diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index e6938485b4..281bf37528 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -5622,4 +5622,16 @@ mod solve_expr { r#"Outer"#, ) } + + #[test] + fn issue_2583_specialize_errors_behind_unified_branches() { + infer_eq_without_problem( + indoc!( + r#" + if True then List.first [] else Str.toI64 "" + "# + ), + "Result I64 [ InvalidNumStr, ListWasEmpty ]*", + ) + } } diff --git a/compiler/test_gen/src/gen_result.rs b/compiler/test_gen/src/gen_result.rs index 127c5bfa95..296a23bab7 100644 --- a/compiler/test_gen/src/gen_result.rs +++ b/compiler/test_gen/src/gen_result.rs @@ -256,3 +256,15 @@ fn roc_result_err() { RocResult ); } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn issue_2583_specialize_errors_behind_unified_branches() { + assert_evals_to!( + r#" + if True then List.first [15] else Str.toI64 "" + "#, + RocResult::ok(15i64), + RocResult + ) +} diff --git a/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt new file mode 100644 index 0000000000..fef8630313 --- /dev/null +++ b/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -0,0 +1,43 @@ +procedure : `List.first` [C Int1, C I64] +procedure = `List.first` (`#Attr.#arg1`): + let `#UserApp.19` : U64 = 0i64; + let `#UserApp.20` : U64 = lowlevel ListLen `#Attr.#arg1`; + let `#UserApp.15` : Int1 = lowlevel NotEq `#UserApp.19` `#UserApp.20`; + if `#UserApp.15` then + let `#UserApp.18` : U64 = 0i64; + let `#UserApp.17` : I64 = lowlevel ListGetUnsafe `#Attr.#arg1` `#UserApp.18`; + let `#UserApp.16` : [C Int1, C I64] = Ok `#UserApp.17`; + ret `#UserApp.16`; + else + let `#UserApp.14` : Int1 = true; + let `#UserApp.13` : [C Int1, C I64] = Err `#UserApp.14`; + ret `#UserApp.13`; + +procedure : `Str.toI64` [C Int1, C I64] +procedure = `Str.toI64` (`#Attr.#arg1`): + let `#Attr.#arg2` : {I64, U8} = lowlevel StrToNum `#Attr.#arg1`; + let `#UserApp.8` : U8 = StructAtIndex 1 `#Attr.#arg2`; + let `#UserApp.9` : U8 = 0i64; + let `#UserApp.5` : Int1 = lowlevel NumGt `#UserApp.8` `#UserApp.9`; + if `#UserApp.5` then + let `#UserApp.7` : Int1 = false; + let `#UserApp.6` : [C Int1, C I64] = Err `#UserApp.7`; + ret `#UserApp.6`; + else + let `#UserApp.4` : I64 = StructAtIndex 0 `#Attr.#arg2`; + let `#UserApp.3` : [C Int1, C I64] = Ok `#UserApp.4`; + ret `#UserApp.3`; + +procedure : `#UserApp.main` [C Int1, C I64] +procedure = `#UserApp.main` (): + let `#UserApp.10` : Int1 = true; + if `#UserApp.10` then + let `#UserApp.12` : List I64 = Array []; + let `#UserApp.11` : [C Int1, C I64] = CallByName `List.first` `#UserApp.12`; + dec `#UserApp.12`; + ret `#UserApp.11`; + else + let `#UserApp.2` : Str = ""; + let `#UserApp.1` : [C Int1, C I64] = CallByName `Str.toI64` `#UserApp.2`; + dec `#UserApp.2`; + ret `#UserApp.1`; diff --git a/compiler/test_mono/src/tests.rs b/compiler/test_mono/src/tests.rs index 6051e31c51..64149bd120 100644 --- a/compiler/test_mono/src/tests.rs +++ b/compiler/test_mono/src/tests.rs @@ -1273,6 +1273,15 @@ fn issue_2725_alias_polymorphic_lambda() { ) } +#[mono_test] +fn issue_2583_specialize_errors_behind_unified_branches() { + indoc!( + r#" + if True then List.first [] else Str.toI64 "" + "# + ) +} + // #[ignore] // #[mono_test] // fn static_str_closure() { diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index de3a0a0e11..a68d83f303 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -1287,7 +1287,7 @@ fn integer_type( Content::Alias( Symbol::NUM_INTEGER, vars, - at_signed64, + at_integer_signed64, AliasKind::Structural, ) }); @@ -1532,7 +1532,7 @@ fn float_type( Content::Alias( Symbol::NUM_FLOATINGPOINT, vars, - at_binary64, + at_float_binary64, AliasKind::Structural, ) }); diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 4f3e21f8d7..2fde90be7b 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -332,13 +332,15 @@ fn unify_alias( problems.extend(merge(subs, ctx, *other_content)); } - // THEORY: if two aliases or opaques have the same name and arguments, their - // real_var is the same and we don't need to check it. - // See https://github.com/rtfeldman/roc/pull/1510 + // NOTE: we need to do this unification because unification of type variables + // may have made them larger, which then needs to be reflected in the `real_var`. // - // if problems.is_empty() && either_is_opaque { - // problems.extend(unify_pool(subs, pool, real_var, *other_real_var, ctx.mode)); - // } + // We can skip this if we know type variable unification didn't require + // modification of any variables, but we don't have that kind of information + // yet. + if problems.is_empty() { + problems.extend(unify_pool(subs, pool, real_var, *other_real_var, ctx.mode)); + } problems } else {