Handle multi pattern unbound list rest variables

This commit is contained in:
JRI98 2024-06-21 17:01:49 +01:00
parent 03eadc2e0f
commit f4551978ce
No known key found for this signature in database
GPG key ID: F83B29916FF13F24
4 changed files with 35 additions and 5 deletions

View file

@ -207,7 +207,7 @@ pub struct ListPatterns {
/// [ .., A, B ] -> patterns = [A, B], rest = 0 /// [ .., A, B ] -> patterns = [A, B], rest = 0
/// [ A, .., B ] -> patterns = [A, B], rest = 1 /// [ A, .., B ] -> patterns = [A, B], rest = 1
/// [ A, B, .. ] -> patterns = [A, B], rest = 2 /// [ A, B, .. ] -> patterns = [A, B], rest = 2
pub opt_rest: Option<(usize, Option<Symbol>)>, pub opt_rest: Option<(usize, Option<Loc<Symbol>>)>,
} }
impl ListPatterns { impl ListPatterns {
@ -793,7 +793,8 @@ pub fn canonicalize_pattern<'a>(
pattern_as.identifier.value, pattern_as.identifier.value,
) { ) {
Ok(symbol) => { Ok(symbol) => {
rest_name = Some(symbol); rest_name =
Some(Loc::at(pattern_as.identifier.region, symbol));
} }
Err(pattern) => { Err(pattern) => {
opt_erroneous = Some(pattern); opt_erroneous = Some(pattern);
@ -997,6 +998,10 @@ impl<'a> BindingsFromPattern<'a> {
| OpaqueNotInScope(..) => (), | OpaqueNotInScope(..) => (),
List { patterns, .. } => { List { patterns, .. } => {
stack.extend(patterns.patterns.iter().rev().map(Pattern)); stack.extend(patterns.patterns.iter().rev().map(Pattern));
if let Some((_, Some(rest_sym))) = &patterns.opt_rest {
return Some((rest_sym.value, rest_sym.region));
}
} }
} }
} }

View file

@ -141,7 +141,7 @@ fn headers_from_annotation_help(
constraints.push_type(types, typ) constraints.push_type(types, typ)
}; };
let typ = Loc::at(annotation.region, annotation_index); let typ = Loc::at(annotation.region, annotation_index);
headers.insert(rest, typ); headers.insert(rest.value, typ);
false false
} else { } else {
@ -735,7 +735,7 @@ pub fn constrain_pattern(
if let Some((_, Some(rest))) = opt_rest { if let Some((_, Some(rest))) = opt_rest {
state.headers.insert( state.headers.insert(
*rest, rest.value,
Loc { Loc {
region, region,
value: *constraints[expected].get_type_ref(), value: *constraints[expected].get_type_ref(),

View file

@ -10501,6 +10501,29 @@ In roc, functions are always written as a lambda, like{}
"### "###
); );
test_report!(
issue_6825,
indoc!(
r#"
when [] is
[] | [_, .. as rest] if List.isEmpty rest -> []
_ -> []
"#
),
@r###"
NAME NOT BOUND IN ALL PATTERNS in /code/proj/Main.roc
`rest` is not bound in all patterns of this `when` branch
5 [] | [_, .. as rest] if List.isEmpty rest -> []
^^^^
Identifiers introduced in a `when` branch must be bound in all patterns
of the branch. Otherwise, the program would crash when it tries to use
an identifier that wasn't bound!
"###
);
test_report!( test_report!(
flip_flop_catch_all_branches_not_exhaustive, flip_flop_catch_all_branches_not_exhaustive,
indoc!( indoc!(

View file

@ -1084,7 +1084,9 @@ fn from_can_pattern_help<'a>(
list_layout, list_layout,
element_layout, element_layout,
elements: mono_patterns, elements: mono_patterns,
opt_rest: patterns.opt_rest, opt_rest: patterns
.opt_rest
.map(|(i, name)| (i, name.map(|s| s.value))),
}) })
} }
} }