From c01baa9168cd890ec0d127eb988dc5aa3d527ced Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 21 Apr 2022 09:07:43 -0400 Subject: [PATCH 01/44] Fix failing reporting tests --- compiler/can/src/expr.rs | 11 +++++ compiler/constrain/src/expr.rs | 20 +++++++++ reporting/tests/test_reporting.rs | 68 +++++++++++++++++++++++-------- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 132def6f42..6088dd3be8 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -390,6 +390,17 @@ impl WhenBranch { } } +impl WhenBranch { + pub fn region(&self) -> Region { + Region::across_all( + self.patterns + .iter() + .map(|p| &p.region) + .chain([self.value.region].iter()), + ) + } +} + pub fn canonicalize_expr<'a>( env: &mut Env<'a>, var_store: &mut VarStore, diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index a212adb654..f69c2d3b12 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -606,6 +606,15 @@ pub fn constrain_expr( ) }; + let branches_region = { + debug_assert!(!branches.is_empty()); + Region::span_across( + &loc_cond.region, + // &branches.first().unwrap().region(), + &branches.last().unwrap().region(), + ) + }; + let branch_expr_reason = |expected: &Expected, index, branch_region| match expected { FromAnnotation(name, arity, ann_source, _typ) => { @@ -669,6 +678,17 @@ pub fn constrain_expr( ) }; + let expected_pattern = |sub_pattern| { + PExpected::ForReason( + PReason::WhenMatch { + index: HumanIndex::zero_based(index), + sub_pattern, + }, + cond_type.clone(), + pattern_region, + ) + }; + let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) = constrain_when_branch_help( constraints, diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index fa950fdaac..9ed6f0cdee 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -2658,20 +2658,31 @@ mod test_reporting { Red -> 3 "# ), + // TODO(2903): improve tag typo quality indoc!( r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - This `when` does not cover all the possibilities: + The branches of this `when` expression don't match the condition: 4│> when x is - 5│> Red -> 3 + 5│ Red -> 3 - Other possibilities include: + This `x` value is a: - Green + [ Green, Red ] - I would have to crash if I saw one of those! Add branches for them! + But the branch patterns have type: + + [ Red ] + + The branches must be cases of the `when` condition's type! + + Tip: Seems like a tag typo. Maybe `Green` should be `Red`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case "# ), ) @@ -2690,21 +2701,32 @@ mod test_reporting { Green -> 1 "# ), + // TODO(2903): improve tag typo quality indoc!( r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - This `when` does not cover all the possibilities: + The branches of this `when` expression don't match the condition: 4│> when x is - 5│> Red -> 0 - 6│> Green -> 1 + 5│ Red -> 0 + 6│ Green -> 1 - Other possibilities include: + This `x` value is a: - Blue + [ Blue, Green, Red ] - I would have to crash if I saw one of those! Add branches for them! + But the branch patterns have type: + + [ Green, Red ] + + The branches must be cases of the `when` condition's type! + + Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case "# ), ) @@ -2723,22 +2745,31 @@ mod test_reporting { NotAsked -> 3 "# ), + // TODO(2903): improve tag typo quality indoc!( r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - This `when` does not cover all the possibilities: + The branches of this `when` expression don't match the condition: 5│> when x is - 6│> NotAsked -> 3 + 6│ NotAsked -> 3 - Other possibilities include: + This `x` value is a: - Failure _ - Loading - Success _ + [ Failure I64, Loading, NotAsked, Success Str ] - I would have to crash if I saw one of those! Add branches for them! + But the branch patterns have type: + + [ NotAsked ] + + The branches must be cases of the `when` condition's type! + + Tip: Seems like a tag typo. Maybe `Success` should be `NotAsked`? + + Tip: Can more type annotations be added? Type annotations always help + me give more specific messages, and I think they could help a lot in + this case "# ), ) @@ -8446,6 +8477,7 @@ I need all branches in an `if` to have the same type! @F B -> "" "# ), + // TODO(2903): improve tag typo quality indoc!( r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ From 5fe902b8d39edccd35498f1a2b015f8e76ce4f5f Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 21 Apr 2022 10:10:50 -0400 Subject: [PATCH 02/44] Improve non-exhaustive typechecking errors We will replace this with proper exhaustiveness checking when we have that in the typechecking phase, but for now this should do. --- reporting/tests/test_reporting.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 9ed6f0cdee..ebb9a50e85 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -2658,7 +2658,6 @@ mod test_reporting { Red -> 3 "# ), - // TODO(2903): improve tag typo quality indoc!( r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ @@ -2678,11 +2677,9 @@ mod test_reporting { The branches must be cases of the `when` condition's type! - Tip: Seems like a tag typo. Maybe `Green` should be `Red`? + Tip: Looks like the branches are missing coverage of the `Green` tag. - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case + Tip: Maybe you need to add a catch-all branch, like `_`? "# ), ) @@ -2701,7 +2698,6 @@ mod test_reporting { Green -> 1 "# ), - // TODO(2903): improve tag typo quality indoc!( r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ @@ -2722,11 +2718,9 @@ mod test_reporting { The branches must be cases of the `when` condition's type! - Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? + Tip: Looks like the branches are missing coverage of the `Blue` tag. - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case + Tip: Maybe you need to add a catch-all branch, like `_`? "# ), ) @@ -2745,7 +2739,6 @@ mod test_reporting { NotAsked -> 3 "# ), - // TODO(2903): improve tag typo quality indoc!( r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ @@ -2765,11 +2758,10 @@ mod test_reporting { The branches must be cases of the `when` condition's type! - Tip: Seems like a tag typo. Maybe `Success` should be `NotAsked`? + Tip: Looks like the branches are missing coverage of the + `Success`, `Failure` and `Loading` tags. - Tip: Can more type annotations be added? Type annotations always help - me give more specific messages, and I think they could help a lot in - this case + Tip: Maybe you need to add a catch-all branch, like `_`? "# ), ) @@ -8477,7 +8469,6 @@ I need all branches in an `if` to have the same type! @F B -> "" "# ), - // TODO(2903): improve tag typo quality indoc!( r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ From b5efd830e54ed75080c7f28fdd03836d0732f990 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 21 Apr 2022 14:11:32 -0400 Subject: [PATCH 03/44] Solve and gen ability members that bind other able variables --- compiler/can/src/def.rs | 26 +++++---------- compiler/can/src/traverse.rs | 26 ++++++++++++--- compiler/mono/src/ir.rs | 8 +++++ compiler/solve/src/ability.rs | 7 ++-- compiler/solve/tests/solve_expr.rs | 45 ++++++++++++++++++++++++-- compiler/test_gen/src/gen_abilities.rs | 26 +++++++++++++++ 6 files changed, 110 insertions(+), 28 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 0dd250fef9..3fd2438f9f 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -578,12 +578,14 @@ fn resolve_abilities<'a>( // What variables in the annotation are bound to the parent ability, and what variables // are bound to some other ability? - let (variables_bound_to_ability, variables_bound_to_other_abilities): (Vec<_>, Vec<_>) = - member_annot - .introduced_variables - .able - .iter() - .partition(|av| av.ability == loc_ability_name.value); + let (variables_bound_to_ability, _variables_bound_to_other_abilities): ( + Vec<_>, + Vec<_>, + ) = member_annot + .introduced_variables + .able + .iter() + .partition(|av| av.ability == loc_ability_name.value); let mut bad_has_clauses = false; @@ -618,18 +620,6 @@ fn resolve_abilities<'a>( bad_has_clauses = true; } - if !variables_bound_to_other_abilities.is_empty() { - // Disallow variables bound to other abilities, for now. - for bad_variable in variables_bound_to_other_abilities.iter() { - env.problem(Problem::AbilityMemberBindsExternalAbility { - member: member_sym, - ability: loc_ability_name.value, - region: bad_variable.first_seen, - }); - } - bad_has_clauses = true; - } - if bad_has_clauses { // Pretend the member isn't a part of the ability continue; diff --git a/compiler/can/src/traverse.rs b/compiler/can/src/traverse.rs index 55ecdf14b9..8f579d39a7 100644 --- a/compiler/can/src/traverse.rs +++ b/compiler/can/src/traverse.rs @@ -45,11 +45,12 @@ fn walk_def(visitor: &mut V, def: &Def) { .. } = def; - visitor.visit_pattern( - &loc_pattern.value, - loc_pattern.region, - loc_pattern.value.opt_var(), - ); + let opt_var = match loc_pattern.value { + Pattern::Identifier(..) | Pattern::AbilityMemberSpecialization { .. } => Some(*expr_var), + _ => loc_pattern.value.opt_var(), + }; + + visitor.visit_pattern(&loc_pattern.value, loc_pattern.region, opt_var); visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var); if let Some(annot) = &annotation { visitor.visit_annotation(annot); @@ -70,6 +71,9 @@ fn walk_expr(visitor: &mut V, expr: &Expr) { } => { walk_when(visitor, *cond_var, *expr_var, loc_cond, branches); } + Expr::Call(f, loc_args, _) => { + walk_call(visitor, f, loc_args); + } e => todo!("{:?}", e), } } @@ -120,6 +124,18 @@ fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: } } +fn walk_call( + visitor: &mut V, + f: &(Variable, Loc, Variable, Variable), + loc_args: &[(Variable, Loc)], +) { + let (fn_var, loc_fn_expr, _lambda_set, _ret) = f; + visitor.visit_expr(&loc_fn_expr.value, loc_fn_expr.region, *fn_var); + loc_args + .iter() + .for_each(|(v, e)| visitor.visit_expr(&e.value, e.region, *v)); +} + fn walk_pattern(_visitor: &mut V, _pat: &Pattern) { todo!() } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 75e3cf04de..8abbaf9b14 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -4922,6 +4922,14 @@ fn get_specialization<'a>( Some(member) => { let snapshot = env.subs.snapshot(); instantiate_rigids(env.subs, member.signature_var); + let this_f = env.subs.get_content_without_compacting(symbol_var); + let member_f = env + .subs + .get_content_without_compacting(member.signature_var); + use roc_types::subs::SubsFmtContent; + let this_f = SubsFmtContent(&this_f, env.subs); + let member_f = SubsFmtContent(&member_f, env.subs); + dbg!(symbol, this_f, member_f); let (_, must_implement_ability) = unify( env.subs, symbol_var, diff --git a/compiler/solve/src/ability.rs b/compiler/solve/src/ability.rs index a9796590cf..d7479c0780 100644 --- a/compiler/solve/src/ability.rs +++ b/compiler/solve/src/ability.rs @@ -173,9 +173,12 @@ pub fn type_implementing_member( let ability_implementations_for_specialization = specialization_must_implement_constraints .clone() - .get_unique(); + .get_unique() + .into_iter() + .filter(|mia| mia.ability == ability) + .count(); - ability_implementations_for_specialization.len() + ability_implementations_for_specialization }, 1, "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}", diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index a161271d74..c5a5d96821 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -25,7 +25,8 @@ mod solve_expr { // HELPERS lazy_static! { - static ref RE_TYPE_QUERY: Regex = Regex::new(r#"^\s*#\s*(?P\^+)\s*$"#).unwrap(); + static ref RE_TYPE_QUERY: Regex = + Regex::new(r#"(?P\^+)(?:\{-(?P\d+)\})?"#).unwrap(); } #[derive(Debug, Clone, Copy)] @@ -35,9 +36,14 @@ mod solve_expr { let line_info = LineInfo::new(src); let mut queries = vec![]; for (i, line) in src.lines().enumerate() { - if let Some(capture) = RE_TYPE_QUERY.captures(line) { + for capture in RE_TYPE_QUERY.captures_iter(line) { let wher = capture.name("where").unwrap(); + let subtract_col = capture + .name("sub") + .and_then(|m| str::parse(m.as_str()).ok()) + .unwrap_or(0); let (start, end) = (wher.start() as u32, wher.end() as u32); + let (start, end) = (start - subtract_col, end - subtract_col); let last_line = i as u32 - 1; let start_lc = LineColumn { line: last_line, @@ -273,7 +279,8 @@ mod solve_expr { let start = region.start().offset; let end = region.end().offset; let text = &src[start as usize..end as usize]; - let var = find_type_at(region, &decls).expect(&format!("No type for {}!", &text)); + let var = find_type_at(region, &decls) + .expect(&format!("No type for {} ({:?})!", &text, region)); name_all_type_vars(var, subs); let content = subs.get_content_without_compacting(var); @@ -6232,6 +6239,38 @@ mod solve_expr { "# ), "F b -> b", + ); + } + + #[test] + fn ability_member_takes_different_able_variable() { + infer_queries( + indoc!( + r#" + app "test" provides [ result ] to "./platform" + + Hash has hash : a -> U64 | a has Hash + + IntoHash has intoHash : a, b -> b | a has IntoHash, b has Hash + + Id := U64 + hash = \$Id n -> n + #^^^^{-1} + + User := Id + intoHash = \$User id, _ -> id + #^^^^^^^^{-1} + + result = hash (intoHash ($User ($Id 123)) ($Id 1)) + # ^^^^ ^^^^^^^^ + "# + ), + &[ + "hash : Id -> U64", + "intoHash : User, Id -> Id", + "hash : Id -> U64", + "intoHash : User, Id -> Id", + ], ) } diff --git a/compiler/test_gen/src/gen_abilities.rs b/compiler/test_gen/src/gen_abilities.rs index 4da1464de1..66dd302d1b 100644 --- a/compiler/test_gen/src/gen_abilities.rs +++ b/compiler/test_gen/src/gen_abilities.rs @@ -216,3 +216,29 @@ fn ability_used_as_type_still_compiles() { u64 ) } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn ability_member_takes_different_able_variable() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [ result ] to "./platform" + + Hash has hash : a -> U64 | a has Hash + + IntoHash has intoHash : a, b -> b | a has IntoHash, b has Hash + + Id := U64 + hash = \$Id n -> n + + User := Id + intoHash = \$User id, _ -> id + + result = hash (intoHash ($User ($Id 123)) ($Id 1)) + "# + ), + 123, + u64 + ) +} From c67bc5a8d633c4233cca9e0b09014b5d45e5a213 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 09:39:50 -0400 Subject: [PATCH 04/44] Remove when-related changes --- compiler/constrain/src/expr.rs | 20 ---------- compiler/solve/tests/solve_expr.rs | 4 +- reporting/tests/test_reporting.rs | 59 +++++++++--------------------- 3 files changed, 20 insertions(+), 63 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index f69c2d3b12..a212adb654 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -606,15 +606,6 @@ pub fn constrain_expr( ) }; - let branches_region = { - debug_assert!(!branches.is_empty()); - Region::span_across( - &loc_cond.region, - // &branches.first().unwrap().region(), - &branches.last().unwrap().region(), - ) - }; - let branch_expr_reason = |expected: &Expected, index, branch_region| match expected { FromAnnotation(name, arity, ann_source, _typ) => { @@ -678,17 +669,6 @@ pub fn constrain_expr( ) }; - let expected_pattern = |sub_pattern| { - PExpected::ForReason( - PReason::WhenMatch { - index: HumanIndex::zero_based(index), - sub_pattern, - }, - cond_type.clone(), - pattern_region, - ) - }; - let (new_pattern_vars, new_pattern_headers, pattern_con, branch_con) = constrain_when_branch_help( constraints, diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index c5a5d96821..ef59ea0a54 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6251,7 +6251,7 @@ mod solve_expr { Hash has hash : a -> U64 | a has Hash - IntoHash has intoHash : a, b -> b | a has IntoHash, b has Hash + IntoHash has intoHash : a -> b | a has IntoHash, b has Hash Id := U64 hash = \$Id n -> n @@ -6261,7 +6261,7 @@ mod solve_expr { intoHash = \$User id, _ -> id #^^^^^^^^{-1} - result = hash (intoHash ($User ($Id 123)) ($Id 1)) + result = hash (intoHash ($User ($Id 123))) # ^^^^ ^^^^^^^^ "# ), diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index ebb9a50e85..fa950fdaac 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -2662,24 +2662,16 @@ mod test_reporting { r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - The branches of this `when` expression don't match the condition: + This `when` does not cover all the possibilities: 4│> when x is - 5│ Red -> 3 + 5│> Red -> 3 - This `x` value is a: + Other possibilities include: - [ Green, Red ] + Green - But the branch patterns have type: - - [ Red ] - - The branches must be cases of the `when` condition's type! - - Tip: Looks like the branches are missing coverage of the `Green` tag. - - Tip: Maybe you need to add a catch-all branch, like `_`? + I would have to crash if I saw one of those! Add branches for them! "# ), ) @@ -2702,25 +2694,17 @@ mod test_reporting { r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - The branches of this `when` expression don't match the condition: + This `when` does not cover all the possibilities: 4│> when x is - 5│ Red -> 0 - 6│ Green -> 1 + 5│> Red -> 0 + 6│> Green -> 1 - This `x` value is a: + Other possibilities include: - [ Blue, Green, Red ] + Blue - But the branch patterns have type: - - [ Green, Red ] - - The branches must be cases of the `when` condition's type! - - Tip: Looks like the branches are missing coverage of the `Blue` tag. - - Tip: Maybe you need to add a catch-all branch, like `_`? + I would have to crash if I saw one of those! Add branches for them! "# ), ) @@ -2743,25 +2727,18 @@ mod test_reporting { r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ - The branches of this `when` expression don't match the condition: + This `when` does not cover all the possibilities: 5│> when x is - 6│ NotAsked -> 3 + 6│> NotAsked -> 3 - This `x` value is a: + Other possibilities include: - [ Failure I64, Loading, NotAsked, Success Str ] + Failure _ + Loading + Success _ - But the branch patterns have type: - - [ NotAsked ] - - The branches must be cases of the `when` condition's type! - - Tip: Looks like the branches are missing coverage of the - `Success`, `Failure` and `Loading` tags. - - Tip: Maybe you need to add a catch-all branch, like `_`? + I would have to crash if I saw one of those! Add branches for them! "# ), ) From 45134a547de547e29b232e7cfaf51b49e25b3763 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 09:50:22 -0400 Subject: [PATCH 05/44] Update solve test --- compiler/solve/tests/solve_expr.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index ef59ea0a54..a72453e72f 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6243,7 +6243,7 @@ mod solve_expr { } #[test] - fn ability_member_takes_different_able_variable() { + fn ability_member_binds_different_able_variable() { infer_queries( indoc!( r#" @@ -6254,22 +6254,22 @@ mod solve_expr { IntoHash has intoHash : a -> b | a has IntoHash, b has Hash Id := U64 - hash = \$Id n -> n + hash = \@Id n -> n #^^^^{-1} User := Id - intoHash = \$User id, _ -> id + intoHash = \@User id -> id #^^^^^^^^{-1} - result = hash (intoHash ($User ($Id 123))) + result = hash (intoHash (@User (@Id 123))) # ^^^^ ^^^^^^^^ "# ), &[ "hash : Id -> U64", - "intoHash : User, Id -> Id", - "hash : Id -> U64", - "intoHash : User, Id -> Id", + "intoHash : User -> Id", + "hash : a -> U64 | a has Hash", + "intoHash : User -> a | a has Hash", ], ) } From 01af970b498ae896e2b356fbe16301fa87c098e1 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 17:15:34 -0400 Subject: [PATCH 06/44] Constraining ability members is the same as a usual def --- compiler/constrain/src/expr.rs | 2 +- compiler/constrain/src/module.rs | 67 ++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index a212adb654..a395f561be 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1771,7 +1771,7 @@ pub struct InstantiateRigids { pub new_infer_variables: Vec, } -fn instantiate_rigids( +pub fn instantiate_rigids( annotation: &Type, introduced_vars: &IntroducedVariables, loc_pattern: &Loc, diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index 0051a7049e..5b9b9c78a4 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -1,3 +1,7 @@ +use crate::expr::{ + constrain_def_make_constraint, constrain_def_pattern, instantiate_rigids, Env, + InstantiateRigids, +}; use roc_builtins::std::StdLib; use roc_can::abilities::AbilitiesStore; use roc_can::constraint::{Constraint, Constraints}; @@ -171,38 +175,53 @@ pub fn frontload_ability_constraints( constraints: &mut Constraints, abilities_store: &AbilitiesStore, mut constraint: Constraint, + home: ModuleId, ) -> Constraint { for (member_name, member_data) in abilities_store.root_ability_members().iter() { - // 1. Attach the type of member signature to the reserved signature_var. This is - // infallible. - let unify_with_signature_var = constraints.equal_types_var( - member_data.signature_var, - Expected::NoExpectation(member_data.signature.clone()), - Category::Storage(std::file!(), std::column!()), - Region::zero(), + let rigids = Default::default(); + let env = Env { home, rigids }; + let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(*member_name)); + + let mut def_pattern_state = constrain_def_pattern( + constraints, + &env, + &pattern, + Type::Variable(member_data.signature_var), ); - // 2. Store the member signature on the member symbol. This makes sure we generalize it on - // the toplevel, as appropriate. - let vars = &member_data.variables; - let rigids = (vars.rigid_vars.iter()) - // For our purposes, in the let constraint, able vars are treated like rigids. - .chain(vars.able_vars.iter()) - .copied(); - let flex = vars.flex_vars.iter().copied(); + def_pattern_state.vars.push(member_data.signature_var); - let let_constr = constraints.let_constraint( - rigids, - flex, - [( - *member_name, - Loc::at_zero(Type::Variable(member_data.signature_var)), - )], + let mut ftv = env.rigids; + + let InstantiateRigids { + signature, + new_rigid_variables, + new_infer_variables, + } = instantiate_rigids( + &member_data.signature, + &member_data.introduced_variables, + &pattern, + &mut ftv, + &mut def_pattern_state.headers, + ); + + def_pattern_state + .constraints + .push(constraints.equal_types_var( + member_data.signature_var, + Expected::NoExpectation(signature.clone()), + Category::Storage(file!(), line!()), + Region::zero(), + )); + + constraint = constrain_def_make_constraint( + constraints, + new_rigid_variables, + new_infer_variables, Constraint::True, constraint, + def_pattern_state, ); - - constraint = constraints.and_constraint([unify_with_signature_var, let_constr]); } constraint } From 2e57bf0b6a65962e712b0a64d70d4e6e3dcfe74e Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 17:17:24 -0400 Subject: [PATCH 07/44] Permit able variables in aliases, and thread them through --- compiler/can/src/abilities.rs | 20 ++++- compiler/can/src/annotation.rs | 95 ++++++++++++++++++---- compiler/can/src/def.rs | 61 +++++++++------ compiler/can/src/effect_module.rs | 13 +-- compiler/can/src/expr.rs | 5 +- compiler/can/src/module.rs | 4 +- compiler/can/src/pattern.rs | 5 +- compiler/can/src/scope.rs | 11 ++- compiler/constrain/src/builtins.rs | 14 ++-- compiler/constrain/src/expr.rs | 12 ++- compiler/constrain/src/pattern.rs | 13 ++- compiler/solve/src/solve.rs | 93 +++++++++++++++++----- compiler/solve/tests/solve_expr.rs | 46 +++++++---- compiler/types/src/solved_types.rs | 8 +- compiler/types/src/types.rs | 122 ++++++++++++++++++++++++++--- 15 files changed, 399 insertions(+), 123 deletions(-) diff --git a/compiler/can/src/abilities.rs b/compiler/can/src/abilities.rs index 43f958eece..9b19d71de2 100644 --- a/compiler/can/src/abilities.rs +++ b/compiler/can/src/abilities.rs @@ -1,3 +1,4 @@ +use crate::annotation::IntroducedVariables; use roc_collections::all::MutMap; use roc_module::symbol::Symbol; use roc_region::all::Region; @@ -13,12 +14,13 @@ pub struct MemberVariables { /// Stores information about an ability member definition, including the parent ability, the /// defining type, and what type variables need to be instantiated with instances of the ability. // TODO: SoA and put me in an arena -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct AbilityMemberData { pub parent_ability: Symbol, pub signature_var: Variable, pub signature: Type, pub variables: MemberVariables, + pub introduced_variables: IntroducedVariables, pub region: Region, } @@ -33,7 +35,7 @@ pub struct MemberSpecialization { /// ability, and what types implement them. // TODO(abilities): this should probably go on the Scope, I don't put it there for now because we // are only dealing with inter-module abilities for now. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone)] pub struct AbilitiesStore { /// Maps an ability to the members defining it. members_of_ability: MutMap>, @@ -61,10 +63,19 @@ impl AbilitiesStore { pub fn register_ability( &mut self, ability: Symbol, - members: Vec<(Symbol, Region, Variable, Type, MemberVariables)>, + members: Vec<( + Symbol, + Region, + Variable, + Type, + MemberVariables, + IntroducedVariables, + )>, ) { let mut members_vec = Vec::with_capacity(members.len()); - for (member, region, signature_var, signature, variables) in members.into_iter() { + for (member, region, signature_var, signature, variables, introduced_variables) in + members.into_iter() + { members_vec.push(member); let old_member = self.ability_members.insert( member, @@ -74,6 +85,7 @@ impl AbilitiesStore { signature, region, variables, + introduced_variables, }, ); debug_assert!(old_member.is_none(), "Replacing existing member definition"); diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 97cc04ee9a..0a567e12e7 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -9,8 +9,8 @@ use roc_problem::can::ShadowKind; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; use roc_types::types::{ - name_type_var, Alias, AliasCommon, AliasKind, LambdaSet, Problem, RecordField, Type, - TypeExtension, + name_type_var, Alias, AliasCommon, AliasKind, AliasVar, LambdaSet, OptAbleType, OptAbleVar, + Problem, RecordField, Type, TypeExtension, }; #[derive(Clone, Debug)] @@ -71,6 +71,48 @@ impl<'a> NamedOrAbleVariable<'a> { } } +pub enum OwnedNamedOrAble { + Named(NamedVariable), + Able(AbleVariable), +} + +impl OwnedNamedOrAble { + pub fn first_seen(&self) -> Region { + match self { + OwnedNamedOrAble::Named(nv) => nv.first_seen, + OwnedNamedOrAble::Able(av) => av.first_seen, + } + } + + pub fn ref_name(&self) -> &Lowercase { + match self { + OwnedNamedOrAble::Named(nv) => &nv.name, + OwnedNamedOrAble::Able(av) => &av.name, + } + } + + pub fn name(self) -> Lowercase { + match self { + OwnedNamedOrAble::Named(nv) => nv.name, + OwnedNamedOrAble::Able(av) => av.name, + } + } + + pub fn variable(&self) -> Variable { + match self { + OwnedNamedOrAble::Named(nv) => nv.variable, + OwnedNamedOrAble::Able(av) => av.variable, + } + } + + pub fn opt_ability(&self) -> Option { + match self { + OwnedNamedOrAble::Named(_) => None, + OwnedNamedOrAble::Able(av) => Some(av.ability), + } + } +} + /// A named type variable, not bound to an ability. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct NamedVariable { @@ -603,7 +645,7 @@ fn can_annotation_help( references, ); let mut vars = Vec::with_capacity(loc_vars.len()); - let mut lowercase_vars = Vec::with_capacity(loc_vars.len()); + let mut lowercase_vars: Vec> = Vec::with_capacity(loc_vars.len()); references.insert(symbol); @@ -616,9 +658,17 @@ fn can_annotation_help( }; let var_name = Lowercase::from(var); + // TODO(abilities): check that there are no abilities bound here. if let Some(var) = introduced_variables.var_by_name(&var_name) { vars.push(Type::Variable(var)); - lowercase_vars.push(Loc::at(loc_var.region, (var_name, var))); + lowercase_vars.push(Loc::at( + loc_var.region, + AliasVar { + name: var_name, + var, + opt_bound_ability: None, + }, + )); } else { let var = var_store.fresh(); @@ -626,7 +676,14 @@ fn can_annotation_help( .insert_named(var_name.clone(), Loc::at(loc_var.region, var)); vars.push(Type::Variable(var)); - lowercase_vars.push(Loc::at(loc_var.region, (var_name, var))); + lowercase_vars.push(Loc::at( + loc_var.region, + AliasVar { + name: var_name, + var, + opt_bound_ability: None, + }, + )); } } @@ -675,7 +732,7 @@ fn can_annotation_help( hidden_variables.extend(alias_actual.variables()); for loc_var in lowercase_vars.iter() { - hidden_variables.remove(&loc_var.value.1); + hidden_variables.remove(&loc_var.value.var); } scope.add_alias( @@ -702,7 +759,13 @@ fn can_annotation_help( } else { Type::Alias { symbol, - type_arguments: vars, + type_arguments: vars + .into_iter() + .map(|typ| OptAbleType { + typ, + opt_ability: None, + }) + .collect(), lambda_set_variables: alias.lambda_set_variables.clone(), actual: Box::new(alias.typ.clone()), kind: alias.kind, @@ -995,7 +1058,7 @@ fn shallow_dealias_with_scope<'a>(scope: &'a mut Scope, typ: &'a Type) -> &'a Ty pub fn instantiate_and_freshen_alias_type( var_store: &mut VarStore, introduced_variables: &mut IntroducedVariables, - type_variables: &[Loc<(Lowercase, Variable)>], + type_variables: &[Loc], type_arguments: Vec, lambda_set_variables: &[LambdaSet], mut actual_type: Type, @@ -1004,8 +1067,8 @@ pub fn instantiate_and_freshen_alias_type( let mut type_var_to_arg = Vec::new(); for (loc_var, arg_ann) in type_variables.iter().zip(type_arguments.into_iter()) { - let name = loc_var.value.0.clone(); - let var = loc_var.value.1; + let name = loc_var.value.name.clone(); + let var = loc_var.value.var; substitutions.insert(var, arg_ann.clone()); type_var_to_arg.push((name.clone(), arg_ann)); @@ -1040,19 +1103,21 @@ pub fn instantiate_and_freshen_alias_type( pub fn freshen_opaque_def( var_store: &mut VarStore, opaque: &Alias, -) -> (Vec, Vec, Type) { +) -> (Vec, Vec, Type) { debug_assert!(opaque.kind == AliasKind::Opaque); - let fresh_variables: Vec = opaque + let fresh_variables: Vec = opaque .type_variables .iter() - .map(|_| var_store.fresh()) + .map(|alias_var| OptAbleVar { + var: var_store.fresh(), + opt_ability: alias_var.value.opt_bound_ability, + }) .collect(); let fresh_type_arguments = fresh_variables .iter() - .copied() - .map(Type::Variable) + .map(|av| Type::Variable(av.var)) .collect(); // NB: We don't introduce the fresh variables here, we introduce them during constraint gen. diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 3fd2438f9f..aadf374fa4 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -2,6 +2,7 @@ use crate::abilities::MemberVariables; use crate::annotation::canonicalize_annotation; use crate::annotation::find_type_def_symbols; use crate::annotation::IntroducedVariables; +use crate::annotation::OwnedNamedOrAble; use crate::env::Env; use crate::expr::AnnotatedMark; use crate::expr::ClosureData; @@ -29,6 +30,7 @@ use roc_problem::can::{CycleEntry, Problem, RuntimeError}; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; use roc_types::types::AliasKind; +use roc_types::types::AliasVar; use roc_types::types::LambdaSet; use roc_types::types::{Alias, Type}; use std::fmt::Debug; @@ -318,38 +320,43 @@ pub(crate) fn canonicalize_defs<'a>( &abilities_in_scope, ); - // Does this alias reference any abilities? For now, we don't permit that. - let ability_references = can_ann - .references - .iter() - .filter_map(|&ty_ref| abilities_in_scope.iter().find(|&&name| name == ty_ref)) - .collect::>(); - - if let Some(one_ability_ref) = ability_references.first() { - env.problem(Problem::AliasUsesAbility { - loc_name: name, - ability: **one_ability_ref, - }); - } - // Record all the annotation's references in output.references.lookups for symbol in can_ann.references { output.references.insert_type_lookup(symbol); } - let mut can_vars: Vec> = Vec::with_capacity(vars.len()); + let mut can_vars: Vec> = Vec::with_capacity(vars.len()); let mut is_phantom = false; - let mut named = can_ann.introduced_variables.named; + let IntroducedVariables { + named, + able, + wildcards, + inferred, + .. + } = can_ann.introduced_variables; + + let mut named: Vec<_> = (named.into_iter().map(OwnedNamedOrAble::Named)) + .chain(able.into_iter().map(OwnedNamedOrAble::Able)) + .collect(); for loc_lowercase in vars.iter() { - let opt_index = named.iter().position(|nv| nv.name == loc_lowercase.value); + let opt_index = named + .iter() + .position(|nv| nv.ref_name() == &loc_lowercase.value); match opt_index { Some(index) => { // This is a valid lowercase rigid var for the type def. let named_variable = named.swap_remove(index); + let var = named_variable.variable(); + let opt_bound_ability = named_variable.opt_ability(); + let name = named_variable.name(); can_vars.push(Loc { - value: (named_variable.name, named_variable.variable), + value: AliasVar { + name, + var, + opt_bound_ability, + }, region: loc_lowercase.region, }); } @@ -370,16 +377,11 @@ pub(crate) fn canonicalize_defs<'a>( continue; } - let IntroducedVariables { - wildcards, - inferred, - .. - } = can_ann.introduced_variables; let num_unbound = named.len() + wildcards.len() + inferred.len(); if num_unbound > 0 { let one_occurrence = named .iter() - .map(|nv| Loc::at(nv.first_seen, nv.variable)) + .map(|nv| Loc::at(nv.first_seen(), nv.variable())) .chain(wildcards) .chain(inferred) .next() @@ -644,6 +646,7 @@ fn resolve_abilities<'a>( var_store.fresh(), member_annot.typ, variables, + iv, )); } @@ -1730,7 +1733,7 @@ fn make_tag_union_of_alias_recursive<'a>( let alias_args = alias .type_variables .iter() - .map(|l| (l.value.0.clone(), Type::Variable(l.value.1))) + .map(|l| (l.value.name.clone(), Type::Variable(l.value.var))) .collect::>(); let made_recursive = make_tag_union_recursive_help( @@ -1837,10 +1840,16 @@ fn make_tag_union_recursive_help<'a, 'b>( type_arguments, .. } => { + // NB: We need to collect the type arguments to shut off rustc's closure type + // instantiator. Otherwise we get unfortunate errors like + // reached the recursion limit while instantiating `make_tag_union_recursive_help::<...n/src/def.rs:1879:65: 1879:77]>>` + let type_arguments: Vec<&Type> = type_arguments.iter().map(|ta| &ta.typ).collect(); + let recursive_alias = Loc::at_zero((symbol, type_arguments.into_iter())); + // try to make `actual` recursive make_tag_union_recursive_help( env, - Loc::at_zero((symbol, type_arguments.iter())), + recursive_alias, region, others, actual, diff --git a/compiler/can/src/effect_module.rs b/compiler/can/src/effect_module.rs index 606fcbdfa0..7e055f3126 100644 --- a/compiler/can/src/effect_module.rs +++ b/compiler/can/src/effect_module.rs @@ -9,7 +9,7 @@ use roc_module::ident::TagName; use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable}; -use roc_types::types::{AliasKind, LambdaSet, Type, TypeExtension}; +use roc_types::types::{AliasKind, LambdaSet, OptAbleType, OptAbleVar, Type, TypeExtension}; #[derive(Debug, Default, Clone, Copy)] pub(crate) struct HostedGeneratedFunctions { @@ -1007,7 +1007,7 @@ fn build_effect_loop( Type::Alias { symbol: effect_symbol, - type_arguments: vec![state_type], + type_arguments: vec![OptAbleType::unbound(state_type)], lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable( closure_var, ))], @@ -1445,7 +1445,7 @@ fn build_effect_opaque( Type::Alias { symbol: effect_symbol, - type_arguments: vec![Type::Variable(a_var)], + type_arguments: vec![OptAbleType::unbound(Type::Variable(a_var))], lambda_set_variables: vec![roc_types::types::LambdaSet(Type::Variable(closure_var))], actual: Box::new(actual), kind: AliasKind::Opaque, @@ -1454,7 +1454,7 @@ fn build_effect_opaque( fn build_fresh_opaque_variables( var_store: &mut VarStore, -) -> (Box, Vec, Vec) { +) -> (Box, Vec, Vec) { let closure_var = var_store.fresh(); // NB: if there are bugs, check whether not introducing variables is a problem! @@ -1466,7 +1466,10 @@ fn build_fresh_opaque_variables( Box::new(Type::Variable(closure_var)), Box::new(Type::Variable(a_var)), ); - let type_arguments = vec![a_var]; + let type_arguments = vec![OptAbleVar { + var: a_var, + opt_ability: None, + }]; let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))]; (Box::new(actual), type_arguments, lambda_set_variables) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 6088dd3be8..5f4eb61b19 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -19,7 +19,7 @@ use roc_parse::pattern::PatternType::*; use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError}; use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable}; -use roc_types::types::{Alias, Category, LambdaSet, Type}; +use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type}; use std::fmt::{Debug, Display}; use std::{char, u32}; @@ -191,7 +191,8 @@ pub enum Expr { // for the expression from the opaque definition. `type_arguments` is something like // [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]". specialized_def_type: Box, - type_arguments: Vec, + // Fresh type variable and any ability it is bound to + type_arguments: Vec, lambda_set_variables: Vec, }, diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 5d90542d58..5953bd978a 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -18,7 +18,7 @@ use roc_parse::pattern::PatternType; use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; -use roc_types::types::{Alias, AliasKind, Type}; +use roc_types::types::{Alias, AliasKind, AliasVar, Type}; #[derive(Debug)] pub struct Module { @@ -119,7 +119,7 @@ impl GeneratedInfo { scope.add_alias( effect_symbol, Region::zero(), - vec![Loc::at_zero(("a".into(), a_var))], + vec![Loc::at_zero(AliasVar::unbound("a".into(), a_var))], actual, AliasKind::Opaque, ); diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index db26136195..e2da660297 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -13,7 +13,7 @@ use roc_parse::pattern::PatternType; use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError, ShadowKind}; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; -use roc_types::types::{LambdaSet, PatternCategory, Type}; +use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type}; /// A pattern, including possible problems (e.g. shadowing) so that /// codegen can generate a runtime error if this pattern is reached. @@ -47,7 +47,8 @@ pub enum Pattern { // for the expression from the opaque definition. `type_arguments` is something like // [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]". specialized_def_type: Box, - type_arguments: Vec, + // Fresh type variable and any ability it is bound to + type_arguments: Vec, lambda_set_variables: Vec, }, RecordDestructure { diff --git a/compiler/can/src/scope.rs b/compiler/can/src/scope.rs index 860cf04904..52867942b8 100644 --- a/compiler/can/src/scope.rs +++ b/compiler/can/src/scope.rs @@ -1,10 +1,9 @@ use roc_collections::VecMap; -use roc_module::ident::{Ident, Lowercase}; +use roc_module::ident::Ident; use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol}; use roc_problem::can::RuntimeError; use roc_region::all::{Loc, Region}; -use roc_types::subs::Variable; -use roc_types::types::{Alias, AliasKind, Type}; +use roc_types::types::{Alias, AliasKind, AliasVar, Type}; use crate::abilities::AbilitiesStore; @@ -335,7 +334,7 @@ impl Scope { &mut self, name: Symbol, region: Region, - vars: Vec>, + vars: Vec>, typ: Type, kind: AliasKind, ) { @@ -394,7 +393,7 @@ impl Scope { pub fn create_alias( name: Symbol, region: Region, - vars: Vec>, + vars: Vec>, typ: Type, kind: AliasKind, ) -> Alias { @@ -408,7 +407,7 @@ pub fn create_alias( let mut hidden = type_variables; for loc_var in vars.iter() { - hidden.remove(&loc_var.value.1); + hidden.remove(&loc_var.value.var); } if !hidden.is_empty() { diff --git a/compiler/constrain/src/builtins.rs b/compiler/constrain/src/builtins.rs index 7a0e9a73b3..a8095a825c 100644 --- a/compiler/constrain/src/builtins.rs +++ b/compiler/constrain/src/builtins.rs @@ -5,9 +5,9 @@ use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, Sig use roc_module::symbol::Symbol; use roc_region::all::Region; use roc_types::subs::Variable; -use roc_types::types::Reason; use roc_types::types::Type::{self, *}; use roc_types::types::{AliasKind, Category}; +use roc_types::types::{OptAbleType, Reason}; #[must_use] #[inline(always)] @@ -160,7 +160,7 @@ pub fn str_type() -> Type { #[inline(always)] fn builtin_alias( symbol: Symbol, - type_arguments: Vec, + type_arguments: Vec, actual: Box, kind: AliasKind, ) -> Type { @@ -177,7 +177,7 @@ fn builtin_alias( pub fn num_float(range: Type) -> Type { builtin_alias( Symbol::NUM_FLOAT, - vec![range.clone()], + vec![OptAbleType::unbound(range.clone())], Box::new(num_num(num_floatingpoint(range))), AliasKind::Structural, ) @@ -187,7 +187,7 @@ pub fn num_float(range: Type) -> Type { pub fn num_floatingpoint(range: Type) -> Type { builtin_alias( Symbol::NUM_FLOATINGPOINT, - vec![range.clone()], + vec![OptAbleType::unbound(range.clone())], Box::new(range), AliasKind::Opaque, ) @@ -227,7 +227,7 @@ pub fn num_binary64() -> Type { pub fn num_int(range: Type) -> Type { builtin_alias( Symbol::NUM_INT, - vec![range.clone()], + vec![OptAbleType::unbound(range.clone())], Box::new(num_num(num_integer(range))), AliasKind::Structural, ) @@ -247,7 +247,7 @@ pub fn num_signed64() -> Type { pub fn num_integer(range: Type) -> Type { builtin_alias( Symbol::NUM_INTEGER, - vec![range.clone()], + vec![OptAbleType::unbound(range.clone())], Box::new(range), AliasKind::Opaque, ) @@ -257,7 +257,7 @@ pub fn num_integer(range: Type) -> Type { pub fn num_num(typ: Type) -> Type { builtin_alias( Symbol::NUM_NUM, - vec![typ.clone()], + vec![OptAbleType::unbound(typ.clone())], Box::new(typ), AliasKind::Opaque, ) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index a395f561be..0332f15e13 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -18,7 +18,7 @@ use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; use roc_types::types::Type::{self, *}; use roc_types::types::{ - AliasKind, AnnotationSource, Category, PReason, Reason, RecordField, TypeExtension, + AliasKind, AnnotationSource, Category, OptAbleType, PReason, Reason, RecordField, TypeExtension, }; /// This is for constraining Defs @@ -998,7 +998,13 @@ pub fn constrain_expr( let opaque_type = Type::Alias { symbol: *name, - type_arguments: type_arguments.iter().copied().map(Type::Variable).collect(), + type_arguments: type_arguments + .iter() + .map(|v| OptAbleType { + typ: Type::Variable(v.var), + opt_ability: v.opt_ability, + }) + .collect(), lambda_set_variables: lambda_set_variables.clone(), actual: Box::new(arg_type.clone()), kind: AliasKind::Opaque, @@ -1035,7 +1041,7 @@ pub fn constrain_expr( let mut vars = vec![*arg_var, *opaque_var]; // Also add the fresh variables we created for the type argument and lambda sets - vars.extend(type_arguments); + vars.extend(type_arguments.iter().map(|v| v.var)); vars.extend(lambda_set_variables.iter().map(|v| { v.0.expect_variable("all lambda sets should be fresh variables here") })); diff --git a/compiler/constrain/src/pattern.rs b/compiler/constrain/src/pattern.rs index b2dde1ae63..19d552ddf4 100644 --- a/compiler/constrain/src/pattern.rs +++ b/compiler/constrain/src/pattern.rs @@ -10,7 +10,8 @@ use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; use roc_types::types::{ - AliasKind, Category, PReason, PatternCategory, Reason, RecordField, Type, TypeExtension, + AliasKind, Category, OptAbleType, PReason, PatternCategory, Reason, RecordField, Type, + TypeExtension, }; #[derive(Default)] @@ -514,7 +515,13 @@ pub fn constrain_pattern( let opaque_type = Type::Alias { symbol: *opaque, - type_arguments: type_arguments.iter().copied().map(Type::Variable).collect(), + type_arguments: type_arguments + .iter() + .map(|v| OptAbleType { + typ: Type::Variable(v.var), + opt_ability: v.opt_ability, + }) + .collect(), lambda_set_variables: lambda_set_variables.clone(), actual: Box::new(arg_pattern_type.clone()), kind: AliasKind::Opaque, @@ -571,7 +578,7 @@ pub fn constrain_pattern( .vars .extend_from_slice(&[*arg_pattern_var, *whole_var]); // Also add the fresh variables we created for the type argument and lambda sets - state.vars.extend(type_arguments); + state.vars.extend(type_arguments.iter().map(|v| v.var)); state.vars.extend(lambda_set_variables.iter().map(|v| { v.0.expect_variable("all lambda sets should be fresh variables here") })); diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 7c361f4ac6..cd4b9960d6 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -17,8 +17,8 @@ use roc_types::subs::{ }; use roc_types::types::Type::{self, *}; use roc_types::types::{ - gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, PatternCategory, - Reason, TypeExtension, + gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, LambdaSet, + OptAbleType, OptAbleVar, PatternCategory, Reason, TypeExtension, }; use roc_unify::unify::{unify, Mode, Unified::*}; @@ -115,7 +115,7 @@ struct DelayedAliasVariables { } impl DelayedAliasVariables { - fn recursion_variables(self, variables: &mut [Variable]) -> &mut [Variable] { + fn recursion_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { let start = self.start as usize + (self.type_variables_len + self.lambda_set_variables_len) as usize; let length = self.recursion_variables_len as usize; @@ -123,14 +123,14 @@ impl DelayedAliasVariables { &mut variables[start..][..length] } - fn lambda_set_variables(self, variables: &mut [Variable]) -> &mut [Variable] { + fn lambda_set_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { let start = self.start as usize + self.type_variables_len as usize; let length = self.lambda_set_variables_len as usize; &mut variables[start..][..length] } - fn type_variables(self, variables: &mut [Variable]) -> &mut [Variable] { + fn type_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { let start = self.start as usize; let length = self.type_variables_len as usize; @@ -141,7 +141,7 @@ impl DelayedAliasVariables { #[derive(Debug, Default)] pub struct Aliases { aliases: Vec<(Symbol, Type, DelayedAliasVariables, AliasKind)>, - variables: Vec, + variables: Vec, } impl Aliases { @@ -151,17 +151,29 @@ impl Aliases { let start = self.variables.len() as _; self.variables - .extend(alias.type_variables.iter().map(|x| x.value.1)); + // TODO: propogate ability? + .extend( + alias + .type_variables + .iter() + .map(|x| OptAbleVar::from(&x.value)), + ); self.variables.extend(alias.lambda_set_variables.iter().map( |x| match x.as_inner() { - Type::Variable(v) => *v, + Type::Variable(v) => OptAbleVar::unbound(*v), _ => unreachable!("lambda set type is not a variable"), }, )); let recursion_variables_len = alias.recursion_variables.len() as _; - self.variables.extend(alias.recursion_variables); + self.variables.extend( + alias + .recursion_variables + .iter() + .copied() + .map(OptAbleVar::unbound), + ); DelayedAliasVariables { start, @@ -303,25 +315,31 @@ impl Aliases { let mut substitutions: MutMap<_, _> = Default::default(); - for rec_var in delayed_variables + for OptAbleVar { + var: rec_var, + opt_ability, + } in delayed_variables .recursion_variables(&mut self.variables) .iter_mut() { + debug_assert!(opt_ability.is_none()); let new_var = subs.fresh_unnamed_flex_var(); substitutions.insert(*rec_var, new_var); *rec_var = new_var; } let old_type_variables = delayed_variables.type_variables(&mut self.variables); + let variable_opt_abilities: Vec<_> = + old_type_variables.iter().map(|ov| ov.opt_ability).collect(); let new_type_variables = &subs.variables[alias_variables.type_variables().indices()]; for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) { // if constraint gen duplicated a type these variables could be the same // (happens very often in practice) - if *old != *new { - substitutions.insert(*old, *new); + if old.var != *new { + substitutions.insert(old.var, *new); - *old = *new; + old.var = *new; } } @@ -333,9 +351,10 @@ impl Aliases { .iter_mut() .zip(new_lambda_set_variables) { - if *old != *new { - substitutions.insert(*old, *new); - *old = *new; + debug_assert!(old.opt_ability.is_none()); + if old.var != *new { + substitutions.insert(old.var, *new); + old.var = *new; } } @@ -1937,8 +1956,46 @@ fn type_to_variable<'a>( let length = type_arguments.len() + lambda_set_variables.len(); let new_variables = VariableSubsSlice::reserve_into_subs(subs, length); - for (target_index, arg_type) in (new_variables.indices()).zip(type_arguments) { - let copy_var = helper!(arg_type); + for (target_index, OptAbleType { typ, opt_ability }) in + (new_variables.indices()).zip(type_arguments) + { + let copy_var = match opt_ability { + None => helper!(typ), + Some(ability) => { + match RegisterVariable::from_type(subs, rank, pools, arena, typ) { + RegisterVariable::Direct(var) => { + use Content::*; + match *subs.get_content_without_compacting(var) { + FlexVar(opt_name) => subs + .set_content(var, FlexAbleVar(opt_name, *ability)), + RigidVar(name) => subs.set_content( + var, + FlexAbleVar(Some(name), *ability), + ), + RigidAbleVar(name, existing_ability) => { + debug_assert_eq!(existing_ability, *ability); + subs.set_content( + var, + FlexAbleVar(Some(name), existing_ability), + ); + } + _ => { + // TODO associate the type to the bound ability, and check + // that it correctly implements the ability. + } + } + var + } + RegisterVariable::Deferred => { + // TODO associate the type to the bound ability, and check + // that it correctly implements the ability. + let var = subs.fresh_unnamed_flex_var(); + stack.push(TypeToVar::Defer(typ, var)); + var + } + } + } + }; subs.variables[target_index] = copy_var; } diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index a72453e72f..0222acac32 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6243,33 +6243,47 @@ mod solve_expr { } #[test] - fn ability_member_binds_different_able_variable() { + fn encoder() { infer_queries( indoc!( r#" - app "test" provides [ result ] to "./platform" + app "test" provides [ myU8Bytes ] to "./platform" - Hash has hash : a -> U64 | a has Hash + Encoder fmt := List U8, fmt -> List U8 | fmt has Format - IntoHash has intoHash : a -> b | a has IntoHash, b has Hash + Encoding has + toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format - Id := U64 - hash = \@Id n -> n - #^^^^{-1} + Format has + u8 : U8 -> Encoder fmt | fmt has Format - User := Id - intoHash = \@User id -> id - #^^^^^^^^{-1} + appendWith : List U8, Encoder fmt, fmt -> List U8 | fmt has Format + appendWith = \lst, (@Encoder doFormat), fmt -> doFormat lst fmt - result = hash (intoHash (@User (@Id 123))) - # ^^^^ ^^^^^^^^ + toBytes : val, fmt -> List U8 | val has Encoding, fmt has Format + toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt + + + Linear := {} + + # impl Format for Linear + u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n) + #^^{-1} + + MyU8 := U8 + + # impl Encoding for MyU8 + toEncoder = \@MyU8 n -> u8 n + #^^^^^^^^^{-1} + + myU8Bytes = toBytes (@MyU8 15) (@Linear {}) + #^^^^^^^^^{-1} "# ), &[ - "hash : Id -> U64", - "intoHash : User -> Id", - "hash : a -> U64 | a has Hash", - "intoHash : User -> a | a has Hash", + "u8 : U8 -> Encoder Linear", + "toEncoder : MyU8 -> Encoder fmt | fmt has Format", + "myU8Bytes : List U8", ], ) } diff --git a/compiler/types/src/solved_types.rs b/compiler/types/src/solved_types.rs index 1f4eeb4f1c..7a899f6c46 100644 --- a/compiler/types/src/solved_types.rs +++ b/compiler/types/src/solved_types.rs @@ -1,5 +1,5 @@ use crate::subs::{VarId, VarStore, Variable}; -use crate::types::{AliasKind, Problem, RecordField, Type, TypeExtension}; +use crate::types::{AliasKind, OptAbleType, Problem, RecordField, Type, TypeExtension}; use roc_collections::all::{ImMap, SendMap}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::Symbol; @@ -213,7 +213,11 @@ pub fn to_type( let mut type_variables = Vec::with_capacity(solved_type_variables.len()); for solved_arg in solved_type_variables { - type_variables.push(to_type(solved_arg, free_vars, var_store)); + type_variables.push(OptAbleType { + typ: to_type(solved_arg, free_vars, var_store), + // TODO: is this always correct? + opt_ability: None, + }); } let mut lambda_set_variables = Vec::with_capacity(solved_lambda_sets.len()); diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index 92cfdd9020..0ba04ba799 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -191,6 +191,36 @@ pub struct AliasCommon { pub lambda_set_variables: Vec, } +#[derive(Clone, Copy, Debug)] +pub struct OptAbleVar { + pub var: Variable, + pub opt_ability: Option, +} + +impl OptAbleVar { + pub fn unbound(var: Variable) -> Self { + Self { + var, + opt_ability: None, + } + } +} + +#[derive(PartialEq, Eq, Debug)] +pub struct OptAbleType { + pub typ: Type, + pub opt_ability: Option, +} + +impl OptAbleType { + pub fn unbound(typ: Type) -> Self { + Self { + typ, + opt_ability: None, + } + } +} + #[derive(PartialEq, Eq)] pub enum Type { EmptyRec, @@ -208,7 +238,7 @@ pub enum Type { DelayedAlias(AliasCommon), Alias { symbol: Symbol, - type_arguments: Vec, + type_arguments: Vec, lambda_set_variables: Vec, actual: Box, kind: AliasKind, @@ -300,6 +330,16 @@ impl Clone for Type { } } +impl Clone for OptAbleType { + fn clone(&self) -> Self { + // This passes through `Type`, so defer to that to bump the clone counter. + Self { + typ: self.typ.clone(), + opt_ability: self.opt_ability, + } + } +} + #[derive(PartialEq, Eq, Clone)] pub enum TypeExtension { Open(Box), @@ -410,7 +450,10 @@ impl fmt::Debug for Type { write!(f, "(Alias {:?}", symbol)?; for arg in type_arguments { - write!(f, " {:?}", arg)?; + write!(f, " {:?}", &arg.typ)?; + if let Some(ab) = arg.opt_ability { + write!(f, ":{:?}", ab)?; + } } for (lambda_set, greek_letter) in @@ -709,7 +752,7 @@ impl Type { .. } => { for value in type_arguments.iter_mut() { - stack.push(value); + stack.push(&mut value.typ); } for lambda_set in lambda_set_variables.iter_mut() { @@ -818,7 +861,7 @@ impl Type { .. } => { for value in type_arguments.iter_mut() { - stack.push(value); + stack.push(&mut value.typ); } for lambda_set in lambda_set_variables.iter_mut() { stack.push(lambda_set.as_inner_mut()); @@ -915,7 +958,7 @@ impl Type { .. } => { for ta in type_arguments { - ta.substitute_alias(rep_symbol, rep_args, actual)?; + ta.typ.substitute_alias(rep_symbol, rep_args, actual)?; } alias_actual.substitute_alias(rep_symbol, rep_args, actual) } @@ -1140,15 +1183,35 @@ impl Type { lambda_set_variables, actual: actual_type, .. + } => { + for arg in type_args { + arg.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables); + } + + for arg in lambda_set_variables { + arg.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables); + } + + actual_type.instantiate_aliases( + region, + aliases, + var_store, + new_lambda_set_variables, + ); } - | Alias { + Alias { type_arguments: type_args, lambda_set_variables, actual: actual_type, .. } => { for arg in type_args { - arg.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables); + arg.typ.instantiate_aliases( + region, + aliases, + var_store, + new_lambda_set_variables, + ); } for arg in lambda_set_variables { @@ -1210,7 +1273,12 @@ impl Type { // TODO substitute further in args for ( Loc { - value: (_, placeholder), + value: + AliasVar { + var: placeholder, + opt_bound_ability, + .. + }, .. }, filler, @@ -1223,7 +1291,10 @@ impl Type { var_store, new_lambda_set_variables, ); - named_args.push(filler.clone()); + named_args.push(OptAbleType { + typ: filler.clone(), + opt_ability: *opt_bound_ability, + }); substitution.insert(*placeholder, filler); } @@ -1509,7 +1580,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { .. } => { for arg in type_arguments { - variables_help(arg, accum); + variables_help(&arg.typ, accum); } variables_help(actual, accum); } @@ -1645,7 +1716,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) { .. } => { for arg in type_arguments { - variables_help_detailed(arg, accum); + variables_help_detailed(&arg.typ, accum); } variables_help_detailed(actual, accum); } @@ -1862,10 +1933,37 @@ pub enum AliasKind { Opaque, } +#[derive(Clone, Debug, PartialEq)] +pub struct AliasVar { + pub name: Lowercase, + pub var: Variable, + /// `Some` if this variable is bound to an ability; `None` otherwise. + pub opt_bound_ability: Option, +} + +impl AliasVar { + pub fn unbound(name: Lowercase, var: Variable) -> AliasVar { + Self { + name, + var, + opt_bound_ability: None, + } + } +} + +impl From<&AliasVar> for OptAbleVar { + fn from(av: &AliasVar) -> OptAbleVar { + OptAbleVar { + var: av.var, + opt_ability: av.opt_bound_ability, + } + } +} + #[derive(Clone, Debug, PartialEq)] pub struct Alias { pub region: Region, - pub type_variables: Vec>, + pub type_variables: Vec>, /// lambda set variables, e.g. the one annotating the arrow in /// a |c|-> b From 7b195d5fdf9068ea3d72f998c6a51c3dc7e07d73 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 17:24:40 -0400 Subject: [PATCH 08/44] Abilities in aliases and external member defs are legal now --- reporting/tests/helpers/mod.rs | 7 +++- reporting/tests/test_reporting.rs | 66 ------------------------------- 2 files changed, 5 insertions(+), 68 deletions(-) diff --git a/reporting/tests/helpers/mod.rs b/reporting/tests/helpers/mod.rs index 9729112a75..4a429c4c5b 100644 --- a/reporting/tests/helpers/mod.rs +++ b/reporting/tests/helpers/mod.rs @@ -17,7 +17,7 @@ use roc_problem::can::Problem; use roc_region::all::Loc; use roc_solve::solve::{self, Aliases}; use roc_types::subs::{Content, Subs, VarStore, Variable}; -use roc_types::types::Type; +use roc_types::types::{AliasVar, Type}; use std::hash::Hash; use std::path::{Path, PathBuf}; @@ -228,7 +228,10 @@ fn add_aliases(scope: &mut Scope, var_store: &mut VarStore) { let mut type_variables: Vec<_> = free_vars.unnamed_vars.into_iter().collect(); type_variables.sort(); for (loc_name, (_, var)) in vars.iter().zip(type_variables) { - variables.push(Loc::at(loc_name.region, (loc_name.value.clone(), var))); + variables.push(Loc::at( + loc_name.region, + AliasVar::unbound(loc_name.value.clone(), var), + )); } scope.add_alias(symbol, region, variables, typ, kind); diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index fa950fdaac..2fe63c75cc 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -9019,41 +9019,6 @@ I need all branches in an `if` to have the same type! ) } - #[test] - fn alias_using_ability() { - new_report_problem_as( - "alias_using_ability", - indoc!( - r#" - app "test" provides [ a ] to "./platform" - - Ability has ab : a -> {} | a has Ability - - Alias : Ability - - a : Alias - "# - ), - indoc!( - r#" - ── ALIAS USES ABILITY ──────────────────────────────────── /code/proj/Main.roc ─ - - The definition of the `Alias` aliases references the ability `Ability`: - - 5│ Alias : Ability - ^^^^^ - - Abilities are not types, but you can add an ability constraint to a - type variable `a` by writing - - | a has Ability - - at the end of the type. - "# - ), - ) - } - #[test] fn ability_shadows_ability() { new_report_problem_as( @@ -9130,37 +9095,6 @@ I need all branches in an `if` to have the same type! ) } - #[test] - fn ability_member_binds_extra_ability() { - new_report_problem_as( - "ability_member_binds_extra_ability", - indoc!( - r#" - app "test" provides [ eq ] to "./platform" - - Eq has eq : a, a -> Bool.Bool | a has Eq - Hash has hash : a, b -> Num.U64 | a has Eq, b has Hash - "# - ), - indoc!( - r#" - ── ABILITY MEMBER HAS EXTRANEOUS HAS CLAUSE ────────────── /code/proj/Main.roc ─ - - The definition of the ability member `hash` includes a has clause - binding an ability it is not a part of: - - 4│ Hash has hash : a, b -> Num.U64 | a has Eq, b has Hash - ^^^^^^^^ - - Currently, ability members can only bind variables to the ability they - are a part of. - - Hint: Did you mean to bind the `Hash` ability instead? - "# - ), - ) - } - #[test] fn ability_member_binds_parent_twice() { new_report_problem_as( From a242a9041636f34580dc7bd3b30181dd6ef7765d Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 17:42:55 -0400 Subject: [PATCH 09/44] Stale comment --- compiler/solve/src/solve.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index cd4b9960d6..a18a8caeb9 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -150,14 +150,12 @@ impl Aliases { { let start = self.variables.len() as _; - self.variables - // TODO: propogate ability? - .extend( - alias - .type_variables - .iter() - .map(|x| OptAbleVar::from(&x.value)), - ); + self.variables.extend( + alias + .type_variables + .iter() + .map(|x| OptAbleVar::from(&x.value)), + ); self.variables.extend(alias.lambda_set_variables.iter().map( |x| match x.as_inner() { From c1b1f60630e86d90f1a96739f1bd38c7f1d77b55 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Wed, 4 May 2022 17:49:42 -0400 Subject: [PATCH 10/44] Coupon cutting --- compiler/can/src/def.rs | 1 + compiler/mono/src/ir.rs | 10 ++-------- compiler/solve/src/ability.rs | 15 ++++++--------- compiler/solve/src/solve.rs | 1 + 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index aadf374fa4..fee660fa83 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1843,6 +1843,7 @@ fn make_tag_union_recursive_help<'a, 'b>( // NB: We need to collect the type arguments to shut off rustc's closure type // instantiator. Otherwise we get unfortunate errors like // reached the recursion limit while instantiating `make_tag_union_recursive_help::<...n/src/def.rs:1879:65: 1879:77]>>` + #[allow(clippy::needless_collect)] let type_arguments: Vec<&Type> = type_arguments.iter().map(|ta| &ta.typ).collect(); let recursive_alias = Loc::at_zero((symbol, type_arguments.into_iter())); diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 8abbaf9b14..b82d9b2297 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -4922,14 +4922,7 @@ fn get_specialization<'a>( Some(member) => { let snapshot = env.subs.snapshot(); instantiate_rigids(env.subs, member.signature_var); - let this_f = env.subs.get_content_without_compacting(symbol_var); - let member_f = env - .subs - .get_content_without_compacting(member.signature_var); - use roc_types::subs::SubsFmtContent; - let this_f = SubsFmtContent(&this_f, env.subs); - let member_f = SubsFmtContent(&member_f, env.subs); - dbg!(symbol, this_f, member_f); + let (_, must_implement_ability) = unify( env.subs, symbol_var, @@ -4938,6 +4931,7 @@ fn get_specialization<'a>( ) .expect_success("This typechecked previously"); env.subs.rollback_to(snapshot); + let specializing_type = type_implementing_member(&must_implement_ability, member.parent_ability); diff --git a/compiler/solve/src/ability.rs b/compiler/solve/src/ability.rs index d7479c0780..ee23b5567c 100644 --- a/compiler/solve/src/ability.rs +++ b/compiler/solve/src/ability.rs @@ -170,15 +170,12 @@ pub fn type_implementing_member( ability: Symbol, ) -> Symbol { debug_assert_eq!({ - let ability_implementations_for_specialization = - specialization_must_implement_constraints - .clone() - .get_unique() - .into_iter() - .filter(|mia| mia.ability == ability) - .count(); - - ability_implementations_for_specialization + specialization_must_implement_constraints + .clone() + .get_unique() + .into_iter() + .filter(|mia| mia.ability == ability) + .count() }, 1, "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}", diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index a18a8caeb9..2cb1282b65 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -327,6 +327,7 @@ impl Aliases { } let old_type_variables = delayed_variables.type_variables(&mut self.variables); + #[allow(clippy::needless_collect)] // otherwise we borrow self.variables immutably twice let variable_opt_abilities: Vec<_> = old_type_variables.iter().map(|ov| ov.opt_ability).collect(); let new_type_variables = &subs.variables[alias_variables.type_variables().indices()]; From f48792a17391f3cf87250e0c553fdebde0033d8e Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 5 May 2022 11:49:12 -0400 Subject: [PATCH 11/44] Add encoder tests --- compiler/solve/tests/solve_expr.rs | 2 +- compiler/test_gen/src/gen_abilities.rs | 42 ++++++++++++++++++-------- compiler/test_mono/src/tests.rs | 32 ++++++++++++++++++++ 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 0222acac32..97016b1b07 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6264,7 +6264,7 @@ mod solve_expr { toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt - Linear := {} + Linear := {} # impl Format for Linear u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n) diff --git a/compiler/test_gen/src/gen_abilities.rs b/compiler/test_gen/src/gen_abilities.rs index 66dd302d1b..56056d5d43 100644 --- a/compiler/test_gen/src/gen_abilities.rs +++ b/compiler/test_gen/src/gen_abilities.rs @@ -10,6 +10,9 @@ use crate::helpers::wasm::assert_evals_to; #[cfg(test)] use indoc::indoc; +#[cfg(test)] +use roc_std::RocList; + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn hash_specialization() { @@ -218,27 +221,42 @@ fn ability_used_as_type_still_compiles() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -fn ability_member_takes_different_able_variable() { +#[cfg(any(feature = "gen-llvm"))] +fn encode() { assert_evals_to!( indoc!( r#" - app "test" provides [ result ] to "./platform" + app "test" provides [ myU8Bytes ] to "./platform" - Hash has hash : a -> U64 | a has Hash + Encoder fmt := List U8, fmt -> List U8 | fmt has Format - IntoHash has intoHash : a, b -> b | a has IntoHash, b has Hash + Encoding has + toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format - Id := U64 - hash = \$Id n -> n + Format has + u8 : U8 -> Encoder fmt | fmt has Format - User := Id - intoHash = \$User id, _ -> id + appendWith : List U8, Encoder fmt, fmt -> List U8 | fmt has Format + appendWith = \lst, (@Encoder doFormat), fmt -> doFormat lst fmt - result = hash (intoHash ($User ($Id 123)) ($Id 1)) + toBytes : val, fmt -> List U8 | val has Encoding, fmt has Format + toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt + + + Linear := {} + + # impl Format for Linear + u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n) + + MyU8 := U8 + + # impl Encoding for MyU8 + toEncoder = \@MyU8 n -> u8 n + + myU8Bytes = toBytes (@MyU8 15) (@Linear {}) "# ), - 123, - u64 + RocList::from_slice(&[15]), + RocList ) } diff --git a/compiler/test_mono/src/tests.rs b/compiler/test_mono/src/tests.rs index 859776724f..abdcc5b7b3 100644 --- a/compiler/test_mono/src/tests.rs +++ b/compiler/test_mono/src/tests.rs @@ -1344,6 +1344,38 @@ fn opaque_assign_to_symbol() { ) } +#[mono_test] +fn encode() { + indoc!( + r#" + app "test" provides [ myU8Bytes ] to "./platform" + + Encoder fmt := List U8, fmt -> List U8 | fmt has Format + + Encoding has + toEncoder : val -> Encoder fmt | val has Encoding, fmt has Format + + Format has + u8 : U8 -> Encoder fmt | fmt has Format + + + Linear := {} + + # impl Format for Linear + u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n) + + MyU8 := U8 + + # impl Encoding for MyU8 + toEncoder = \@MyU8 n -> u8 n + + myU8Bytes = + when toEncoder (@MyU8 15) is + @Encoder doEncode -> doEncode [] (@Linear {}) + "# + ) +} + // #[ignore] // #[mono_test] // fn static_str_closure() { From d2eb5e17ecdd942004d1f912ea7103ca248c7920 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 5 May 2022 12:59:04 -0400 Subject: [PATCH 12/44] Fill out can traversals --- compiler/can/src/traverse.rs | 201 +++++++++++++++++++++++++++++------ 1 file changed, 169 insertions(+), 32 deletions(-) diff --git a/compiler/can/src/traverse.rs b/compiler/can/src/traverse.rs index 8f579d39a7..8ad473e0be 100644 --- a/compiler/can/src/traverse.rs +++ b/compiler/can/src/traverse.rs @@ -1,11 +1,12 @@ //! Traversals over the can ast. +use roc_module::ident::Lowercase; use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; use crate::{ def::{Annotation, Declaration, Def}, - expr::{ClosureData, Expr, WhenBranch}, + expr::{AccessorData, ClosureData, Expr, Field, WhenBranch}, pattern::Pattern, }; @@ -31,7 +32,7 @@ fn walk_decl(visitor: &mut V, decl: &Declaration) { } Declaration::Builtin(def) => visitor.visit_def(def), Declaration::InvalidCycle(_cycles) => { - todo!() + // ignore } } } @@ -71,13 +72,105 @@ fn walk_expr(visitor: &mut V, expr: &Expr) { } => { walk_when(visitor, *cond_var, *expr_var, loc_cond, branches); } - Expr::Call(f, loc_args, _) => { - walk_call(visitor, f, loc_args); + Expr::Num(..) => { /* terminal */ } + Expr::Int(..) => { /* terminal */ } + Expr::Float(..) => { /* terminal */ } + Expr::Str(..) => { /* terminal */ } + Expr::SingleQuote(..) => { /* terminal */ } + Expr::List { + elem_var, + loc_elems, + } => { + walk_list(visitor, *elem_var, loc_elems); } - e => todo!("{:?}", e), + Expr::Var(..) => { /* terminal */ } + Expr::If { + cond_var, + branches, + branch_var, + final_else, + } => walk_if(visitor, *cond_var, branches, *branch_var, final_else), + Expr::LetRec(defs, body, body_var) => { + defs.iter().for_each(|def| visitor.visit_def(def)); + visitor.visit_expr(&body.value, body.region, *body_var); + } + Expr::LetNonRec(def, body, body_var) => { + visitor.visit_def(def); + visitor.visit_expr(&body.value, body.region, *body_var); + } + Expr::Call(f, args, _called_via) => { + let (fn_var, loc_fn, _closure_var, _ret_var) = &**f; + walk_call(visitor, *fn_var, loc_fn, args); + } + Expr::RunLowLevel { + op: _, + args, + ret_var: _, + } => { + args.iter() + .for_each(|(v, e)| visitor.visit_expr(e, Region::zero(), *v)); + } + Expr::ForeignCall { + foreign_symbol: _, + args, + ret_var: _, + } => { + args.iter() + .for_each(|(v, e)| visitor.visit_expr(e, Region::zero(), *v)); + } + Expr::Record { + record_var: _, + fields, + } => { + walk_record_fields(visitor, fields.iter()); + } + Expr::EmptyRecord => { /* terminal */ } + Expr::Access { + field_var, + loc_expr, + field: _, + record_var: _, + ext_var: _, + } => visitor.visit_expr(&loc_expr.value, loc_expr.region, *field_var), + Expr::Accessor(AccessorData { .. }) => { /* terminal */ } + Expr::Update { + record_var: _, + ext_var: _, + symbol: _, + updates, + } => { + walk_record_fields(visitor, updates.iter()); + } + Expr::Tag { + variant_var: _, + ext_var: _, + name: _, + arguments, + } => arguments + .iter() + .for_each(|(v, le)| visitor.visit_expr(&le.value, le.region, *v)), + Expr::ZeroArgumentTag { .. } => { /* terminal */ } + Expr::OpaqueRef { + opaque_var: _, + name: _, + argument, + specialized_def_type: _, + type_arguments: _, + lambda_set_variables: _, + } => { + let (var, le) = &**argument; + visitor.visit_expr(&le.value, le.region, *var); + } + Expr::Expect(e1, e2) => { + // TODO: what type does an expect have? + visitor.visit_expr(&e1.value, e1.region, Variable::NULL); + visitor.visit_expr(&e2.value, e2.region, Variable::NULL); + } + Expr::RuntimeError(..) => { /* terminal */ } } } +#[inline(always)] fn walk_closure(visitor: &mut V, clos: &ClosureData) { let ClosureData { arguments, @@ -93,6 +186,7 @@ fn walk_closure(visitor: &mut V, clos: &ClosureData) { visitor.visit_expr(&loc_body.value, loc_body.region, *return_type); } +#[inline(always)] fn walk_when( visitor: &mut V, cond_var: Variable, @@ -107,6 +201,7 @@ fn walk_when( .for_each(|branch| walk_when_branch(visitor, branch, expr_var)); } +#[inline(always)] fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) { let WhenBranch { patterns, @@ -124,23 +219,58 @@ fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: } } +#[inline(always)] +fn walk_list(visitor: &mut V, elem_var: Variable, loc_elems: &[Loc]) { + loc_elems + .iter() + .for_each(|le| visitor.visit_expr(&le.value, le.region, elem_var)); +} + +#[inline(always)] +fn walk_if( + visitor: &mut V, + cond_var: Variable, + branches: &[(Loc, Loc)], + branch_var: Variable, + final_else: &Loc, +) { + branches.iter().for_each(|(cond, body)| { + visitor.visit_expr(&cond.value, cond.region, cond_var); + visitor.visit_expr(&body.value, body.region, branch_var); + }); + visitor.visit_expr(&final_else.value, final_else.region, branch_var); +} + +#[inline(always)] fn walk_call( visitor: &mut V, - f: &(Variable, Loc, Variable, Variable), - loc_args: &[(Variable, Loc)], + fn_var: Variable, + fn_expr: &Loc, + args: &[(Variable, Loc)], ) { - let (fn_var, loc_fn_expr, _lambda_set, _ret) = f; - visitor.visit_expr(&loc_fn_expr.value, loc_fn_expr.region, *fn_var); - loc_args - .iter() - .for_each(|(v, e)| visitor.visit_expr(&e.value, e.region, *v)); + visitor.visit_expr(&fn_expr.value, fn_expr.region, fn_var); + args.iter() + .for_each(|(v, le)| visitor.visit_expr(&le.value, le.region, *v)); } -fn walk_pattern(_visitor: &mut V, _pat: &Pattern) { - todo!() +#[inline(always)] +fn walk_record_fields<'a, V: Visitor>( + visitor: &mut V, + fields: impl Iterator, +) { + fields.for_each( + |( + _name, + Field { + var, + loc_expr, + region: _, + }, + )| { visitor.visit_expr(&loc_expr.value, loc_expr.region, *var) }, + ) } -trait Visitor: Sized { +trait Visitor: Sized + PatternVisitor { fn visit_decls(&mut self, decls: &[Declaration]) { walk_decls(self, decls); } @@ -153,12 +283,8 @@ trait Visitor: Sized { walk_def(self, def); } - fn visit_pattern(&mut self, pat: &Pattern, _region: Region, _opt_var: Option) { - walk_pattern(self, pat) - } - fn visit_annotation(&mut self, _pat: &Annotation) { - // TODO + // ignore by default } fn visit_expr(&mut self, expr: &Expr, _region: Region, _var: Variable) { @@ -166,23 +292,22 @@ trait Visitor: Sized { } } +fn walk_pattern(_visitor: &mut V, _pattern: &Pattern) { + // ignore for now +} + +trait PatternVisitor: Sized { + fn visit_pattern(&mut self, pattern: &Pattern, _region: Region, _opt_var: Option) { + walk_pattern(self, pattern); + } +} + struct TypeAtVisitor { region: Region, typ: Option, } -impl Visitor for TypeAtVisitor { - fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) { - if region == self.region { - debug_assert!(self.typ.is_none()); - self.typ = Some(var); - return; - } - if region.contains(&self.region) { - walk_expr(self, expr); - } - } - +impl PatternVisitor for TypeAtVisitor { fn visit_pattern(&mut self, pat: &Pattern, region: Region, opt_var: Option) { if region == self.region { debug_assert!(self.typ.is_none()); @@ -194,6 +319,18 @@ impl Visitor for TypeAtVisitor { } } } +impl Visitor for TypeAtVisitor { + fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) { + if region == self.region { + debug_assert!(self.typ.is_none()); + self.typ = Some(var); + return; + } + if region.contains(&self.region) { + walk_expr(self, expr); + } + } +} /// Attempts to find the type of an expression at `region`, if it exists. pub fn find_type_at(region: Region, decls: &[Declaration]) -> Option { From ebbcd57022f41d6e66d0e75a0b28aafde6ba4a3a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 5 May 2022 13:51:44 -0400 Subject: [PATCH 13/44] Make visitors public --- compiler/can/src/traverse.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/compiler/can/src/traverse.rs b/compiler/can/src/traverse.rs index 8ad473e0be..de93ad16ef 100644 --- a/compiler/can/src/traverse.rs +++ b/compiler/can/src/traverse.rs @@ -18,11 +18,11 @@ macro_rules! visit_list { }; } -fn walk_decls(visitor: &mut V, decls: &[Declaration]) { +pub fn walk_decls(visitor: &mut V, decls: &[Declaration]) { visit_list!(visitor, visit_decl, decls) } -fn walk_decl(visitor: &mut V, decl: &Declaration) { +pub fn walk_decl(visitor: &mut V, decl: &Declaration) { match decl { Declaration::Declare(def) => { visitor.visit_def(def); @@ -37,7 +37,7 @@ fn walk_decl(visitor: &mut V, decl: &Declaration) { } } -fn walk_def(visitor: &mut V, def: &Def) { +pub fn walk_def(visitor: &mut V, def: &Def) { let Def { loc_pattern, loc_expr, @@ -58,7 +58,7 @@ fn walk_def(visitor: &mut V, def: &Def) { } } -fn walk_expr(visitor: &mut V, expr: &Expr) { +pub fn walk_expr(visitor: &mut V, expr: &Expr) { match expr { Expr::Closure(closure_data) => walk_closure(visitor, closure_data), Expr::When { @@ -84,6 +84,7 @@ fn walk_expr(visitor: &mut V, expr: &Expr) { walk_list(visitor, *elem_var, loc_elems); } Expr::Var(..) => { /* terminal */ } + Expr::AbilityMember(..) => { /* terminal */ } Expr::If { cond_var, branches, @@ -171,7 +172,7 @@ fn walk_expr(visitor: &mut V, expr: &Expr) { } #[inline(always)] -fn walk_closure(visitor: &mut V, clos: &ClosureData) { +pub fn walk_closure(visitor: &mut V, clos: &ClosureData) { let ClosureData { arguments, loc_body, @@ -187,7 +188,7 @@ fn walk_closure(visitor: &mut V, clos: &ClosureData) { } #[inline(always)] -fn walk_when( +pub fn walk_when( visitor: &mut V, cond_var: Variable, expr_var: Variable, @@ -202,7 +203,7 @@ fn walk_when( } #[inline(always)] -fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) { +pub fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) { let WhenBranch { patterns, value, @@ -220,14 +221,14 @@ fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: } #[inline(always)] -fn walk_list(visitor: &mut V, elem_var: Variable, loc_elems: &[Loc]) { +pub fn walk_list(visitor: &mut V, elem_var: Variable, loc_elems: &[Loc]) { loc_elems .iter() .for_each(|le| visitor.visit_expr(&le.value, le.region, elem_var)); } #[inline(always)] -fn walk_if( +pub fn walk_if( visitor: &mut V, cond_var: Variable, branches: &[(Loc, Loc)], @@ -242,7 +243,7 @@ fn walk_if( } #[inline(always)] -fn walk_call( +pub fn walk_call( visitor: &mut V, fn_var: Variable, fn_expr: &Loc, @@ -254,7 +255,7 @@ fn walk_call( } #[inline(always)] -fn walk_record_fields<'a, V: Visitor>( +pub fn walk_record_fields<'a, V: Visitor>( visitor: &mut V, fields: impl Iterator, ) { @@ -270,7 +271,7 @@ fn walk_record_fields<'a, V: Visitor>( ) } -trait Visitor: Sized + PatternVisitor { +pub trait Visitor: Sized + PatternVisitor { fn visit_decls(&mut self, decls: &[Declaration]) { walk_decls(self, decls); } @@ -292,11 +293,11 @@ trait Visitor: Sized + PatternVisitor { } } -fn walk_pattern(_visitor: &mut V, _pattern: &Pattern) { +pub fn walk_pattern(_visitor: &mut V, _pattern: &Pattern) { // ignore for now } -trait PatternVisitor: Sized { +pub trait PatternVisitor: Sized { fn visit_pattern(&mut self, pattern: &Pattern, _region: Region, _opt_var: Option) { walk_pattern(self, pattern); } From edee22276363e1e3d883ea1124a9d1615509bcd6 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 5 May 2022 13:52:26 -0400 Subject: [PATCH 14/44] Add AbilityMember can variant for resolving ability specializations --- compiler/can/src/expr.rs | 10 ++++++++++ compiler/can/src/module.rs | 1 + compiler/constrain/src/expr.rs | 5 +++++ compiler/mono/src/ir.rs | 3 ++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 5f4eb61b19..b7f7cb6224 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -21,6 +21,7 @@ use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable}; use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type}; use std::fmt::{Debug, Display}; +use std::sync::{Arc, Mutex}; use std::{char, u32}; #[derive(Clone, Default, Debug)] @@ -83,6 +84,13 @@ pub enum Expr { // Lookups Var(Symbol), + AbilityMember( + /// Actual member name + Symbol, + /// Specialization to use + Arc>>, + ), + // Branching When { /// The actual condition of the when expression. @@ -213,6 +221,7 @@ impl Expr { Self::SingleQuote(..) => Category::Character, Self::List { .. } => Category::List, &Self::Var(sym) => Category::Lookup(sym), + &Self::AbilityMember(sym, _) => Category::Lookup(sym), Self::When { .. } => Category::When, Self::If { .. } => Category::If, Self::LetRec(_, expr) => expr.value.category(), @@ -1368,6 +1377,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> | other @ Accessor { .. } | other @ Update { .. } | other @ Var(_) + | other @ AbilityMember(..) | other @ RunLowLevel { .. } | other @ ForeignCall { .. } => other, diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 5953bd978a..2d0fda6c9b 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -704,6 +704,7 @@ fn fix_values_captured_in_closure_expr( | Str(_) | SingleQuote(_) | Var(_) + | AbilityMember(..) | EmptyRecord | RuntimeError(_) | ZeroArgumentTag { .. } diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 0332f15e13..4df57a9e36 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -336,6 +336,11 @@ pub fn constrain_expr( // make lookup constraint to lookup this symbol's type in the environment constraints.lookup(*symbol, expected, region) } + AbilityMember(symbol, _specialization) => { + // make lookup constraint to lookup this symbol's type in the environment + constraints.lookup(*symbol, expected, region) + // TODO: consider trying to solve `_specialization` here. + } Closure(ClosureData { function_type: fn_var, closure_type: closure_var, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index b82d9b2297..5fb2a132c0 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -15,7 +15,7 @@ use roc_debug_flags::{ dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, }; -use roc_error_macros::internal_error; +use roc_error_macros::{internal_error, todo_abilities}; use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; @@ -3564,6 +3564,7 @@ pub fn with_hole<'a>( specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol) } + AbilityMember(..) => todo_abilities!(), Tag { variant_var, name: tag_name, From 025d501cfd84148ef2e6879babb04cbd6c7a9e43 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 08:51:31 -0400 Subject: [PATCH 15/44] Determine ability specializations before walking a body --- compiler/can/src/expr.rs | 16 +++- compiler/mono/src/copy.rs | 1 + compiler/mono/src/ir.rs | 140 ++++++++++++++++++++++------------ compiler/solve/src/ability.rs | 30 ++++++++ 4 files changed, 136 insertions(+), 51 deletions(-) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index b7f7cb6224..73017c42b0 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -21,7 +21,7 @@ use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable}; use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type}; use std::fmt::{Debug, Display}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; use std::{char, u32}; #[derive(Clone, Default, Debug)] @@ -88,7 +88,7 @@ pub enum Expr { /// Actual member name Symbol, /// Specialization to use - Arc>>, + Arc>>, ), // Branching @@ -1328,7 +1328,11 @@ fn canonicalize_var_lookup( Ok(symbol) => { output.references.insert_value_lookup(symbol); - Var(symbol) + if scope.abilities_store.is_ability_member_name(symbol) { + AbilityMember(symbol, Arc::new(RwLock::new(None))) + } else { + Var(symbol) + } } Err(problem) => { env.problem(Problem::RuntimeError(problem.clone())); @@ -1343,7 +1347,11 @@ fn canonicalize_var_lookup( Ok(symbol) => { output.references.insert_value_lookup(symbol); - Var(symbol) + if scope.abilities_store.is_ability_member_name(symbol) { + AbilityMember(symbol, Arc::new(RwLock::new(None))) + } else { + Var(symbol) + } } Err(problem) => { // Either the module wasn't imported, or diff --git a/compiler/mono/src/copy.rs b/compiler/mono/src/copy.rs index 0895d0ca7f..d45a1144e5 100644 --- a/compiler/mono/src/copy.rs +++ b/compiler/mono/src/copy.rs @@ -67,6 +67,7 @@ pub fn deep_copy_type_vars_into_expr<'a>( loc_elems: loc_elems.iter().map(|le| le.map(go_help)).collect(), }, Var(sym) => Var(*sym), + AbilityMember(sym, specialization) => AbilityMember(*sym, specialization.clone()), When { loc_cond, cond_var, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 5fb2a132c0..9e52e74eae 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -22,6 +22,7 @@ use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_problem::can::{RuntimeError, ShadowKind}; use roc_region::all::{Loc, Region}; +use roc_solve::ability::resolve_ability_specialization; use roc_std::RocDec; use roc_target::TargetInfo; use roc_types::subs::{ @@ -2479,6 +2480,78 @@ fn generate_runtime_error_function<'a>( } } +fn resolve_abilities_in_specialized_body<'a>( + env: &mut Env<'a, '_>, + procs: &Procs<'a>, + specialized_body: &roc_can::expr::Expr, + body_var: Variable, +) { + use roc_can::expr::Expr; + use roc_can::traverse::{walk_expr, PatternVisitor, Visitor}; + use roc_unify::unify::unify; + + struct Specializer<'a> { + subs: &'a mut Subs, + procs: &'a Procs<'a>, + abilities_store: &'a AbilitiesStore, + } + impl PatternVisitor for Specializer<'_> {} + impl Visitor for Specializer<'_> { + fn visit_expr(&mut self, expr: &Expr, _region: Region, var: Variable) { + match expr { + Expr::Closure(..) => { + // Don't walk down closure bodies. They will have their types refined when they + // are themselves specialized, so we'll handle ability resolution in them at + // that time too. + } + Expr::AbilityMember(member_sym, specialization_cell) => { + let mut specialization_cell = specialization_cell + .write() + .expect("Can't lock specialization cell"); + if specialization_cell.is_some() { + // We already know the specialization; we are good to go. + return; + } + + let specialization = resolve_ability_specialization( + self.subs, + self.abilities_store, + *member_sym, + var, + ) + .expect("Ability specialization is unknown - code generation cannot proceed!"); + + // We must now refine the current type state to account for this specialization, + // since `var` may only have partial specialization information - enough to + // figure out what specialization we need, but not the types of all arguments + // and return types. So, unify with the variable with the specialization's type. + let specialization_def = self + .procs + .partial_procs + .get_symbol(specialization) + .expect("Specialization found, but it's not in procs"); + let specialization_var = specialization_def.annotation; + + let unified = unify(self.subs, var, specialization_var, Mode::EQ); + unified.expect_success("Specialization does not unify"); + + *specialization_cell = Some(specialization); + } + _ => walk_expr(self, expr), + } + // TODO: I think we actually want bottom-up visiting, or bidirectional visiting. This + // is pre-order (top-down). + } + } + + let mut specializer = Specializer { + subs: env.subs, + procs, + abilities_store: env.abilities_store, + }; + specializer.visit_expr(specialized_body, Region::zero(), body_var); +} + fn specialize_external<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, @@ -2614,6 +2687,7 @@ fn specialize_external<'a>( }; let body = partial_proc.body.clone(); + resolve_abilities_in_specialized_body(env, procs, &body, partial_proc.body_var); let mut specialized_body = from_can(env, partial_proc.body_var, body, procs, layout_cache); match specialized { @@ -4407,14 +4481,10 @@ pub fn with_hole<'a>( // a proc in this module, or an imported symbol procs.partial_procs.contains_key(key) || (env.is_imported_symbol(key) && !procs.is_imported_module_thunk(key)) - || env.abilities_store.is_ability_member_name(key) }; match loc_expr.value { roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => { - // This might be an ability member - if so, use the appropriate specialization. - let proc_name = get_specialization(env, fn_var, proc_name).unwrap_or(proc_name); - // a call by a known name call_by_name( env, @@ -4427,6 +4497,23 @@ pub fn with_hole<'a>( hole, ) } + roc_can::expr::Expr::AbilityMember(_, specialization) => { + let specialization_cell = specialization.read().unwrap(); + let proc_name = specialization_cell.expect( + "Ability specialization is unknown - code generation cannot proceed!", + ); + + call_by_name( + env, + procs, + fn_var, + proc_name, + loc_args, + layout_cache, + assigned, + hole, + ) + } _ => { // Call by pointer - the closure was anonymous, e.g. // @@ -4544,8 +4631,8 @@ pub fn with_hole<'a>( } UnspecializedExpr(symbol) => { match procs.ability_member_aliases.get(symbol).unwrap() { - &AbilityMember(member) => { - let proc_name = get_specialization(env, fn_var, member).expect("Recorded as an ability member, but it doesn't have a specialization"); + &self::AbilityMember(member) => { + let proc_name = resolve_ability_specialization(env.subs, env.abilities_store, member, fn_var).expect("Recorded as an ability member, but it doesn't have a specialization"); // a call by a known name return call_by_name( @@ -4905,47 +4992,6 @@ pub fn with_hole<'a>( } } -#[inline(always)] -fn get_specialization<'a>( - env: &mut Env<'a, '_>, - symbol_var: Variable, - symbol: Symbol, -) -> Option { - use roc_solve::ability::type_implementing_member; - use roc_solve::solve::instantiate_rigids; - use roc_unify::unify::unify; - - match env.abilities_store.member_def(symbol) { - None => { - // This is not an ability member, it doesn't need specialization. - None - } - Some(member) => { - let snapshot = env.subs.snapshot(); - instantiate_rigids(env.subs, member.signature_var); - - let (_, must_implement_ability) = unify( - env.subs, - symbol_var, - member.signature_var, - roc_unify::unify::Mode::EQ, - ) - .expect_success("This typechecked previously"); - env.subs.rollback_to(snapshot); - - let specializing_type = - type_implementing_member(&must_implement_ability, member.parent_ability); - - let specialization = env - .abilities_store - .get_specialization(symbol, specializing_type) - .expect("No specialization is recorded - I thought there would only be a type error here."); - - Some(specialization.symbol) - } - } -} - #[allow(clippy::too_many_arguments)] fn construct_closure_data<'a, I>( env: &mut Env<'a, '_>, diff --git a/compiler/solve/src/ability.rs b/compiler/solve/src/ability.rs index ee23b5567c..fc51b01d37 100644 --- a/compiler/solve/src/ability.rs +++ b/compiler/solve/src/ability.rs @@ -7,6 +7,7 @@ use roc_types::types::{Category, PatternCategory}; use roc_unify::unify::MustImplementAbility; use roc_unify::unify::MustImplementConstraints; +use crate::solve::instantiate_rigids; use crate::solve::{IncompleteAbilityImplementation, TypeError}; #[derive(Debug, Clone)] @@ -188,3 +189,32 @@ pub fn type_implementing_member( .unwrap() .typ } + +pub fn resolve_ability_specialization( + subs: &mut Subs, + abilities_store: &AbilitiesStore, + ability_member: Symbol, + specialization_var: Variable, +) -> Option { + use roc_unify::unify::{unify, Mode}; + + let member_def = abilities_store + .member_def(ability_member) + .expect("Not an ability member symbol"); + + let snapshot = subs.snapshot(); + instantiate_rigids(subs, member_def.signature_var); + let (_, must_implement_ability) = + unify(subs, specialization_var, member_def.signature_var, Mode::EQ).expect_success( + "If resolving a specialization, the specialization must be known to typecheck.", + ); + + subs.rollback_to(snapshot); + + let specializing_type = + type_implementing_member(&must_implement_ability, member_def.parent_ability); + + let specialization = abilities_store.get_specialization(ability_member, specializing_type)?; + + Some(specialization.symbol) +} From e5eae75ad6316ede97ec33b610be83fef1a7ff3a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 08:52:30 -0400 Subject: [PATCH 16/44] Ignore gen test, for now --- compiler/test_gen/src/gen_abilities.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/test_gen/src/gen_abilities.rs b/compiler/test_gen/src/gen_abilities.rs index 56056d5d43..967ea7f1d5 100644 --- a/compiler/test_gen/src/gen_abilities.rs +++ b/compiler/test_gen/src/gen_abilities.rs @@ -222,6 +222,7 @@ fn ability_used_as_type_still_compiles() { #[test] #[cfg(any(feature = "gen-llvm"))] +#[ignore] fn encode() { assert_evals_to!( indoc!( From 645c0f2738179aeefba4f174bf00a06b389ad818 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 09:10:26 -0400 Subject: [PATCH 17/44] Don't eagerly walk down let bindings to specialize an ability --- compiler/mono/src/ir.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 9e52e74eae..70481de742 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2504,6 +2504,12 @@ fn resolve_abilities_in_specialized_body<'a>( // are themselves specialized, so we'll handle ability resolution in them at // that time too. } + Expr::LetRec(..) | Expr::LetNonRec(..) => { + // Also don't walk down let-bindings. These may be generalized and we won't + // know their specializations until we collect them while building up the def. + // So, we'll resolve any nested abilities when we know their specialized type + // during def construction. + } Expr::AbilityMember(member_sym, specialization_cell) => { let mut specialization_cell = specialization_cell .write() From 53c8ec3a57a2685025b9b60d54b8f83680654b54 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 09:10:45 -0400 Subject: [PATCH 18/44] Correctly aliasing of ability members --- compiler/mono/src/ir.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 70481de742..3bb9958d45 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -5762,7 +5762,8 @@ pub fn from_can<'a>( return from_can(env, variable, cont.value, procs, layout_cache); } - roc_can::expr::Expr::Var(original) => { + roc_can::expr::Expr::Var(original) + | roc_can::expr::Expr::AbilityMember(original, _) => { // a variable is aliased, e.g. // // foo = bar @@ -7046,20 +7047,23 @@ fn handle_variable_aliasing<'a, BuildRest>( where BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>, { - if env.abilities_store.is_ability_member_name(right) { - procs - .ability_member_aliases - .insert(left, AbilityMember(right)); - return build_rest(env, procs, layout_cache); - } - - if let Some(&ability_member) = procs.ability_member_aliases.get(right) { - // If `right` links to a partial expression, make sure we link `left` to it as well, so - // that usages of it will be specialized when building the rest of the program. - procs.ability_member_aliases.insert(left, ability_member); - return build_rest(env, procs, layout_cache); + // 1. Handle references to ability members - we could be aliasing an ability member, or another + // alias to an ability member. + { + if env.abilities_store.is_ability_member_name(right) { + procs + .ability_member_aliases + .insert(left, AbilityMember(right)); + return build_rest(env, procs, layout_cache); + } + if let Some(&ability_member) = procs.ability_member_aliases.get(right) { + procs.ability_member_aliases.insert(left, ability_member); + return build_rest(env, procs, layout_cache); + } } + // 2. Handle references to a known proc - again, we may be either aliasing the proc, or another + // alias to a proc. if procs.partial_procs.contains_key(right) { // This is an alias to a function defined in this module. // Attach the alias, then build the rest of the module, so that we reference and specialize @@ -7096,6 +7100,8 @@ where // then we must construct its closure; since imported symbols have no closure, we use the empty struct let_empty_struct(left, env.arena.alloc(result)) } else { + // Otherwise, we are referencing a non-proc value. + // We need to lift all specializations of "left" to be specializations of "right". let mut scratchpad_update_specializations = std::vec::Vec::new(); From 30ef59a59131646fdf0bcf8674a108c56eeb7e9f Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 13:41:50 -0400 Subject: [PATCH 19/44] Fix test import --- compiler/can/tests/helpers/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/can/tests/helpers/mod.rs b/compiler/can/tests/helpers/mod.rs index aaff51324c..038ae2cef1 100644 --- a/compiler/can/tests/helpers/mod.rs +++ b/compiler/can/tests/helpers/mod.rs @@ -11,7 +11,7 @@ use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol}; use roc_problem::can::Problem; use roc_region::all::{Loc, Region}; use roc_types::subs::{VarStore, Variable}; -use roc_types::types::Type; +use roc_types::types::{AliasVar, Type}; use std::hash::Hash; pub fn test_home() -> ModuleId { @@ -59,7 +59,10 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut scope.add_alias( Symbol::NUM_INT, Region::zero(), - vec![Loc::at_zero(("a".into(), Variable::EMPTY_RECORD))], + vec![Loc::at_zero(AliasVar::unbound( + "a".into(), + Variable::EMPTY_RECORD, + ))], Type::EmptyRec, roc_types::types::AliasKind::Structural, ); From 474c18ab839876a19da074fdbbbdedac7c16645c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 14:55:08 -0400 Subject: [PATCH 20/44] Handle recursion --- compiler/mono/src/ir.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 3bb9958d45..421df7c242 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -10,7 +10,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_can::abilities::AbilitiesStore; use roc_can::expr::{AnnotatedMark, ClosureData, IntValue}; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; -use roc_collections::VecMap; +use roc_collections::{MutSet, VecMap}; use roc_debug_flags::{ dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, @@ -2494,6 +2494,7 @@ fn resolve_abilities_in_specialized_body<'a>( subs: &'a mut Subs, procs: &'a Procs<'a>, abilities_store: &'a AbilitiesStore, + seen_defs: MutSet, } impl PatternVisitor for Specializer<'_> {} impl Visitor for Specializer<'_> { @@ -2539,14 +2540,25 @@ fn resolve_abilities_in_specialized_body<'a>( let specialization_var = specialization_def.annotation; let unified = unify(self.subs, var, specialization_var, Mode::EQ); - unified.expect_success("Specialization does not unify"); + unified.expect_success( + "Specialization does not unify - this is a typechecker bug!", + ); + + // Now walk the specialization def to pick up any more needed types. Of course, + // we only want to pass through it once to avoid unbounded recursion. + if !self.seen_defs.contains(&specialization) { + self.visit_expr( + &specialization_def.body, + Region::zero(), + specialization_def.body_var, + ); + self.seen_defs.insert(specialization); + } *specialization_cell = Some(specialization); } _ => walk_expr(self, expr), } - // TODO: I think we actually want bottom-up visiting, or bidirectional visiting. This - // is pre-order (top-down). } } @@ -2554,6 +2566,7 @@ fn resolve_abilities_in_specialized_body<'a>( subs: env.subs, procs, abilities_store: env.abilities_store, + seen_defs: MutSet::default(), }; specializer.visit_expr(specialized_body, Region::zero(), body_var); } From bc0b82b97c4a595fcfdd0b1bf836a5038f4f1153 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 14:55:20 -0400 Subject: [PATCH 21/44] Expand encode gen test --- compiler/test_gen/src/gen_abilities.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/test_gen/src/gen_abilities.rs b/compiler/test_gen/src/gen_abilities.rs index 967ea7f1d5..309d9ab657 100644 --- a/compiler/test_gen/src/gen_abilities.rs +++ b/compiler/test_gen/src/gen_abilities.rs @@ -222,7 +222,6 @@ fn ability_used_as_type_still_compiles() { #[test] #[cfg(any(feature = "gen-llvm"))] -#[ignore] fn encode() { assert_evals_to!( indoc!( @@ -249,15 +248,20 @@ fn encode() { # impl Format for Linear u8 = \n -> @Encoder (\lst, @Linear {} -> List.append lst n) - MyU8 := U8 + Rgba := { r : U8, g : U8, b : U8, a : U8 } - # impl Encoding for MyU8 - toEncoder = \@MyU8 n -> u8 n + # impl Encoding for Rgba + toEncoder = \@Rgba {r, g, b, a} -> + @Encoder \lst, fmt -> lst + |> appendWith (u8 r) fmt + |> appendWith (u8 g) fmt + |> appendWith (u8 b) fmt + |> appendWith (u8 a) fmt - myU8Bytes = toBytes (@MyU8 15) (@Linear {}) + myU8Bytes = toBytes (@Rgba { r: 106, g: 90, b: 205, a: 255 }) (@Linear {}) "# ), - RocList::from_slice(&[15]), + RocList::from_slice(&[106, 90, 205, 255]), RocList ) } From 638c446c2f2034ab957bc12258406a9f8215e544 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 14:58:08 -0400 Subject: [PATCH 22/44] Fix constraints --- compiler/constrain/src/module.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index 5b9b9c78a4..14e30f5085 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -16,8 +16,6 @@ use roc_types::solved_types::{FreeVars, SolvedType}; use roc_types::subs::{VarStore, Variable}; use roc_types::types::{AnnotationSource, Category, Type}; -use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env}; - /// The types of all exposed values/functions of a collection of modules #[derive(Clone, Debug, Default)] pub struct ExposedByModule { @@ -109,7 +107,7 @@ pub fn constrain_module( let constraint = crate::expr::constrain_decls(constraints, home, declarations); let constraint = constrain_symbols_from_requires(constraints, symbols_from_requires, home, constraint); - let constraint = frontload_ability_constraints(constraints, abilities_store, constraint); + let constraint = frontload_ability_constraints(constraints, abilities_store, home, constraint); // The module constraint should always save the environment at the end. debug_assert!(constraints.contains_save_the_environment(&constraint)); @@ -174,8 +172,8 @@ fn constrain_symbols_from_requires( pub fn frontload_ability_constraints( constraints: &mut Constraints, abilities_store: &AbilitiesStore, - mut constraint: Constraint, home: ModuleId, + mut constraint: Constraint, ) -> Constraint { for (member_name, member_data) in abilities_store.root_ability_members().iter() { let rigids = Default::default(); From f208f7b4a1a375effe2fc596502c5d7a0c70231d Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 15:00:10 -0400 Subject: [PATCH 23/44] Ability members are functions too --- compiler/constrain/src/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 4df57a9e36..304d14576d 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -258,7 +258,7 @@ pub fn constrain_expr( let (fn_var, loc_fn, closure_var, ret_var) = &**boxed; // The expression that evaluates to the function being called, e.g. `foo` in // (foo) bar baz - let opt_symbol = if let Var(symbol) = loc_fn.value { + let opt_symbol = if let Var(symbol) | AbilityMember(symbol, _) = loc_fn.value { Some(symbol) } else { None From 85b3110beab3f20172385d7855714d5d59c7512a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 16:09:25 -0400 Subject: [PATCH 24/44] Typecheck decoder --- compiler/solve/tests/solve_expr.rs | 75 +++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 97016b1b07..92e151c871 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6242,6 +6242,24 @@ mod solve_expr { ); } + #[test] + fn alias_in_opaque() { + infer_eq_without_problem( + indoc!( + r#" + app "test" provides [ foo ] to "./platform" + + MyError : [ Error ] + + MyResult := Result U8 MyError + + foo = @MyResult (Err Error) + "# + ), + "MyResult", + ) + } + #[test] fn encoder() { infer_queries( @@ -6289,20 +6307,63 @@ mod solve_expr { } #[test] - fn alias_in_opaque() { - infer_eq_without_problem( + fn decoder() { + infer_queries( indoc!( r#" - app "test" provides [ foo ] to "./platform" + app "test" provides [ myU8 ] to "./platform" - MyError : [ Error ] + DecodeError : [ TooShort, Leftover (List U8) ] - MyResult := Result U8 MyError + Decoder val fmt := List U8, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting - foo = @MyResult (Err Error) + Decoding has + decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting + + DecoderFormatting has + u8 : Decoder U8 fmt | fmt has DecoderFormatting + + decodeWith : List U8, Decoder val fmt, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting + decodeWith = \lst, (@Decoder doDecode), fmt -> doDecode lst fmt + + fromBytes : List U8, fmt -> Result val DecodeError + | fmt has DecoderFormatting, val has Decoding + fromBytes = \lst, fmt -> + when decodeWith lst decoder fmt is + { result, rest } -> + when result is + Ok val -> if List.isEmpty rest then val else Err (Leftover rest) + Err e -> Err e + + + Linear := {} + + # impl DecoderFormatting for Linear + u8 = @Decoder \lst, @Linear {} -> + #^^{-1} + when List.first lst is + Ok n -> { result: Ok n, rest: List.dropFirst lst } + Err _ -> { result: Err TooShort, rest: [] } + + MyU8 := U8 + + # impl Decoding for MyU8 + decoder = @Decoder \lst, fmt -> + #^^^^^^^{-1} + when decodeWith lst u8 fmt is + { result, rest } -> + { result: Result.map result (\n -> @MyU8 n), rest } + + myU8 : Result MyU8 _ + myU8 = fromBytes [ 15 ] (@Linear {}) + #^^^^{-1} "# ), - "MyResult", + &[ + "u8 : Decoder U8 Linear", + "decoder : Decoder MyU8 fmt | fmt has DecoderFormatting", + "myU8 : Result MyU8 DecodeError", + ], ) } } From 5a5fc0162d97ceda6fb5787eadae95f7a57a3bdc Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 17:40:04 -0400 Subject: [PATCH 25/44] FlexAble must always constrain a Flex --- compiler/unify/src/unify.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 9a5ca9a28a..db4cf0b648 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1813,11 +1813,12 @@ fn unify_flex( other: &Content, ) -> Outcome { match other { - FlexVar(None) => { - // If both are flex, and only left has a name, keep the name around. + FlexVar(other_opt_name) => { + // Prefer using right's name. + let opt_name = opt_name.or(*other_opt_name); match opt_able_bound { - Some(ability) => merge(subs, ctx, FlexAbleVar(*opt_name, ability)), - None => merge(subs, ctx, FlexVar(*opt_name)), + Some(ability) => merge(subs, ctx, FlexAbleVar(opt_name, ability)), + None => merge(subs, ctx, FlexVar(opt_name)), } } @@ -1849,8 +1850,7 @@ fn unify_flex( } } - FlexVar(Some(_)) - | RigidVar(_) + RigidVar(_) | RigidAbleVar(_, _) | RecursionVar { .. } | Structure(_) @@ -1858,7 +1858,6 @@ fn unify_flex( | RangedNumber(..) => { // TODO special-case boolean here // In all other cases, if left is flex, defer to right. - // (This includes using right's name if both are flex and named.) merge(subs, ctx, *other) } From 8bff2875b013270081dd175e80312cae4fc36aa6 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 17:41:03 -0400 Subject: [PATCH 26/44] Account for ability specialization in reuse_symbol --- compiler/mono/src/ir.rs | 55 +++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 421df7c242..60afa97360 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -7000,34 +7000,41 @@ fn can_reuse_symbol<'a>( procs: &Procs<'a>, expr: &roc_can::expr::Expr, ) -> ReuseSymbol { + use roc_can::expr::Expr::*; use ReuseSymbol::*; - if let roc_can::expr::Expr::Var(symbol) = expr { - let symbol = *symbol; - - let arguments = [ - Symbol::ARG_1, - Symbol::ARG_2, - Symbol::ARG_3, - Symbol::ARG_4, - Symbol::ARG_5, - Symbol::ARG_6, - Symbol::ARG_7, - ]; - - if arguments.contains(&symbol) { - Value(symbol) - } else if env.is_imported_symbol(symbol) { - Imported(symbol) - } else if procs.partial_procs.contains_key(symbol) { - LocalFunction(symbol) - } else if procs.ability_member_aliases.get(symbol).is_some() { - UnspecializedExpr(symbol) - } else { - Value(symbol) + let symbol = match expr { + AbilityMember(_, specialization) => { + let specialization_symbol = specialization + .read() + .unwrap() + .expect("Specialization must be known!"); + specialization_symbol } + Var(symbol) => *symbol, + _ => return NotASymbol, + }; + + let arguments = [ + Symbol::ARG_1, + Symbol::ARG_2, + Symbol::ARG_3, + Symbol::ARG_4, + Symbol::ARG_5, + Symbol::ARG_6, + Symbol::ARG_7, + ]; + + if arguments.contains(&symbol) { + Value(symbol) + } else if env.is_imported_symbol(symbol) { + Imported(symbol) + } else if procs.partial_procs.contains_key(symbol) { + LocalFunction(symbol) + } else if procs.ability_member_aliases.get(symbol).is_some() { + UnspecializedExpr(symbol) } else { - NotASymbol + Value(symbol) } } From 1a53daa66506d17b56df9293f3e945e3ca3f21e0 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 17:41:36 -0400 Subject: [PATCH 27/44] Remove ability todo in mono --- compiler/mono/src/ir.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 60afa97360..5bdeb6c0c8 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -15,7 +15,7 @@ use roc_debug_flags::{ dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, }; -use roc_error_macros::{internal_error, todo_abilities}; +use roc_error_macros::internal_error; use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; @@ -3657,7 +3657,22 @@ pub fn with_hole<'a>( specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol) } - AbilityMember(..) => todo_abilities!(), + AbilityMember(_member, specialization) => { + let specialization_symbol = specialization + .read() + .unwrap() + .expect("Specialization was never made!"); + + specialize_naked_symbol( + env, + variable, + procs, + layout_cache, + assigned, + hole, + specialization_symbol, + ) + } Tag { variant_var, name: tag_name, From 13884c4ce6e096c8c181f7f42ca522e40202f1d0 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 17:41:55 -0400 Subject: [PATCH 28/44] Fix solve test --- compiler/solve/tests/solve_expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 92e151c871..f05fe04163 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6332,7 +6332,7 @@ mod solve_expr { when decodeWith lst decoder fmt is { result, rest } -> when result is - Ok val -> if List.isEmpty rest then val else Err (Leftover rest) + Ok val -> if List.isEmpty rest then Ok val else Err (Leftover rest) Err e -> Err e From 08710c815370186be0283cd1a06709c305be764c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Fri, 6 May 2022 17:41:59 -0400 Subject: [PATCH 29/44] Decoders now code-generate --- compiler/test_gen/src/gen_abilities.rs | 58 ++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/compiler/test_gen/src/gen_abilities.rs b/compiler/test_gen/src/gen_abilities.rs index 309d9ab657..5974ddcf04 100644 --- a/compiler/test_gen/src/gen_abilities.rs +++ b/compiler/test_gen/src/gen_abilities.rs @@ -265,3 +265,61 @@ fn encode() { RocList ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn decode() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [ myU8 ] to "./platform" + + DecodeError : [ TooShort, Leftover (List U8) ] + + Decoder val fmt := List U8, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting + + Decoding has + decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting + + DecoderFormatting has + u8 : Decoder U8 fmt | fmt has DecoderFormatting + + decodeWith : List U8, Decoder val fmt, fmt -> { result: Result val DecodeError, rest: List U8 } | fmt has DecoderFormatting + decodeWith = \lst, (@Decoder doDecode), fmt -> doDecode lst fmt + + fromBytes : List U8, fmt -> Result val DecodeError + | fmt has DecoderFormatting, val has Decoding + fromBytes = \lst, fmt -> + when decodeWith lst decoder fmt is + { result, rest } -> + when result is + Ok val -> if List.isEmpty rest then Ok val else Err (Leftover rest) + Err e -> Err e + + + Linear := {} + + # impl DecoderFormatting for Linear + u8 = @Decoder \lst, @Linear {} -> + when List.first lst is + Ok n -> { result: Ok n, rest: List.dropFirst lst } + Err _ -> { result: Err TooShort, rest: [] } + + MyU8 := U8 + + # impl Decoding for MyU8 + decoder = @Decoder \lst, fmt -> + when decodeWith lst u8 fmt is + { result, rest } -> + { result: Result.map result (\n -> @MyU8 n), rest } + + myU8 = + when fromBytes [ 15 ] (@Linear {}) is + Ok (@MyU8 n) -> n + _ -> 27u8 + "# + ), + 15, + u8 + ); +} From e08b5fa91e3638b91cca1e618dba3f3494418ae4 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 13:40:00 -0400 Subject: [PATCH 30/44] Specializer->Resolver --- compiler/mono/src/ir.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 5bdeb6c0c8..49d080b7b4 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2490,14 +2490,14 @@ fn resolve_abilities_in_specialized_body<'a>( use roc_can::traverse::{walk_expr, PatternVisitor, Visitor}; use roc_unify::unify::unify; - struct Specializer<'a> { + struct Resolver<'a> { subs: &'a mut Subs, procs: &'a Procs<'a>, abilities_store: &'a AbilitiesStore, seen_defs: MutSet, } - impl PatternVisitor for Specializer<'_> {} - impl Visitor for Specializer<'_> { + impl PatternVisitor for Resolver<'_> {} + impl Visitor for Resolver<'_> { fn visit_expr(&mut self, expr: &Expr, _region: Region, var: Variable) { match expr { Expr::Closure(..) => { @@ -2562,7 +2562,7 @@ fn resolve_abilities_in_specialized_body<'a>( } } - let mut specializer = Specializer { + let mut specializer = Resolver { subs: env.subs, procs, abilities_store: env.abilities_store, From 4a623a48dc471e3b7c79190f41606d4ce4acacff Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 14:03:19 -0400 Subject: [PATCH 31/44] Simpl decoder a bit --- compiler/mono/src/ir.rs | 21 +++++++++++++++++++++ compiler/test_gen/src/gen_abilities.rs | 12 ++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 49d080b7b4..2d4bfc607c 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -5928,6 +5928,13 @@ pub fn from_can<'a>( let _res = roc_unify::unify::unify(env.subs, var, def.expr_var, Mode::EQ); + resolve_abilities_in_specialized_body( + env, + procs, + &def.loc_expr.value, + def.expr_var, + ); + return with_hole( env, def.loc_expr.value, @@ -5959,6 +5966,13 @@ pub fn from_can<'a>( Mode::EQ, ); + resolve_abilities_in_specialized_body( + env, + procs, + &def.loc_expr.value, + def.expr_var, + ); + stmt = with_hole( env, specialized_expr, @@ -6017,6 +6031,13 @@ pub fn from_can<'a>( let outer_symbol = env.unique_symbol(); stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt); + resolve_abilities_in_specialized_body( + env, + procs, + &def.loc_expr.value, + def.expr_var, + ); + // convert the def body, store in outer_symbol with_hole( env, diff --git a/compiler/test_gen/src/gen_abilities.rs b/compiler/test_gen/src/gen_abilities.rs index 5974ddcf04..bd2fa3e852 100644 --- a/compiler/test_gen/src/gen_abilities.rs +++ b/compiler/test_gen/src/gen_abilities.rs @@ -292,9 +292,10 @@ fn decode() { fromBytes = \lst, fmt -> when decodeWith lst decoder fmt is { result, rest } -> - when result is - Ok val -> if List.isEmpty rest then Ok val else Err (Leftover rest) - Err e -> Err e + Result.after result \val -> + if List.isEmpty rest + then Ok val + else Err (Leftover rest) Linear := {} @@ -309,9 +310,8 @@ fn decode() { # impl Decoding for MyU8 decoder = @Decoder \lst, fmt -> - when decodeWith lst u8 fmt is - { result, rest } -> - { result: Result.map result (\n -> @MyU8 n), rest } + { result, rest } = decodeWith lst u8 fmt + { result: Result.map result (\n -> @MyU8 n), rest } myU8 = when fromBytes [ 15 ] (@Linear {}) is From 5cc6727e9f46c1ead8b84116a613b57096f0206f Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 16:15:11 -0400 Subject: [PATCH 32/44] Simplify introduced variables in ability member --- compiler/can/src/abilities.rs | 18 ++++-------------- compiler/can/src/def.rs | 1 - compiler/constrain/src/expr.rs | 2 +- compiler/constrain/src/module.rs | 27 ++++++++++----------------- 4 files changed, 15 insertions(+), 33 deletions(-) diff --git a/compiler/can/src/abilities.rs b/compiler/can/src/abilities.rs index 9b19d71de2..350c6f00af 100644 --- a/compiler/can/src/abilities.rs +++ b/compiler/can/src/abilities.rs @@ -1,4 +1,3 @@ -use crate::annotation::IntroducedVariables; use roc_collections::all::MutMap; use roc_module::symbol::Symbol; use roc_region::all::Region; @@ -7,6 +6,8 @@ use roc_types::{subs::Variable, types::Type}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct MemberVariables { pub able_vars: Vec, + /// This includes - named rigid vars, lambda sets, wildcards. See + /// [`crate::annotation::IntroducedVariables::collect_rigid`]. pub rigid_vars: Vec, pub flex_vars: Vec, } @@ -20,7 +21,6 @@ pub struct AbilityMemberData { pub signature_var: Variable, pub signature: Type, pub variables: MemberVariables, - pub introduced_variables: IntroducedVariables, pub region: Region, } @@ -63,19 +63,10 @@ impl AbilitiesStore { pub fn register_ability( &mut self, ability: Symbol, - members: Vec<( - Symbol, - Region, - Variable, - Type, - MemberVariables, - IntroducedVariables, - )>, + members: Vec<(Symbol, Region, Variable, Type, MemberVariables)>, ) { let mut members_vec = Vec::with_capacity(members.len()); - for (member, region, signature_var, signature, variables, introduced_variables) in - members.into_iter() - { + for (member, region, signature_var, signature, variables) in members.into_iter() { members_vec.push(member); let old_member = self.ability_members.insert( member, @@ -85,7 +76,6 @@ impl AbilitiesStore { signature, region, variables, - introduced_variables, }, ); debug_assert!(old_member.is_none(), "Replacing existing member definition"); diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index fee660fa83..ed4c1dc2a0 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -646,7 +646,6 @@ fn resolve_abilities<'a>( var_store.fresh(), member_annot.typ, variables, - iv, )); } diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 304d14576d..525173740b 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1782,7 +1782,7 @@ pub struct InstantiateRigids { pub new_infer_variables: Vec, } -pub fn instantiate_rigids( +fn instantiate_rigids( annotation: &Type, introduced_vars: &IntroducedVariables, loc_pattern: &Loc, diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index 14e30f5085..20c0f376ac 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -1,7 +1,4 @@ -use crate::expr::{ - constrain_def_make_constraint, constrain_def_pattern, instantiate_rigids, Env, - InstantiateRigids, -}; +use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env}; use roc_builtins::std::StdLib; use roc_can::abilities::AbilitiesStore; use roc_can::constraint::{Constraint, Constraints}; @@ -189,25 +186,21 @@ pub fn frontload_ability_constraints( def_pattern_state.vars.push(member_data.signature_var); - let mut ftv = env.rigids; + let vars = &member_data.variables; + let new_rigid_variables = vars + .rigid_vars + .iter() + .chain(vars.able_vars.iter()) + .copied() + .collect(); - let InstantiateRigids { - signature, - new_rigid_variables, - new_infer_variables, - } = instantiate_rigids( - &member_data.signature, - &member_data.introduced_variables, - &pattern, - &mut ftv, - &mut def_pattern_state.headers, - ); + let new_infer_variables = vars.flex_vars.clone(); def_pattern_state .constraints .push(constraints.equal_types_var( member_data.signature_var, - Expected::NoExpectation(signature.clone()), + Expected::NoExpectation(member_data.signature.clone()), Category::Storage(file!(), line!()), Region::zero(), )); From a47dc711f3d427d685ea4ff6b59112afa20a14ea Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 16:17:48 -0400 Subject: [PATCH 33/44] Remove a couple vec allocs --- compiler/constrain/src/expr.rs | 16 ++++++++-------- compiler/constrain/src/module.rs | 18 ++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 525173740b..d6dd14f57e 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1482,8 +1482,8 @@ fn constrain_typed_def( constrain_def_make_constraint( constraints, - new_rigid_variables, - new_infer_variables, + new_rigid_variables.into_iter(), + new_infer_variables.into_iter(), expr_con, body_con, def_pattern_state, @@ -1517,8 +1517,8 @@ fn constrain_typed_def( constrain_def_make_constraint( constraints, - new_rigid_variables, - new_infer_variables, + new_rigid_variables.into_iter(), + new_infer_variables.into_iter(), expr_con, body_con, def_pattern_state, @@ -1683,8 +1683,8 @@ fn constrain_def( constrain_def_make_constraint( constraints, - vec![], - vec![], + std::iter::empty(), + std::iter::empty(), expr_con, body_con, def_pattern_state, @@ -1695,8 +1695,8 @@ fn constrain_def( pub fn constrain_def_make_constraint( constraints: &mut Constraints, - new_rigid_variables: Vec, - new_infer_variables: Vec, + new_rigid_variables: impl Iterator, + new_infer_variables: impl Iterator, expr_con: Constraint, body_con: Constraint, def_pattern_state: PatternState, diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index 20c0f376ac..ec913f44cd 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -139,8 +139,8 @@ fn constrain_symbols_from_requires( constraints, // No new rigids or flex vars because they are represented in the type // annotation. - vec![], - vec![], + std::iter::empty(), + std::iter::empty(), Constraint::True, constraint, def_pattern_state, @@ -187,14 +187,8 @@ pub fn frontload_ability_constraints( def_pattern_state.vars.push(member_data.signature_var); let vars = &member_data.variables; - let new_rigid_variables = vars - .rigid_vars - .iter() - .chain(vars.able_vars.iter()) - .copied() - .collect(); - - let new_infer_variables = vars.flex_vars.clone(); + let rigid_variables = vars.rigid_vars.iter().chain(vars.able_vars.iter()).copied(); + let infer_variables = vars.flex_vars.iter().copied(); def_pattern_state .constraints @@ -207,8 +201,8 @@ pub fn frontload_ability_constraints( constraint = constrain_def_make_constraint( constraints, - new_rigid_variables, - new_infer_variables, + rigid_variables, + infer_variables, Constraint::True, constraint, def_pattern_state, From 154b17d9f0e55d7168e2d1c82093a21415186cf6 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 16:46:37 -0400 Subject: [PATCH 34/44] Correct instantiation of delayed opaques I didn't realize that delayed instantiation of aliases instantiates their real var, not the whole alias type. So prior to this, we were wrapping all opaques in a structural `Alias(..)`. Which is okay from a typechecking perspective because Aliases are transparent, but is wasteful. --- compiler/solve/src/solve.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 2cb1282b65..bb31d42a91 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -17,8 +17,8 @@ use roc_types::subs::{ }; use roc_types::types::Type::{self, *}; use roc_types::types::{ - gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, LambdaSet, - OptAbleType, OptAbleVar, PatternCategory, Reason, TypeExtension, + gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, OptAbleType, + OptAbleVar, PatternCategory, Reason, TypeExtension, }; use roc_unify::unify::{unify, Mode, Unified::*}; @@ -327,9 +327,6 @@ impl Aliases { } let old_type_variables = delayed_variables.type_variables(&mut self.variables); - #[allow(clippy::needless_collect)] // otherwise we borrow self.variables immutably twice - let variable_opt_abilities: Vec<_> = - old_type_variables.iter().map(|ov| ov.opt_ability).collect(); let new_type_variables = &subs.variables[alias_variables.type_variables().indices()]; for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) { From b3845c21bdc02a1d19c7337e69b3b2f0fde8bd51 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 17:12:28 -0400 Subject: [PATCH 35/44] Print able type variables correctly --- compiler/solve/src/solve.rs | 9 ++------- compiler/solve/tests/solve_expr.rs | 18 ++++++++++++++++++ compiler/types/src/pretty_print.rs | 16 +++++++++++++++- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index bb31d42a91..1493eee1a1 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1958,6 +1958,8 @@ fn type_to_variable<'a>( let copy_var = match opt_ability { None => helper!(typ), Some(ability) => { + // If this type argument is marked as being bound to an ability, we must + // now correctly instantiate it as so. match RegisterVariable::from_type(subs, rank, pools, arena, typ) { RegisterVariable::Direct(var) => { use Content::*; @@ -1968,13 +1970,6 @@ fn type_to_variable<'a>( var, FlexAbleVar(Some(name), *ability), ), - RigidAbleVar(name, existing_ability) => { - debug_assert_eq!(existing_ability, *ability); - subs.set_content( - var, - FlexAbleVar(Some(name), existing_ability), - ); - } _ => { // TODO associate the type to the bound ability, and check // that it correctly implements the ability. diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index f05fe04163..91ff4aca6f 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6260,6 +6260,24 @@ mod solve_expr { ) } + #[test] + fn alias_propogates_able_var() { + infer_eq_without_problem( + indoc!( + r#" + app "test" provides [ zeroEncoder ] to "./platform" + + Encoder fmt := List U8, fmt -> List U8 | fmt has Format + + Format has it : fmt -> {} | fmt has Format + + zeroEncoder = @Encoder \lst, _ -> lst + "# + ), + "Encoder a | a has Format", + ) + } + #[test] fn encoder() { infer_queries( diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index b266edddb8..276a8ab2a1 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -116,7 +116,7 @@ fn find_names_needed( } match &subs.get_content_without_compacting(variable).clone() { - RecursionVar { opt_name: None, .. } | FlexVar(None) | FlexAbleVar(None, _) => { + RecursionVar { opt_name: None, .. } | FlexVar(None) => { let root = subs.get_root_key_without_compacting(variable); // If this var is *not* its own root, then the @@ -135,6 +135,15 @@ fn find_names_needed( } } } + FlexAbleVar(None, _) => { + let root = subs.get_root_key_without_compacting(variable); + if !root_appearances.contains_key(&root) { + roots.push(root); + } + // Able vars are always printed at least twice (in the signature, and in the "has" + // clause set). + root_appearances.insert(root, Appearances::Multiple); + } RecursionVar { opt_name: Some(name_index), .. @@ -271,6 +280,11 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) { let content = FlexVar(Some(name_index)); subs.set_content(root, content); } + &FlexAbleVar(None, ability) => { + let name_index = SubsIndex::push_new(&mut subs.field_names, name); + let content = FlexAbleVar(Some(name_index), ability); + subs.set_content(root, content); + } RecursionVar { opt_name: None, structure, From 08beedb648b7f0da4c30155526fb70915049bf8a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 17:19:18 -0400 Subject: [PATCH 36/44] Improve codelink comment --- compiler/can/src/abilities.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/can/src/abilities.rs b/compiler/can/src/abilities.rs index 350c6f00af..9db02a7cdc 100644 --- a/compiler/can/src/abilities.rs +++ b/compiler/can/src/abilities.rs @@ -7,7 +7,7 @@ use roc_types::{subs::Variable, types::Type}; pub struct MemberVariables { pub able_vars: Vec, /// This includes - named rigid vars, lambda sets, wildcards. See - /// [`crate::annotation::IntroducedVariables::collect_rigid`]. + /// [`IntroducedVariables::collect_rigid`](crate::annotation::IntroducedVariables::collect_rigid). pub rigid_vars: Vec, pub flex_vars: Vec, } @@ -34,7 +34,7 @@ pub struct MemberSpecialization { /// Stores information about what abilities exist in a scope, what it means to implement an /// ability, and what types implement them. // TODO(abilities): this should probably go on the Scope, I don't put it there for now because we -// are only dealing with inter-module abilities for now. +// are only dealing with intra-module abilities for now. #[derive(Default, Debug, Clone)] pub struct AbilitiesStore { /// Maps an ability to the members defining it. From 4858979d6b4ddb0d5bf4158e55b3ff6c2c72ebb7 Mon Sep 17 00:00:00 2001 From: Ayaz <20735482+ayazhafiz@users.noreply.github.com> Date: Sat, 7 May 2022 18:33:49 -0400 Subject: [PATCH 37/44] Remove stale comment --- compiler/can/src/expr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 73017c42b0..53b53f6fd5 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -199,7 +199,6 @@ pub enum Expr { // for the expression from the opaque definition. `type_arguments` is something like // [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]". specialized_def_type: Box, - // Fresh type variable and any ability it is bound to type_arguments: Vec, lambda_set_variables: Vec, }, From d0bd08dbe0de03df0070007388748b2ef375fb85 Mon Sep 17 00:00:00 2001 From: Ayaz <20735482+ayazhafiz@users.noreply.github.com> Date: Sat, 7 May 2022 18:34:36 -0400 Subject: [PATCH 38/44] Typo --- compiler/solve/tests/solve_expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 91ff4aca6f..3b37623caf 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -6261,7 +6261,7 @@ mod solve_expr { } #[test] - fn alias_propogates_able_var() { + fn alias_propagates_able_var() { infer_eq_without_problem( indoc!( r#" From 2cacda00eb4481b8f89c7b5e3e8a12f791fbd798 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 21:00:28 -0400 Subject: [PATCH 39/44] Add some internal error assertions --- compiler/solve/src/solve.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 1493eee1a1..a2a75cd146 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1966,10 +1966,8 @@ fn type_to_variable<'a>( match *subs.get_content_without_compacting(var) { FlexVar(opt_name) => subs .set_content(var, FlexAbleVar(opt_name, *ability)), - RigidVar(name) => subs.set_content( - var, - FlexAbleVar(Some(name), *ability), - ), + RigidVar(..) => internal_error!("Rigid var in type arg for {:?} - this is a bug in the solver, or our understanding", actual), + RigidAbleVar(..) | FlexAbleVar(..) => internal_error!("Able var in type arg for {:?} - this is a bug in the solver, or our understanding", actual), _ => { // TODO associate the type to the bound ability, and check // that it correctly implements the ability. From da604e978e93c5a8b60f983e6a8a948594a31312 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 21:32:50 -0400 Subject: [PATCH 40/44] Use SpecializationId to mark specializations --- compiler/can/src/abilities.rs | 49 ++++++++++++++++++++++++++++++ compiler/can/src/expr.rs | 8 ++--- compiler/load_internal/src/file.rs | 8 ++--- compiler/mono/src/ir.rs | 41 +++++++++++++------------ 4 files changed, 78 insertions(+), 28 deletions(-) diff --git a/compiler/can/src/abilities.rs b/compiler/can/src/abilities.rs index 9db02a7cdc..d5b29493a4 100644 --- a/compiler/can/src/abilities.rs +++ b/compiler/can/src/abilities.rs @@ -31,10 +31,20 @@ pub struct MemberSpecialization { pub region: Region, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SpecializationId(u64); + +impl Default for SpecializationId { + fn default() -> Self { + Self(0) + } +} + /// Stores information about what abilities exist in a scope, what it means to implement an /// ability, and what types implement them. // TODO(abilities): this should probably go on the Scope, I don't put it there for now because we // are only dealing with intra-module abilities for now. +// TODO(abilities): many of these should be `VecMap`s. Do some benchmarking. #[derive(Default, Debug, Clone)] pub struct AbilitiesStore { /// Maps an ability to the members defining it. @@ -56,6 +66,12 @@ pub struct AbilitiesStore { /// Maps a tuple (member, type) specifying that `type` declares an implementation of an ability /// member `member`, to the exact symbol that implements the ability. declared_specializations: MutMap<(Symbol, Symbol), MemberSpecialization>, + + next_specialization_id: u64, + + /// Resolved specializations for a symbol. These might be ephemeral (known due to type solving), + /// or resolved on-the-fly during mono. + resolved_specializations: MutMap, } impl AbilitiesStore { @@ -170,4 +186,37 @@ impl AbilitiesStore { pub fn members_of_ability(&self, ability: Symbol) -> Option<&[Symbol]> { self.members_of_ability.get(&ability).map(|v| v.as_ref()) } + + pub fn fresh_specialization_id(&mut self) -> SpecializationId { + debug_assert!(self.next_specialization_id != std::u64::MAX); + + let id = SpecializationId(self.next_specialization_id); + self.next_specialization_id += 1; + id + } + + pub fn insert_resolved(&mut self, id: SpecializationId, specialization: Symbol) { + debug_assert!(self.is_specialization_name(specialization)); + + let old_specialization = self.resolved_specializations.insert(id, specialization); + + debug_assert!( + old_specialization.is_none(), + "Existing resolution: {:?}", + old_specialization + ); + } + + pub fn remove_resolved(&mut self, id: SpecializationId) { + let old_specialization = self.resolved_specializations.remove(&id); + + debug_assert!( + old_specialization.is_some(), + "Trying to remove a resolved specialization that was never there!", + ); + } + + pub fn get_resolved(&self, id: SpecializationId) -> Option { + self.resolved_specializations.get(&id).copied() + } } diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 53b53f6fd5..3b6c99898a 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -1,3 +1,4 @@ +use crate::abilities::SpecializationId; use crate::annotation::{freshen_opaque_def, IntroducedVariables}; use crate::builtins::builtin_defs_map; use crate::def::{can_defs_with_return, Def}; @@ -21,7 +22,6 @@ use roc_region::all::{Loc, Region}; use roc_types::subs::{ExhaustiveMark, RedundantMark, VarStore, Variable}; use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type}; use std::fmt::{Debug, Display}; -use std::sync::{Arc, RwLock}; use std::{char, u32}; #[derive(Clone, Default, Debug)] @@ -88,7 +88,7 @@ pub enum Expr { /// Actual member name Symbol, /// Specialization to use - Arc>>, + SpecializationId, ), // Branching @@ -1328,7 +1328,7 @@ fn canonicalize_var_lookup( output.references.insert_value_lookup(symbol); if scope.abilities_store.is_ability_member_name(symbol) { - AbilityMember(symbol, Arc::new(RwLock::new(None))) + AbilityMember(symbol, scope.abilities_store.fresh_specialization_id()) } else { Var(symbol) } @@ -1347,7 +1347,7 @@ fn canonicalize_var_lookup( output.references.insert_value_lookup(symbol); if scope.abilities_store.is_ability_member_name(symbol) { - AbilityMember(symbol, Arc::new(RwLock::new(None))) + AbilityMember(symbol, scope.abilities_store.fresh_specialization_id()) } else { Var(symbol) } diff --git a/compiler/load_internal/src/file.rs b/compiler/load_internal/src/file.rs index 62b1189f77..454f7e2493 100644 --- a/compiler/load_internal/src/file.rs +++ b/compiler/load_internal/src/file.rs @@ -4107,7 +4107,7 @@ fn make_specializations<'a>( specializations_we_must_make: Vec, mut module_timing: ModuleTiming, target_info: TargetInfo, - abilities_store: AbilitiesStore, + mut abilities_store: AbilitiesStore, ) -> Msg<'a> { let make_specializations_start = SystemTime::now(); let mut mono_problems = Vec::new(); @@ -4123,7 +4123,7 @@ fn make_specializations<'a>( update_mode_ids: &mut update_mode_ids, // call_specialization_counter=0 is reserved call_specialization_counter: 1, - abilities_store: &abilities_store, + abilities_store: &mut abilities_store, }; let mut procs = Procs::new_in(arena); @@ -4194,7 +4194,7 @@ fn build_pending_specializations<'a>( target_info: TargetInfo, // TODO remove exposed_to_host: ExposedToHost, - abilities_store: AbilitiesStore, + mut abilities_store: AbilitiesStore, ) -> Msg<'a> { let find_specializations_start = SystemTime::now(); @@ -4221,7 +4221,7 @@ fn build_pending_specializations<'a>( update_mode_ids: &mut update_mode_ids, // call_specialization_counter=0 is reserved call_specialization_counter: 1, - abilities_store: &abilities_store, + abilities_store: &mut abilities_store, }; // Add modules' decls to Procs diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 2d4bfc607c..dc83e8e826 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1254,7 +1254,7 @@ pub struct Env<'a, 'i> { pub target_info: TargetInfo, pub update_mode_ids: &'i mut UpdateModeIds, pub call_specialization_counter: u32, - pub abilities_store: &'i AbilitiesStore, + pub abilities_store: &'i mut AbilitiesStore, } impl<'a, 'i> Env<'a, 'i> { @@ -2493,7 +2493,7 @@ fn resolve_abilities_in_specialized_body<'a>( struct Resolver<'a> { subs: &'a mut Subs, procs: &'a Procs<'a>, - abilities_store: &'a AbilitiesStore, + abilities_store: &'a mut AbilitiesStore, seen_defs: MutSet, } impl PatternVisitor for Resolver<'_> {} @@ -2511,12 +2511,13 @@ fn resolve_abilities_in_specialized_body<'a>( // So, we'll resolve any nested abilities when we know their specialized type // during def construction. } - Expr::AbilityMember(member_sym, specialization_cell) => { - let mut specialization_cell = specialization_cell - .write() - .expect("Can't lock specialization cell"); - if specialization_cell.is_some() { - // We already know the specialization; we are good to go. + Expr::AbilityMember(member_sym, specialization_id) => { + if self + .abilities_store + .get_resolved(*specialization_id) + .is_some() + { + // We already know the specialization from type solving; we are good to go. return; } @@ -2555,7 +2556,8 @@ fn resolve_abilities_in_specialized_body<'a>( self.seen_defs.insert(specialization); } - *specialization_cell = Some(specialization); + self.abilities_store + .insert_resolved(*specialization_id, specialization); } _ => walk_expr(self, expr), } @@ -3657,10 +3659,10 @@ pub fn with_hole<'a>( specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol) } - AbilityMember(_member, specialization) => { - let specialization_symbol = specialization - .read() - .unwrap() + AbilityMember(_member, specialization_id) => { + let specialization_symbol = env + .abilities_store + .get_resolved(specialization_id) .expect("Specialization was never made!"); specialize_naked_symbol( @@ -4531,9 +4533,8 @@ pub fn with_hole<'a>( hole, ) } - roc_can::expr::Expr::AbilityMember(_, specialization) => { - let specialization_cell = specialization.read().unwrap(); - let proc_name = specialization_cell.expect( + roc_can::expr::Expr::AbilityMember(_, specialization_id) => { + let proc_name = env.abilities_store.get_resolved(specialization_id).expect( "Ability specialization is unknown - code generation cannot proceed!", ); @@ -7040,10 +7041,10 @@ fn can_reuse_symbol<'a>( use ReuseSymbol::*; let symbol = match expr { - AbilityMember(_, specialization) => { - let specialization_symbol = specialization - .read() - .unwrap() + AbilityMember(_, specialization_id) => { + let specialization_symbol = env + .abilities_store + .get_resolved(*specialization_id) .expect("Specialization must be known!"); specialization_symbol } From e31e89ac6604acca21765664fd935b875aba2a1c Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 21:57:17 -0400 Subject: [PATCH 41/44] Mark and clear specializations --- compiler/mono/src/ir.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index dc83e8e826..6522b2d93e 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -7,7 +7,7 @@ use crate::layout::{ use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; use roc_builtins::bitcode::{FloatWidth, IntWidth}; -use roc_can::abilities::AbilitiesStore; +use roc_can::abilities::{AbilitiesStore, SpecializationId}; use roc_can::expr::{AnnotatedMark, ClosureData, IntValue}; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; use roc_collections::{MutSet, VecMap}; @@ -2485,7 +2485,7 @@ fn resolve_abilities_in_specialized_body<'a>( procs: &Procs<'a>, specialized_body: &roc_can::expr::Expr, body_var: Variable, -) { +) -> std::vec::Vec { use roc_can::expr::Expr; use roc_can::traverse::{walk_expr, PatternVisitor, Visitor}; use roc_unify::unify::unify; @@ -2495,6 +2495,7 @@ fn resolve_abilities_in_specialized_body<'a>( procs: &'a Procs<'a>, abilities_store: &'a mut AbilitiesStore, seen_defs: MutSet, + specialized: std::vec::Vec, } impl PatternVisitor for Resolver<'_> {} impl Visitor for Resolver<'_> { @@ -2558,6 +2559,9 @@ fn resolve_abilities_in_specialized_body<'a>( self.abilities_store .insert_resolved(*specialization_id, specialization); + + debug_assert!(!self.specialized.contains(&specialization_id)); + self.specialized.push(*specialization_id); } _ => walk_expr(self, expr), } @@ -2569,8 +2573,11 @@ fn resolve_abilities_in_specialized_body<'a>( procs, abilities_store: env.abilities_store, seen_defs: MutSet::default(), + specialized: vec![], }; specializer.visit_expr(specialized_body, Region::zero(), body_var); + + specializer.specialized } fn specialize_external<'a>( @@ -2708,9 +2715,17 @@ fn specialize_external<'a>( }; let body = partial_proc.body.clone(); - resolve_abilities_in_specialized_body(env, procs, &body, partial_proc.body_var); + let resolved_ability_specializations = + resolve_abilities_in_specialized_body(env, procs, &body, partial_proc.body_var); + let mut specialized_body = from_can(env, partial_proc.body_var, body, procs, layout_cache); + // reset the resolved ability specializations so as not to interfere with other specializations + // of this proc. + resolved_ability_specializations + .into_iter() + .for_each(|sid| env.abilities_store.remove_resolved(sid)); + match specialized { SpecializedLayout::FunctionPointerBody { ret_layout, From 798c28fccdbeeaa0e6bdd949fbf606c48be3796b Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 22:05:05 -0400 Subject: [PATCH 42/44] Dryasdust was a popular word in 1860 --- compiler/can/src/abilities.rs | 1 + compiler/mono/src/copy.rs | 2 +- compiler/mono/src/ir.rs | 13 +++++-------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/can/src/abilities.rs b/compiler/can/src/abilities.rs index d5b29493a4..d2aef3fbd2 100644 --- a/compiler/can/src/abilities.rs +++ b/compiler/can/src/abilities.rs @@ -34,6 +34,7 @@ pub struct MemberSpecialization { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SpecializationId(u64); +#[allow(clippy::derivable_impls)] // let's be explicit about this impl Default for SpecializationId { fn default() -> Self { Self(0) diff --git a/compiler/mono/src/copy.rs b/compiler/mono/src/copy.rs index d45a1144e5..0621d34061 100644 --- a/compiler/mono/src/copy.rs +++ b/compiler/mono/src/copy.rs @@ -67,7 +67,7 @@ pub fn deep_copy_type_vars_into_expr<'a>( loc_elems: loc_elems.iter().map(|le| le.map(go_help)).collect(), }, Var(sym) => Var(*sym), - AbilityMember(sym, specialization) => AbilityMember(*sym, specialization.clone()), + AbilityMember(sym, specialization) => AbilityMember(*sym, *specialization), When { loc_cond, cond_var, diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 6522b2d93e..ca270b4c52 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2560,7 +2560,7 @@ fn resolve_abilities_in_specialized_body<'a>( self.abilities_store .insert_resolved(*specialization_id, specialization); - debug_assert!(!self.specialized.contains(&specialization_id)); + debug_assert!(!self.specialized.contains(specialization_id)); self.specialized.push(*specialization_id); } _ => walk_expr(self, expr), @@ -7056,13 +7056,10 @@ fn can_reuse_symbol<'a>( use ReuseSymbol::*; let symbol = match expr { - AbilityMember(_, specialization_id) => { - let specialization_symbol = env - .abilities_store - .get_resolved(*specialization_id) - .expect("Specialization must be known!"); - specialization_symbol - } + AbilityMember(_, specialization_id) => env + .abilities_store + .get_resolved(*specialization_id) + .expect("Specialization must be known!"), Var(symbol) => *symbol, _ => return NotASymbol, }; From 4bfbe479c96bd2dfce6f4cb10a026b7732faea4a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Sat, 7 May 2022 22:08:25 -0400 Subject: [PATCH 43/44] Useless comment --- compiler/can/src/pattern.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index e2da660297..e06f695b29 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -47,7 +47,6 @@ pub enum Pattern { // for the expression from the opaque definition. `type_arguments` is something like // [(n, fresh1)], and `specialized_def_type` becomes "[ Id U64 fresh1 ]". specialized_def_type: Box, - // Fresh type variable and any ability it is bound to type_arguments: Vec, lambda_set_variables: Vec, }, From f2d38653155717bbaa06e1d5f6ffa78feedd4bf7 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Sun, 8 May 2022 15:17:59 -0400 Subject: [PATCH 44/44] Fix can AST walk --- compiler/can/src/traverse.rs | 16 ++++++++-------- compiler/mono/src/ir.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/can/src/traverse.rs b/compiler/can/src/traverse.rs index de93ad16ef..7a23cdf20f 100644 --- a/compiler/can/src/traverse.rs +++ b/compiler/can/src/traverse.rs @@ -58,7 +58,7 @@ pub fn walk_def(visitor: &mut V, def: &Def) { } } -pub fn walk_expr(visitor: &mut V, expr: &Expr) { +pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { match expr { Expr::Closure(closure_data) => walk_closure(visitor, closure_data), Expr::When { @@ -91,13 +91,13 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr) { branch_var, final_else, } => walk_if(visitor, *cond_var, branches, *branch_var, final_else), - Expr::LetRec(defs, body, body_var) => { + Expr::LetRec(defs, body) => { defs.iter().for_each(|def| visitor.visit_def(def)); - visitor.visit_expr(&body.value, body.region, *body_var); + visitor.visit_expr(&body.value, body.region, var); } - Expr::LetNonRec(def, body, body_var) => { + Expr::LetNonRec(def, body) => { visitor.visit_def(def); - visitor.visit_expr(&body.value, body.region, *body_var); + visitor.visit_expr(&body.value, body.region, var); } Expr::Call(f, args, _called_via) => { let (fn_var, loc_fn, _closure_var, _ret_var) = &**f; @@ -288,8 +288,8 @@ pub trait Visitor: Sized + PatternVisitor { // ignore by default } - fn visit_expr(&mut self, expr: &Expr, _region: Region, _var: Variable) { - walk_expr(self, expr); + fn visit_expr(&mut self, expr: &Expr, _region: Region, var: Variable) { + walk_expr(self, expr, var); } } @@ -328,7 +328,7 @@ impl Visitor for TypeAtVisitor { return; } if region.contains(&self.region) { - walk_expr(self, expr); + walk_expr(self, expr, var); } } } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index ca270b4c52..afeb17efc4 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2563,7 +2563,7 @@ fn resolve_abilities_in_specialized_body<'a>( debug_assert!(!self.specialized.contains(specialization_id)); self.specialized.push(*specialization_id); } - _ => walk_expr(self, expr), + _ => walk_expr(self, expr, var), } } }