diff --git a/cli/src/repl.rs b/cli/src/repl.rs index 1995e86e2c..a7f4fc3e21 100644 --- a/cli/src/repl.rs +++ b/cli/src/repl.rs @@ -765,6 +765,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu used.insert(v); } } + Store(tipe, var, _, _) => { + for v in tipe.variables() { + used.insert(v); + } + + used.insert(*var); + } Lookup(_, expectation, _) => { for v in expectation.get_type_ref().variables() { used.insert(v); diff --git a/compiler/can/src/constraint.rs b/compiler/can/src/constraint.rs index 4fd0de9d91..05c5aa840c 100644 --- a/compiler/can/src/constraint.rs +++ b/compiler/can/src/constraint.rs @@ -8,6 +8,7 @@ use roc_types::types::{Category, PatternCategory, Type}; #[derive(Debug, Clone, PartialEq)] pub enum Constraint { Eq(Type, Expected, Category, Region), + Store(Type, Variable, &'static str, u32), Lookup(Symbol, Expected, Region), Pattern(Region, PatternCategory, Type, PExpected), True, // Used for things that always unify, e.g. blanks and runtime errors diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 6c63a4dc4e..c826a1ff76 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1221,19 +1221,9 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint { })), // "the closure's type is equal to expected type" Eq(fn_type.clone(), expected.clone(), Category::Lambda, region), - // "fn_var is equal to the closure's type" - fn_var is used in code gen - Eq( - Type::Variable(*fn_var), - NoExpectation(Type::Variable(expr_var)), - Category::Storage(std::file!(), std::line!()), - region, - ), - Eq( - Type::Variable(expr_var), - expected, - Category::Storage(std::file!(), std::line!()), - region, - ), + // Store type into AST vars. We use Store so errors aren't reported twice + Store(signature.clone(), *fn_var, std::file!(), std::line!()), + Store(signature, expr_var, std::file!(), std::line!()), closure_constraint, ]), ) @@ -1579,18 +1569,9 @@ pub fn rec_defs_help( })), Eq(fn_type.clone(), expected.clone(), Category::Lambda, region), // "fn_var is equal to the closure's type" - fn_var is used in code gen - Eq( - Type::Variable(*fn_var), - NoExpectation(fn_type), - Category::Storage(std::file!(), std::line!()), - region, - ), - Eq( - Type::Variable(expr_var), - expected, - Category::Storage(std::file!(), std::line!()), - region, - ), + // Store type into AST vars. We use Store so errors aren't reported twice + Store(signature.clone(), *fn_var, std::file!(), std::line!()), + Store(signature, expr_var, std::file!(), std::line!()), closure_constraint, ]), ); diff --git a/compiler/load/tests/helpers/mod.rs b/compiler/load/tests/helpers/mod.rs index 5a4ee10103..22feefb073 100644 --- a/compiler/load/tests/helpers/mod.rs +++ b/compiler/load/tests/helpers/mod.rs @@ -389,6 +389,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu used.insert(v); } } + Store(tipe, var, _, _) => { + for v in tipe.variables() { + used.insert(v); + } + + used.insert(*var); + } Lookup(_, expectation, _) => { for v in expectation.get_type_ref().variables() { used.insert(v); diff --git a/compiler/mono/tests/helpers/mod.rs b/compiler/mono/tests/helpers/mod.rs index beecbcacca..cdbcd68ef8 100644 --- a/compiler/mono/tests/helpers/mod.rs +++ b/compiler/mono/tests/helpers/mod.rs @@ -402,6 +402,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu used.insert(v); } } + Store(tipe, var, _, _) => { + for v in tipe.variables() { + used.insert(v); + } + + used.insert(*var); + } Lookup(_, expectation, _) => { for v in expectation.get_type_ref().variables() { used.insert(v); diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 98cc649f02..4a6133c477 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -244,6 +244,34 @@ fn solve( } } } + Store(source, target, _filename, _linenr) => { + // a special version of Eq that is used to store types in the AST. + // IT DOES NOT REPORT ERRORS! + let actual = type_to_var(subs, rank, pools, cached_aliases, source); + let target = *target; + + match unify(subs, actual, target) { + Success(vars) => { + introduce(subs, rank, pools, &vars); + + state + } + Failure(vars, _actual_type, _expected_type) => { + introduce(subs, rank, pools, &vars); + + // ERROR NOT REPORTED + + state + } + BadType(vars, _problem) => { + introduce(subs, rank, pools, &vars); + + // ERROR NOT REPORTED + + state + } + } + } Lookup(symbol, expectation, region) => { let var = *env.vars_by_symbol.get(&symbol).unwrap_or_else(|| { // TODO Instead of panicking, solve this as True and record diff --git a/compiler/solve/tests/helpers/mod.rs b/compiler/solve/tests/helpers/mod.rs index 44243dde0a..dcf196fccb 100644 --- a/compiler/solve/tests/helpers/mod.rs +++ b/compiler/solve/tests/helpers/mod.rs @@ -417,6 +417,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu used.insert(v); } } + Store(tipe, var, _, _) => { + for v in tipe.variables() { + used.insert(v); + } + + used.insert(*var); + } Lookup(_, expectation, _) => { for v in expectation.get_type_ref().variables() { used.insert(v); diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 64183a1088..2a33175150 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -3481,4 +3481,42 @@ mod solve_expr { "Dict Int Int", ); } + + #[test] + fn quicksort_partition_help() { + infer_eq_without_problem( + indoc!( + r#" + app Test provides [ partitionHelp ] imports [] + + swap : Int, Int, List a -> List a + swap = \i, j, list -> + when Pair (List.get list i) (List.get list j) is + Pair (Ok atI) (Ok atJ) -> + list + |> List.set i atJ + |> List.set j atI + + _ -> + [] + + partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] + partitionHelp = \i, j, list, high, pivot -> + if j < high then + when List.get list j is + Ok value -> + if value <= pivot then + partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot + else + partitionHelp i (j + 1) list high pivot + + Err _ -> + Pair i list + else + Pair i list + "# + ), + "Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]", + ); + } } diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 7e4c68b519..4b3777d68b 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1089,7 +1089,7 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _) => { // Type mismatch! Rigid can only unify with flex, even if the // rigid names are the same. - mismatch!("Rigid with {:?}", &other) + mismatch!("Rigid {:?} with {:?}", ctx.first, &other) } Error => { // Error propagates. diff --git a/compiler/uniq/tests/helpers/mod.rs b/compiler/uniq/tests/helpers/mod.rs index 4af99f21d6..9d298728e0 100644 --- a/compiler/uniq/tests/helpers/mod.rs +++ b/compiler/uniq/tests/helpers/mod.rs @@ -414,6 +414,13 @@ fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mu used.insert(v); } } + Store(tipe, var, _, _) => { + for v in tipe.variables() { + used.insert(v); + } + + used.insert(*var); + } Lookup(_, expectation, _) => { for v in expectation.get_type_ref().variables() { used.insert(v);