Merge pull request #3600 from rtfeldman/i3298

Various improvements to is-open constraints/catch-all branches in patterns
This commit is contained in:
Folkert de Vries 2022-07-23 12:49:35 +02:00 committed by GitHub
commit 860df057dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 182 additions and 113 deletions

View file

@ -1653,25 +1653,35 @@ fn open_tag_union(subs: &mut Subs, var: Variable) {
use {Content::*, FlatType::*};
let desc = subs.get(var);
if let Structure(TagUnion(tags, ext)) = desc.content {
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
let new_ext = subs.fresh_unnamed_flex_var();
subs.set_rank(new_ext, desc.rank);
let new_union = Structure(TagUnion(tags, new_ext));
subs.set_content(var, new_union);
match desc.content {
Structure(TagUnion(tags, ext)) => {
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
let new_ext = subs.fresh_unnamed_flex_var();
subs.set_rank(new_ext, desc.rank);
let new_union = Structure(TagUnion(tags, new_ext));
subs.set_content(var, new_union);
}
// Also open up all nested tag unions.
let all_vars = tags.variables().into_iter();
stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var]));
}
// Also open up all nested tag unions.
let all_vars = tags.variables().into_iter();
stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var]));
Structure(Record(fields, _)) => {
// Open up all nested tag unions.
stack.extend(subs.get_subs_slice(fields.variables()));
}
_ => {
// Everything else is not a structural type that can be opened
// (i.e. cannot be matched in a pattern-match)
}
}
// Today, an "open" constraint doesn't affect any types
// other than tag unions. Recursive tag unions are constructed
// at a later time (during occurs checks after tag unions are
// resolved), so that's not handled here either.
// NB: Handle record types here if we add presence constraints
// to their type inference as well.
}
}

View file

@ -245,7 +245,13 @@ mod solve_expr {
assert_eq!(actual, expected.to_string());
}
fn infer_queries_help(src: &str, expected: impl FnOnce(&str), print_only_under_alias: bool) {
#[derive(Default)]
struct InferOptions {
print_only_under_alias: bool,
allow_errors: bool,
}
fn infer_queries_help(src: &str, expected: impl FnOnce(&str), options: InferOptions) {
let (
LoadedModule {
module_id: home,
@ -269,12 +275,14 @@ mod solve_expr {
let (can_problems, type_problems) =
format_problems(&src, home, &interns, can_problems, type_problems);
assert!(
can_problems.is_empty(),
"Canonicalization problems: {}",
can_problems
);
assert!(type_problems.is_empty(), "Type problems: {}", type_problems);
if !options.allow_errors {
assert!(
can_problems.is_empty(),
"Canonicalization problems: {}",
can_problems
);
assert!(type_problems.is_empty(), "Type problems: {}", type_problems);
}
let queries = parse_queries(&src);
assert!(!queries.is_empty(), "No queries provided!");
@ -295,7 +303,7 @@ mod solve_expr {
&interns,
DebugPrint {
print_lambda_sets: true,
print_only_under_alias,
print_only_under_alias: options.print_only_under_alias,
},
);
subs.rollback_to(snapshot);
@ -325,11 +333,10 @@ mod solve_expr {
}
macro_rules! infer_queries {
($program:expr, @$queries:literal $(,)?) => {
infer_queries_help($program, |golden| insta::assert_snapshot!(golden, @$queries), false)
};
($program:expr, @$queries:literal, print_only_under_alias=true $(,)?) => {
infer_queries_help($program, |golden| insta::assert_snapshot!(golden, @$queries), true)
($program:expr, @$queries:literal $($option:ident: $value:expr)*) => {
infer_queries_help($program, |golden| insta::assert_snapshot!(golden, @$queries), InferOptions {
$($option: $value,)* ..InferOptions::default()
})
};
}
@ -6662,7 +6669,7 @@ mod solve_expr {
A#id(4) : A -[[id(4)]]-> A
idChoice : a -[[] + a:id(2):1]-> a | a has Id
idChoice : A -[[id(4)]]-> A
"#,
"#
)
}
@ -6696,8 +6703,8 @@ mod solve_expr {
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
Id#id(3) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
alias : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
"#,
print_only_under_alias = true,
"#
print_only_under_alias: true
)
}
@ -6726,8 +6733,8 @@ mod solve_expr {
@r#"
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
it : {} -[[8(8)]]-> {}
"#,
print_only_under_alias = true,
"#
print_only_under_alias: true
)
}
@ -6757,8 +6764,8 @@ mod solve_expr {
@r#"
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
"#,
print_only_under_alias = true,
"#
print_only_under_alias: true
)
}
@ -6895,8 +6902,8 @@ mod solve_expr {
Named name outerList : [Named Str (List a)] as a
name : Str
outerList : List ([Named Str (List a)] as a)
"#,
print_only_under_alias = true
"#
print_only_under_alias: true
)
}
@ -7012,8 +7019,8 @@ mod solve_expr {
#^^^{-1}
"#
),
@r#"fun : {} -[[thunk(9) (({} -[[15(15)]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13(13) Str]]-> Str)), thunk(9) (({} -[[14(14)]]-> Str)) (Str -[[f(3)]]-> ({} -[[11(11)]]-> Str))]]-> Str"#,
print_only_under_alias = true,
@r#"fun : {} -[[thunk(9) (({} -[[15(15)]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13(13) Str]]-> Str)), thunk(9) (({} -[[14(14)]]-> Str)) (Str -[[f(3)]]-> ({} -[[11(11)]]-> Str))]]-> Str"#
print_only_under_alias: true
);
}
@ -7402,4 +7409,35 @@ mod solve_expr {
"###
);
}
#[test]
fn catchall_branch_for_pattern_not_last() {
infer_queries!(
indoc!(
r#"
\x -> when x is
#^
A B _ -> ""
A _ C -> ""
"#
),
@r#"x : [A [B]* [C]*]"#
allow_errors: true
);
}
#[test]
fn catchall_branch_walk_into_nested_types() {
infer_queries!(
indoc!(
r#"
\x -> when x is
#^
{ a: A { b: B } } -> ""
_ -> ""
"#
),
@r#"x : { a : [A { b : [B]* }*]* }*"#
)
}
}