mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Merge remote-tracking branch 'origin/trunk' into tag-alignment
This commit is contained in:
commit
93c331e1aa
12 changed files with 324 additions and 144 deletions
|
@ -2705,7 +2705,7 @@ pub mod test_constrain {
|
||||||
A _ -> Z
|
A _ -> Z
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[A [M, N]*] -> [X, Y, Z]*",
|
"[A [M, N]] -> [X, Y, Z]*",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1084,7 +1084,8 @@ fn type_to_union_tags<'a>(
|
||||||
|
|
||||||
let ext = {
|
let ext = {
|
||||||
let (it, ext) =
|
let (it, ext) =
|
||||||
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var);
|
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var)
|
||||||
|
.expect("not a tag union");
|
||||||
|
|
||||||
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
||||||
tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
|
@ -735,7 +735,8 @@ pub fn constrain_expr(
|
||||||
let mut pattern_vars = Vec::with_capacity(branches.len());
|
let mut pattern_vars = Vec::with_capacity(branches.len());
|
||||||
let mut pattern_headers = SendMap::default();
|
let mut pattern_headers = SendMap::default();
|
||||||
let mut pattern_cons = Vec::with_capacity(branches.len() + 2);
|
let mut pattern_cons = Vec::with_capacity(branches.len() + 2);
|
||||||
let mut branch_cons = Vec::with_capacity(branches.len());
|
let mut delayed_is_open_constraints = Vec::with_capacity(2);
|
||||||
|
let mut body_cons = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
for (index, when_branch) in branches.iter().enumerate() {
|
for (index, when_branch) in branches.iter().enumerate() {
|
||||||
let expected_pattern = |sub_pattern, sub_region| {
|
let expected_pattern = |sub_pattern, sub_region| {
|
||||||
|
@ -749,8 +750,13 @@ pub fn constrain_expr(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) =
|
let ConstrainedBranch {
|
||||||
constrain_when_branch_help(
|
vars: new_pattern_vars,
|
||||||
|
headers: new_pattern_headers,
|
||||||
|
pattern_constraints,
|
||||||
|
is_open_constrains,
|
||||||
|
body_constraints,
|
||||||
|
} = constrain_when_branch_help(
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
region,
|
region,
|
||||||
|
@ -779,9 +785,10 @@ pub fn constrain_expr(
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern_headers.extend(new_pattern_headers);
|
pattern_headers.extend(new_pattern_headers);
|
||||||
pattern_cons.push(pattern_con);
|
pattern_cons.push(pattern_constraints);
|
||||||
|
delayed_is_open_constraints.extend(is_open_constrains);
|
||||||
|
|
||||||
branch_cons.push(branch_con);
|
body_cons.push(body_constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deviation: elm adds another layer of And nesting
|
// Deviation: elm adds another layer of And nesting
|
||||||
|
@ -793,6 +800,11 @@ pub fn constrain_expr(
|
||||||
// The return type of each branch must equal the return type of
|
// The return type of each branch must equal the return type of
|
||||||
// the entire when-expression.
|
// the entire when-expression.
|
||||||
|
|
||||||
|
// Layer on the "is-open" constraints at the very end, after we know what the branch
|
||||||
|
// types are supposed to look like without open-ness.
|
||||||
|
let is_open_constr = constraints.and_constraint(delayed_is_open_constraints);
|
||||||
|
pattern_cons.push(is_open_constr);
|
||||||
|
|
||||||
// After solving the condition variable with what's expected from the branch patterns,
|
// After solving the condition variable with what's expected from the branch patterns,
|
||||||
// check it against the condition expression.
|
// check it against the condition expression.
|
||||||
//
|
//
|
||||||
|
@ -826,7 +838,7 @@ pub fn constrain_expr(
|
||||||
// Solve all the pattern constraints together, introducing variables in the pattern as
|
// Solve all the pattern constraints together, introducing variables in the pattern as
|
||||||
// need be before solving the bodies.
|
// need be before solving the bodies.
|
||||||
let pattern_constraints = constraints.and_constraint(pattern_cons);
|
let pattern_constraints = constraints.and_constraint(pattern_cons);
|
||||||
let body_constraints = constraints.and_constraint(branch_cons);
|
let body_constraints = constraints.and_constraint(body_cons);
|
||||||
let when_body_con = constraints.let_constraint(
|
let when_body_con = constraints.let_constraint(
|
||||||
[],
|
[],
|
||||||
pattern_vars,
|
pattern_vars,
|
||||||
|
@ -1790,6 +1802,14 @@ fn constrain_value_def(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ConstrainedBranch {
|
||||||
|
vars: Vec<Variable>,
|
||||||
|
headers: VecMap<Symbol, Loc<Type>>,
|
||||||
|
pattern_constraints: Constraint,
|
||||||
|
is_open_constrains: Vec<Constraint>,
|
||||||
|
body_constraints: Constraint,
|
||||||
|
}
|
||||||
|
|
||||||
/// Constrain a when branch, returning (variables in pattern, symbols introduced in pattern, pattern constraint, body constraint).
|
/// Constrain a when branch, returning (variables in pattern, symbols introduced in pattern, pattern constraint, body constraint).
|
||||||
/// We want to constraint all pattern constraints in a "when" before body constraints.
|
/// We want to constraint all pattern constraints in a "when" before body constraints.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -1800,12 +1820,7 @@ fn constrain_when_branch_help(
|
||||||
when_branch: &WhenBranch,
|
when_branch: &WhenBranch,
|
||||||
pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type>,
|
pattern_expected: impl Fn(HumanIndex, Region) -> PExpected<Type>,
|
||||||
expr_expected: Expected<Type>,
|
expr_expected: Expected<Type>,
|
||||||
) -> (
|
) -> ConstrainedBranch {
|
||||||
Vec<Variable>,
|
|
||||||
VecMap<Symbol, Loc<Type>>,
|
|
||||||
Constraint,
|
|
||||||
Constraint,
|
|
||||||
) {
|
|
||||||
let ret_constraint = constrain_expr(
|
let ret_constraint = constrain_expr(
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -1869,7 +1884,8 @@ fn constrain_when_branch_help(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (pattern_constraints, body_constraints) = if let Some(loc_guard) = &when_branch.guard {
|
let (pattern_constraints, delayed_is_open_constraints, body_constraints) =
|
||||||
|
if let Some(loc_guard) = &when_branch.guard {
|
||||||
let guard_constraint = constrain_expr(
|
let guard_constraint = constrain_expr(
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -1883,27 +1899,28 @@ fn constrain_when_branch_help(
|
||||||
);
|
);
|
||||||
|
|
||||||
// must introduce the headers from the pattern before constraining the guard
|
// must introduce the headers from the pattern before constraining the guard
|
||||||
state
|
let delayed_is_open_constraints = state.delayed_is_open_constraints;
|
||||||
.constraints
|
|
||||||
.append(&mut state.delayed_is_open_constraints);
|
|
||||||
let state_constraints = constraints.and_constraint(state.constraints);
|
let state_constraints = constraints.and_constraint(state.constraints);
|
||||||
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
|
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
|
||||||
|
|
||||||
(state_constraints, inner)
|
(state_constraints, delayed_is_open_constraints, inner)
|
||||||
} else {
|
} else {
|
||||||
state
|
let delayed_is_open_constraints = state.delayed_is_open_constraints;
|
||||||
.constraints
|
|
||||||
.append(&mut state.delayed_is_open_constraints);
|
|
||||||
let state_constraints = constraints.and_constraint(state.constraints);
|
let state_constraints = constraints.and_constraint(state.constraints);
|
||||||
(state_constraints, ret_constraint)
|
(
|
||||||
|
state_constraints,
|
||||||
|
delayed_is_open_constraints,
|
||||||
|
ret_constraint,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
ConstrainedBranch {
|
||||||
state.vars,
|
vars: state.vars,
|
||||||
state.headers,
|
headers: state.headers,
|
||||||
pattern_constraints,
|
pattern_constraints,
|
||||||
|
is_open_constrains: delayed_is_open_constraints,
|
||||||
body_constraints,
|
body_constraints,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constrain_field(
|
fn constrain_field(
|
||||||
|
|
|
@ -498,9 +498,6 @@ pub fn constrain_pattern(
|
||||||
state.vars.push(*ext_var);
|
state.vars.push(*ext_var);
|
||||||
state.constraints.push(whole_con);
|
state.constraints.push(whole_con);
|
||||||
state.constraints.push(tag_con);
|
state.constraints.push(tag_con);
|
||||||
state
|
|
||||||
.constraints
|
|
||||||
.append(&mut state.delayed_is_open_constraints);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UnwrappedOpaque {
|
UnwrappedOpaque {
|
||||||
|
|
|
@ -633,6 +633,23 @@ impl<'a> UnionLayout<'a> {
|
||||||
round_up_to_alignment(data_width, data_align)
|
round_up_to_alignment(data_width, data_align)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tag_id_offset(&self, target_info: TargetInfo) -> Option<u32> {
|
||||||
|
use UnionLayout::*;
|
||||||
|
|
||||||
|
if let NonNullableUnwrapped(_) | NullableUnwrapped { .. } = self {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data_width = self.data_size_and_alignment_help_match(None, target_info).0;
|
||||||
|
|
||||||
|
// current, broken logic
|
||||||
|
if data_width > 8 {
|
||||||
|
Some(round_up_to_alignment(data_width, 8))
|
||||||
|
} else {
|
||||||
|
Some(data_width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Very important to use this when doing a memcpy!
|
/// Very important to use this when doing a memcpy!
|
||||||
fn stack_size_without_alignment(&self, target_info: TargetInfo) -> u32 {
|
fn stack_size_without_alignment(&self, target_info: TargetInfo) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
|
@ -3055,7 +3072,9 @@ pub fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
|
||||||
// the ext_var is empty
|
// the ext_var is empty
|
||||||
let mut ext_fields = std::vec::Vec::new();
|
let mut ext_fields = std::vec::Vec::new();
|
||||||
match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) {
|
match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) {
|
||||||
Ok(()) | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) => ext_fields.is_empty(),
|
Ok(()) | Err((_, Content::FlexVar(_) | Content::RigidVar(_) | Content::Error)) => {
|
||||||
|
ext_fields.is_empty()
|
||||||
|
}
|
||||||
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1653,7 +1653,8 @@ fn open_tag_union(subs: &mut Subs, var: Variable) {
|
||||||
use {Content::*, FlatType::*};
|
use {Content::*, FlatType::*};
|
||||||
|
|
||||||
let desc = subs.get(var);
|
let desc = subs.get(var);
|
||||||
if let Structure(TagUnion(tags, ext)) = desc.content {
|
match desc.content {
|
||||||
|
Structure(TagUnion(tags, ext)) => {
|
||||||
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
|
if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext) {
|
||||||
let new_ext = subs.fresh_unnamed_flex_var();
|
let new_ext = subs.fresh_unnamed_flex_var();
|
||||||
subs.set_rank(new_ext, desc.rank);
|
subs.set_rank(new_ext, desc.rank);
|
||||||
|
@ -1666,12 +1667,21 @@ fn open_tag_union(subs: &mut Subs, var: Variable) {
|
||||||
stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var]));
|
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
|
// Today, an "open" constraint doesn't affect any types
|
||||||
// other than tag unions. Recursive tag unions are constructed
|
// other than tag unions. Recursive tag unions are constructed
|
||||||
// at a later time (during occurs checks after tag unions are
|
// at a later time (during occurs checks after tag unions are
|
||||||
// resolved), so that's not handled here either.
|
// resolved), so that's not handled here either.
|
||||||
// NB: Handle record types here if we add presence constraints
|
|
||||||
// to their type inference as well.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2807,7 +2817,8 @@ fn type_to_variable<'a>(
|
||||||
subs,
|
subs,
|
||||||
UnionTags::default(),
|
UnionTags::default(),
|
||||||
temp_ext_var,
|
temp_ext_var,
|
||||||
);
|
)
|
||||||
|
.expect("extension var could not be seen as a tag union");
|
||||||
|
|
||||||
for _ in it {
|
for _ in it {
|
||||||
unreachable!("we assert that the ext var is empty; otherwise we'd already know it was a tag union!");
|
unreachable!("we assert that the ext var is empty; otherwise we'd already know it was a tag union!");
|
||||||
|
@ -3351,7 +3362,8 @@ fn type_to_union_tags<'a>(
|
||||||
subs,
|
subs,
|
||||||
UnionTags::default(),
|
UnionTags::default(),
|
||||||
temp_ext_var,
|
temp_ext_var,
|
||||||
);
|
)
|
||||||
|
.expect("extension var could not be seen as tag union");
|
||||||
|
|
||||||
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
||||||
|
|
||||||
|
|
|
@ -245,7 +245,13 @@ mod solve_expr {
|
||||||
assert_eq!(actual, expected.to_string());
|
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 (
|
let (
|
||||||
LoadedModule {
|
LoadedModule {
|
||||||
module_id: home,
|
module_id: home,
|
||||||
|
@ -269,12 +275,14 @@ mod solve_expr {
|
||||||
let (can_problems, type_problems) =
|
let (can_problems, type_problems) =
|
||||||
format_problems(&src, home, &interns, can_problems, type_problems);
|
format_problems(&src, home, &interns, can_problems, type_problems);
|
||||||
|
|
||||||
|
if !options.allow_errors {
|
||||||
assert!(
|
assert!(
|
||||||
can_problems.is_empty(),
|
can_problems.is_empty(),
|
||||||
"Canonicalization problems: {}",
|
"Canonicalization problems: {}",
|
||||||
can_problems
|
can_problems
|
||||||
);
|
);
|
||||||
assert!(type_problems.is_empty(), "Type problems: {}", type_problems);
|
assert!(type_problems.is_empty(), "Type problems: {}", type_problems);
|
||||||
|
}
|
||||||
|
|
||||||
let queries = parse_queries(&src);
|
let queries = parse_queries(&src);
|
||||||
assert!(!queries.is_empty(), "No queries provided!");
|
assert!(!queries.is_empty(), "No queries provided!");
|
||||||
|
@ -295,7 +303,7 @@ mod solve_expr {
|
||||||
&interns,
|
&interns,
|
||||||
DebugPrint {
|
DebugPrint {
|
||||||
print_lambda_sets: true,
|
print_lambda_sets: true,
|
||||||
print_only_under_alias,
|
print_only_under_alias: options.print_only_under_alias,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
subs.rollback_to(snapshot);
|
subs.rollback_to(snapshot);
|
||||||
|
@ -325,11 +333,10 @@ mod solve_expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! infer_queries {
|
macro_rules! infer_queries {
|
||||||
($program:expr, @$queries:literal $(,)?) => {
|
($program:expr, @$queries:literal $($option:ident: $value:expr)*) => {
|
||||||
infer_queries_help($program, |golden| insta::assert_snapshot!(golden, @$queries), false)
|
infer_queries_help($program, |golden| insta::assert_snapshot!(golden, @$queries), InferOptions {
|
||||||
};
|
$($option: $value,)* ..InferOptions::default()
|
||||||
($program:expr, @$queries:literal, print_only_under_alias=true $(,)?) => {
|
})
|
||||||
infer_queries_help($program, |golden| insta::assert_snapshot!(golden, @$queries), true)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6662,7 +6669,7 @@ mod solve_expr {
|
||||||
A#id(4) : A -[[id(4)]]-> A
|
A#id(4) : A -[[id(4)]]-> A
|
||||||
idChoice : a -[[] + a:id(2):1]-> a | a has Id
|
idChoice : a -[[] + a:id(2):1]-> a | a has Id
|
||||||
idChoice : A -[[id(4)]]-> A
|
idChoice : A -[[id(4)]]-> A
|
||||||
"#,
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6696,8 +6703,8 @@ mod solve_expr {
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||||
Id#id(3) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
Id#id(3) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||||
alias : {} -[[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#"
|
@r#"
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||||
it : {} -[[8(8)]]-> {}
|
it : {} -[[8(8)]]-> {}
|
||||||
"#,
|
"#
|
||||||
print_only_under_alias = true,
|
print_only_under_alias: true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6757,8 +6764,8 @@ mod solve_expr {
|
||||||
@r#"
|
@r#"
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||||
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
|
Named name outerList : [Named Str (List a)] as a
|
||||||
name : Str
|
name : Str
|
||||||
outerList : List ([Named Str (List a)] as a)
|
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}
|
#^^^{-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"#,
|
@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,
|
print_only_under_alias: true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7357,4 +7364,80 @@ mod solve_expr {
|
||||||
"List (A U8)",
|
"List (A U8)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shared_pattern_variable_in_when_patterns() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when A "" is
|
||||||
|
# ^^^^
|
||||||
|
A x | B x -> x
|
||||||
|
# ^ ^ ^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
A "" : [A Str, B Str]
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shared_pattern_variable_in_multiple_branch_when_patterns() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when A "" is
|
||||||
|
# ^^^^
|
||||||
|
A x | B x -> x
|
||||||
|
# ^ ^ ^
|
||||||
|
C x | D x -> x
|
||||||
|
# ^ ^ ^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
A "" : [A Str, B Str, C Str, D Str]
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
x : Str
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]* }*]* }*"#
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1879,3 +1879,27 @@ fn alignment_i128() {
|
||||||
((i128, bool), [u8; 8], u8, [u8; 15])
|
((i128, bool), [u8; 8], u8, [u8; 15])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "Erroneous: Expr::Closure""#)]
|
||||||
|
fn error_type_in_tag_union_payload() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : ([] -> Bool) -> Bool
|
||||||
|
f = \fun ->
|
||||||
|
if True then
|
||||||
|
fun 42
|
||||||
|
else
|
||||||
|
False
|
||||||
|
|
||||||
|
f (\x -> x)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
u8,
|
||||||
|
|x| x,
|
||||||
|
true // ignore type errors
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -2702,7 +2702,8 @@ impl UnionTags {
|
||||||
subs: &'a Subs,
|
subs: &'a Subs,
|
||||||
ext: Variable,
|
ext: Variable,
|
||||||
) -> impl Iterator<Item = (&TagName, &[Variable])> + 'a {
|
) -> impl Iterator<Item = (&TagName, &[Variable])> + 'a {
|
||||||
let (it, _) = crate::types::gather_tags_unsorted_iter(subs, *self, ext);
|
let (it, _) =
|
||||||
|
crate::types::gather_tags_unsorted_iter(subs, *self, ext).expect("not a tag union");
|
||||||
|
|
||||||
let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice));
|
let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice));
|
||||||
|
|
||||||
|
@ -2715,7 +2716,8 @@ impl UnionTags {
|
||||||
subs: &'a Subs,
|
subs: &'a Subs,
|
||||||
ext: Variable,
|
ext: Variable,
|
||||||
) -> (UnsortedUnionLabels<'a, TagName>, Variable) {
|
) -> (UnsortedUnionLabels<'a, TagName>, Variable) {
|
||||||
let (it, ext) = crate::types::gather_tags_unsorted_iter(subs, *self, ext);
|
let (it, ext) =
|
||||||
|
crate::types::gather_tags_unsorted_iter(subs, *self, ext).expect("not a tag union");
|
||||||
let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice));
|
let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice));
|
||||||
let it = it.map(f);
|
let it = it.map(f);
|
||||||
|
|
||||||
|
@ -2741,7 +2743,8 @@ impl UnionTags {
|
||||||
ext,
|
ext,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let union_structure = crate::types::gather_tags(subs, *self, ext);
|
let union_structure =
|
||||||
|
crate::types::gather_tags(subs, *self, ext).expect("not a tag union");
|
||||||
|
|
||||||
(
|
(
|
||||||
Box::new(union_structure.fields.into_iter()),
|
Box::new(union_structure.fields.into_iter()),
|
||||||
|
@ -2767,7 +2770,8 @@ impl UnionTags {
|
||||||
ext,
|
ext,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let (fields, ext) = crate::types::gather_tags_slices(subs, *self, ext);
|
let (fields, ext) =
|
||||||
|
crate::types::gather_tags_slices(subs, *self, ext).expect("not a tag union");
|
||||||
|
|
||||||
(Box::new(fields.into_iter()), ext)
|
(Box::new(fields.into_iter()), ext)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2674,14 +2674,28 @@ pub fn gather_fields(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GatherTagsError {
|
||||||
|
NotATagUnion(Variable),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gathers tag payloads of a type, assuming it is a tag.
|
||||||
|
///
|
||||||
|
/// If the given type is unbound or an error, no payloads are returned.
|
||||||
|
///
|
||||||
|
/// If the given type cannot be seen as a tag, unbound type, or error, this
|
||||||
|
/// function returns an error.
|
||||||
pub fn gather_tags_unsorted_iter(
|
pub fn gather_tags_unsorted_iter(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
other_fields: UnionTags,
|
other_fields: UnionTags,
|
||||||
mut var: Variable,
|
mut var: Variable,
|
||||||
) -> (
|
) -> Result<
|
||||||
|
(
|
||||||
impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_,
|
impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_,
|
||||||
Variable,
|
Variable,
|
||||||
) {
|
),
|
||||||
|
GatherTagsError,
|
||||||
|
> {
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
@ -2711,20 +2725,18 @@ pub fn gather_tags_unsorted_iter(
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(_, _, actual_var, _) => {
|
Alias(_, _, actual_var, _) => {
|
||||||
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
|
|
||||||
var = *actual_var;
|
var = *actual_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
Structure(EmptyTagUnion) => break,
|
Structure(EmptyTagUnion) => break,
|
||||||
FlexVar(_) => break,
|
FlexVar(_) => break,
|
||||||
|
|
||||||
// TODO investigate this likely can happen when there is a type error
|
// TODO investigate, this likely can happen when there is a type error
|
||||||
RigidVar(_) => break,
|
RigidVar(_) => break,
|
||||||
|
|
||||||
other => unreachable!(
|
Error => break,
|
||||||
"something weird ended up in a tag union type: {:?} at {:?}",
|
|
||||||
other, var
|
_ => return Err(GatherTagsError::NotATagUnion(var)),
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2738,15 +2750,15 @@ pub fn gather_tags_unsorted_iter(
|
||||||
(tag_name, subs_slice)
|
(tag_name, subs_slice)
|
||||||
});
|
});
|
||||||
|
|
||||||
(it, var)
|
Ok((it, var))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_tags_slices(
|
pub fn gather_tags_slices(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
other_fields: UnionTags,
|
other_fields: UnionTags,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
) -> (Vec<(TagName, VariableSubsSlice)>, Variable) {
|
) -> Result<(Vec<(TagName, VariableSubsSlice)>, Variable), GatherTagsError> {
|
||||||
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var);
|
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var)?;
|
||||||
|
|
||||||
let mut result: Vec<_> = it
|
let mut result: Vec<_> = it
|
||||||
.map(|(ref_label, field): (_, VariableSubsSlice)| (ref_label.clone(), field))
|
.map(|(ref_label, field): (_, VariableSubsSlice)| (ref_label.clone(), field))
|
||||||
|
@ -2754,11 +2766,15 @@ pub fn gather_tags_slices(
|
||||||
|
|
||||||
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
(result, ext)
|
Ok((result, ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUnionStructure {
|
pub fn gather_tags(
|
||||||
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var);
|
subs: &Subs,
|
||||||
|
other_fields: UnionTags,
|
||||||
|
var: Variable,
|
||||||
|
) -> Result<TagUnionStructure, GatherTagsError> {
|
||||||
|
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var)?;
|
||||||
|
|
||||||
let mut result: Vec<_> = it
|
let mut result: Vec<_> = it
|
||||||
.map(|(ref_label, field): (_, VariableSubsSlice)| {
|
.map(|(ref_label, field): (_, VariableSubsSlice)| {
|
||||||
|
@ -2768,10 +2784,10 @@ pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUn
|
||||||
|
|
||||||
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
TagUnionStructure {
|
Ok(TagUnionStructure {
|
||||||
fields: result,
|
fields: result,
|
||||||
ext,
|
ext,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate_lambda_sets_as_unspecialized(
|
fn instantiate_lambda_sets_as_unspecialized(
|
||||||
|
|
|
@ -838,26 +838,7 @@ fn unify_structure<M: MetaCollector>(
|
||||||
match other {
|
match other {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// If the other is flex, Structure wins!
|
// If the other is flex, Structure wins!
|
||||||
let outcome = merge(subs, ctx, Structure(*flat_type));
|
merge(subs, ctx, Structure(*flat_type))
|
||||||
|
|
||||||
// And if we see a flex variable on the right hand side of a presence
|
|
||||||
// constraint, we know we need to open up the structure we're trying to unify with.
|
|
||||||
match (ctx.mode.is_present(), flat_type) {
|
|
||||||
(true, FlatType::TagUnion(tags, _ext)) => {
|
|
||||||
let new_ext = subs.fresh_unnamed_flex_var();
|
|
||||||
let mut new_desc = ctx.first_desc;
|
|
||||||
new_desc.content = Structure(FlatType::TagUnion(*tags, new_ext));
|
|
||||||
subs.set(ctx.first, new_desc);
|
|
||||||
}
|
|
||||||
(true, FlatType::FunctionOrTagUnion(tn, sym, _ext)) => {
|
|
||||||
let new_ext = subs.fresh_unnamed_flex_var();
|
|
||||||
let mut new_desc = ctx.first_desc;
|
|
||||||
new_desc.content = Structure(FlatType::FunctionOrTagUnion(*tn, *sym, new_ext));
|
|
||||||
subs.set(ctx.first, new_desc);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
outcome
|
|
||||||
}
|
}
|
||||||
FlexAbleVar(_, ability) => {
|
FlexAbleVar(_, ability) => {
|
||||||
// Structure wins
|
// Structure wins
|
||||||
|
|
|
@ -7727,7 +7727,7 @@ All branches in an `if` must have the same type!
|
||||||
|
|
||||||
But all the previous branches match:
|
But all the previous branches match:
|
||||||
|
|
||||||
F [A]a
|
F [A]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9875,4 +9875,30 @@ All branches in an `if` must have the same type!
|
||||||
your code don't wonder why it is there.
|
your code don't wonder why it is there.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
flip_flop_catch_all_branches_not_exhaustive,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
\x -> when x is
|
||||||
|
A B _ -> ""
|
||||||
|
A _ C -> ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This `when` does not cover all the possibilities:
|
||||||
|
|
||||||
|
4│> \x -> when x is
|
||||||
|
5│> A B _ -> ""
|
||||||
|
6│> A _ C -> ""
|
||||||
|
|
||||||
|
Other possibilities include:
|
||||||
|
|
||||||
|
A _ _
|
||||||
|
|
||||||
|
I would have to crash if I saw one of those! Add branches for them!
|
||||||
|
"###
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue