fix and test as pattern type inference

This commit is contained in:
Folkert 2023-01-08 16:40:03 +01:00
parent 57f2233278
commit c2ddeb0de0
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
3 changed files with 150 additions and 41 deletions

View file

@ -781,8 +781,29 @@ pub fn canonicalize_pattern<'a>(
malformed_pattern(env, problem, region)
}
As(_pattern, _pattern_as) => {
todo!();
As(loc_pattern, pattern_as) => {
let can_subpattern = canonicalize_pattern(
env,
var_store,
scope,
output,
pattern_type,
&loc_pattern.value,
loc_pattern.region,
permit_shadows,
);
match canonicalize_pattern_symbol(
env,
scope,
output,
region,
permit_shadows,
pattern_as.identifier.value,
) {
Ok(symbol) => Pattern::As(Box::new(can_subpattern), symbol),
Err(pattern) => pattern,
}
}
Malformed(_str) => {

View file

@ -125,12 +125,23 @@ fn headers_from_annotation_help(
_ => false,
},
List { .. } => {
// There are no interesting headers to introduce for list patterns, since the only
// exhaustive list pattern is
// \[..] -> <body>
// which does not introduce any symbols.
false
List { patterns, .. } => {
if let Some((_, Some(rest))) = patterns.opt_rest {
let annotation_index = {
let typ = types.from_old_type(annotation.value);
constraints.push_type(types, typ)
};
let typ = Loc::at(annotation.region, annotation_index);
headers.insert(rest, typ);
false
} else {
// There are no interesting headers to introduce for list patterns, since the only
// exhaustive list pattern is
// \[..] -> <body>
// which does not introduce any symbols.
false
}
}
AppliedTag {
@ -203,32 +214,6 @@ fn headers_from_annotation_help(
}
}
fn constrain_pattern_symbol(
types: &mut Types,
constraints: &mut Constraints,
region: Region,
expected: PExpectedTypeIndex,
state: &mut PatternState,
symbol: Symbol,
) {
let expected = &constraints[expected];
let type_index = *expected.get_type_ref();
if could_be_a_tag_union(types, type_index) {
state
.delayed_is_open_constraints
.push(constraints.is_open_type(type_index));
}
state.headers.insert(
symbol,
Loc {
region,
value: type_index,
},
);
}
/// This accepts PatternState (rather than returning it) so that the caller can
/// initialize the Vecs in PatternState using with_capacity
/// based on its knowledge of their lengths.
@ -261,11 +246,34 @@ pub fn constrain_pattern(
}
Identifier(symbol) | Shadowed(_, _, symbol) => {
constrain_pattern_symbol(types, constraints, region, expected, state, *symbol);
let type_index = *constraints[expected].get_type_ref();
if could_be_a_tag_union(types, type_index) {
state
.delayed_is_open_constraints
.push(constraints.is_open_type(type_index));
}
state.headers.insert(
*symbol,
Loc {
region,
value: type_index,
},
);
}
As(subpattern, symbol) => {
constrain_pattern_symbol(types, constraints, region, expected, state, *symbol);
// NOTE: we don't use `could_be_a_tag_union` here. The `PATTERN as name` should
// just use the type of the PATTERN, and not influence what type is inferred for PATTERN
state.headers.insert(
*symbol,
Loc {
region,
value: *constraints[expected].get_type_ref(),
},
);
constrain_pattern(
types,
@ -599,14 +607,20 @@ pub fn constrain_pattern(
List {
list_var,
elem_var,
patterns:
ListPatterns {
patterns,
opt_rest: _,
},
patterns: ListPatterns { patterns, opt_rest },
} => {
let elem_var_index = constraints.push_variable(*elem_var);
if let Some((_, Some(rest))) = opt_rest {
state.headers.insert(
*rest,
Loc {
region,
value: *constraints[expected].get_type_ref(),
},
);
}
for loc_pat in patterns.iter() {
let expected = constraints.push_pat_expected_type(PExpected::ForReason(
PReason::ListElem,

View file

@ -8581,4 +8581,78 @@ mod solve_expr {
@"polyDbg : a -[[polyDbg(1)]]-> a"
);
}
#[test]
fn pattern_as_uses_inferred_type() {
infer_queries!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main = when A "foo" is
A _ as a -> a
# ^
b -> b
# ^
"#
),
@r###"
a : [A Str]*
b : [A Str]*
"###
);
}
#[test]
fn pattern_as_does_not_narrow() {
infer_queries!(
indoc!(
r#"
app "test" provides [main] to "./platform"
input : [A Str, B Str]
input = A "foo"
drop : a -> {}
drop = \_ -> {}
main = when input is
# ^^^^^
A _ as a -> drop a
# ^
B _ as b -> drop b
# ^
"#
),
@r###"
input : [A Str, B Str]
a : [A Str, B Str]
b : [A Str, B Str]
"###
);
}
#[test]
fn pattern_as_list() {
infer_queries!(
indoc!(
r#"
app "test" provides [main] to "./platform"
input : List Str
input = [ "foo", "bar" ]
main = when input is
# ^^^^^
[ _first, .. as rest ] -> 1 + List.len rest
# ^^^^
[] -> 0
"#
),
@r###"
input : List Str
rest : List Str
"###
);
}
}