diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index cfd0ef026a..e6597c0114 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -207,6 +207,67 @@ impl<'a> PartialProc<'a> { } } +#[derive(Clone, Debug)] +enum PartialExprLink { + Aliases(Symbol), + Expr(roc_can::expr::Expr, Variable), +} + +/// A table of symbols to polymorphic expressions. For example, in the program +/// +/// n = 1 +/// +/// asU8 : U8 -> U8 +/// asU8 = \_ -> 1 +/// +/// asU32 : U32 -> U8 +/// asU32 = \_ -> 1 +/// +/// asU8 n + asU32 n +/// +/// The expression bound by `n` doesn't have a definite layout until it is used +/// at the call sites `asU8 n`, `asU32 n`. +/// +/// Polymorphic *functions* are stored in `PartialProc`s, since functions are +/// non longer first-class once we finish lowering to the IR. +#[derive(Clone, Debug)] +pub struct PartialExprs(BumpMap); + +impl PartialExprs { + pub fn new_in<'a>(arena: &'a Bump) -> Self { + Self(BumpMap::new_in(arena)) + } + + pub fn insert(&mut self, symbol: Symbol, expr: roc_can::expr::Expr, expr_var: Variable) { + self.0.insert(symbol, PartialExprLink::Expr(expr, expr_var)); + } + + pub fn insert_alias(&mut self, symbol: Symbol, aliases: Symbol) { + self.0.insert(symbol, PartialExprLink::Aliases(aliases)); + } + + pub fn contains(&self, symbol: Symbol) -> bool { + self.0.contains_key(&symbol) + } + + pub fn get(&mut self, mut symbol: Symbol) -> Option<(&roc_can::expr::Expr, Variable)> { + // In practice the alias chain is very short + loop { + match self.0.get(&symbol) { + None => { + return None; + } + Some(&PartialExprLink::Aliases(real_symbol)) => { + symbol = real_symbol; + } + Some(PartialExprLink::Expr(expr, var)) => { + return Some((expr, *var)); + } + } + } + } +} + #[derive(Clone, Copy, Debug, PartialEq)] pub enum CapturedSymbols<'a> { None, @@ -668,6 +729,7 @@ impl<'a> Specialized<'a> { #[derive(Clone, Debug)] pub struct Procs<'a> { pub partial_procs: PartialProcs<'a>, + pub partial_exprs: PartialExprs, pub imported_module_thunks: &'a [Symbol], pub module_thunks: &'a [Symbol], pending_specializations: PendingSpecializations<'a>, @@ -680,6 +742,7 @@ impl<'a> Procs<'a> { pub fn new_in(arena: &'a Bump) -> Self { Self { partial_procs: PartialProcs::new_in(arena), + partial_exprs: PartialExprs::new_in(arena), imported_module_thunks: &[], module_thunks: &[], pending_specializations: PendingSpecializations::Finding(Suspended::new_in(arena)), @@ -3103,16 +3166,20 @@ pub fn with_hole<'a>( _ => {} } - // continue with the default path - let mut stmt = with_hole( - env, - cont.value, - variable, - procs, - layout_cache, - assigned, - hole, - ); + let build_rest = + |env: &mut Env<'a, '_>, + procs: &mut Procs<'a>, + layout_cache: &mut LayoutCache<'a>| { + with_hole( + env, + cont.value, + variable, + procs, + layout_cache, + assigned, + hole, + ) + }; // a variable is aliased if let roc_can::expr::Expr::Var(original) = def.loc_expr.value { @@ -3124,18 +3191,17 @@ pub fn with_hole<'a>( // // foo = RBTRee.empty - stmt = handle_variable_aliasing( + handle_variable_aliasing( env, procs, layout_cache, def.expr_var, symbol, original, - stmt, - ); - - stmt + build_rest, + ) } else { + let rest = build_rest(env, procs, layout_cache); with_hole( env, def.loc_expr.value, @@ -3143,7 +3209,7 @@ pub fn with_hole<'a>( procs, layout_cache, symbol, - env.arena.alloc(stmt), + env.arena.alloc(rest), ) } } else { @@ -3328,6 +3394,7 @@ pub fn with_hole<'a>( let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena); enum Field { + // TODO: rename this since it can handle unspecialized expressions now too Function(Symbol, Variable), ValueSymbol, Field(roc_can::expr::Field), @@ -3338,7 +3405,7 @@ pub fn with_hole<'a>( use ReuseSymbol::*; match fields.remove(&label) { Some(field) => match can_reuse_symbol(env, procs, &field.loc_expr.value) { - Imported(symbol) | LocalFunction(symbol) => { + Imported(symbol) | LocalFunction(symbol) | UnspecializedExpr(symbol) => { field_symbols.push(symbol); can_fields.push(Field::Function(symbol, variable)); } @@ -4064,6 +4131,9 @@ pub fn with_hole<'a>( LocalFunction(_) => { unreachable!("if this was known to be a function, we would not be here") } + UnspecializedExpr(_) => { + unreachable!("if this was known to be an unspecialized expression, we would not be here") + } Imported(thunk_name) => { debug_assert!(procs.is_imported_module_thunk(thunk_name)); @@ -5023,6 +5093,31 @@ fn register_capturing_closure<'a>( } } +fn is_literal_like(expr: &roc_can::expr::Expr) -> bool { + use roc_can::expr::Expr::*; + matches!( + expr, + Num(..) + | Int(..) + | Float(..) + | List { .. } + | Str(_) + | ZeroArgumentTag { .. } + | Tag { .. } + | Record { .. } + ) +} + +fn expr_is_polymorphic<'a>( + env: &mut Env<'a, '_>, + expr: &roc_can::expr::Expr, + expr_var: Variable, +) -> bool { + // TODO: I don't think we need the `is_literal_like` check, but taking it slow for now... + let is_flex_or_rigid = |c: &Content| matches!(c, Content::FlexVar(_) | Content::RigidVar(_)); + is_literal_like(expr) && env.subs.var_contains_content(expr_var, is_flex_or_rigid) +} + pub fn from_can<'a>( env: &mut Env<'a, '_>, variable: Variable, @@ -5177,19 +5272,26 @@ pub fn from_can<'a>( // or // // foo = RBTRee.empty - let mut rest = from_can(env, def.expr_var, cont.value, procs, layout_cache); - rest = handle_variable_aliasing( + // TODO: right now we need help out rustc with the closure types; + // it isn't able to infer the right lifetime bounds. See if we + // can remove the annotations in the future. + let build_rest = + |env: &mut Env<'a, '_>, + procs: &mut Procs<'a>, + layout_cache: &mut LayoutCache<'a>| { + from_can(env, def.expr_var, cont.value, procs, layout_cache) + }; + + return handle_variable_aliasing( env, procs, layout_cache, def.expr_var, *symbol, original, - rest, + build_rest, ); - - return rest; } roc_can::expr::Expr::LetNonRec(nested_def, nested_cont, nested_annotation) => { use roc_can::expr::Expr::*; @@ -5273,6 +5375,21 @@ pub fn from_can<'a>( return from_can(env, variable, new_outer, procs, layout_cache); } + ref body if expr_is_polymorphic(env, body, def.expr_var) => { + // This is a pattern like + // + // n = 1 + // asU8 n + // + // At the definition site `n = 1` we only know `1` to have the type `[Int *]`, + // which won't be refined until the call `asU8 n`. Add it as a partial expression + // that will be specialized at each concrete usage site. + procs + .partial_exprs + .insert(*symbol, def.loc_expr.value, def.expr_var); + + return from_can(env, variable, cont.value, procs, layout_cache); + } _ => { let rest = from_can(env, variable, cont.value, procs, layout_cache); return with_hole( @@ -6290,6 +6407,7 @@ enum ReuseSymbol { Imported(Symbol), LocalFunction(Symbol), Value(Symbol), + UnspecializedExpr(Symbol), NotASymbol, } @@ -6307,6 +6425,8 @@ fn can_reuse_symbol<'a>( Imported(symbol) } else if procs.partial_procs.contains_key(symbol) { LocalFunction(symbol) + } else if procs.partial_exprs.contains(symbol) { + UnspecializedExpr(symbol) } else { Value(symbol) } @@ -6326,15 +6446,29 @@ fn possible_reuse_symbol<'a>( } } -fn handle_variable_aliasing<'a>( +fn handle_variable_aliasing<'a, BuildRest>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, layout_cache: &mut LayoutCache<'a>, variable: Variable, left: Symbol, right: Symbol, - mut result: Stmt<'a>, -) -> Stmt<'a> { + build_rest: BuildRest, +) -> Stmt<'a> +where + BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>, +{ + if procs.partial_exprs.contains(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.partial_exprs.insert_alias(left, right); + return build_rest(env, procs, layout_cache); + } + + // Otherwise we're dealing with an alias to something that doesn't need to be specialized, or + // whose usages will already be specialized in the rest of the program. Let's just build the + // rest of the program now to get our hole. + let mut result = build_rest(env, procs, layout_cache); if procs.is_imported_module_thunk(right) { // if this is an imported symbol, then we must make sure it is // specialized, and wrap the original in a function pointer. @@ -6392,6 +6526,7 @@ fn let_empty_struct<'a>(assigned: Symbol, hole: &'a Stmt<'a>) -> Stmt<'a> { } /// If the symbol is a function, make sure it is properly specialized +// TODO: rename this now that we handle polymorphic non-function expressions too fn reuse_function_symbol<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, @@ -6401,6 +6536,35 @@ fn reuse_function_symbol<'a>( result: Stmt<'a>, original: Symbol, ) -> Stmt<'a> { + if let Some((expr, expr_var)) = procs.partial_exprs.get(original) { + // Specialize the expression type now, based off the `arg_var` we've been given. + // TODO: cache the specialized result + let snapshot = env.subs.snapshot(); + let cache_snapshot = layout_cache.snapshot(); + let _unified = roc_unify::unify::unify( + env.subs, + arg_var.unwrap(), + expr_var, + roc_unify::unify::Mode::Eq, + ); + + let result = with_hole( + env, + expr.clone(), + expr_var, + procs, + layout_cache, + symbol, + env.arena.alloc(result), + ); + + // Restore the prior state so as not to interfere with future specializations. + env.subs.rollback_to(snapshot); + layout_cache.rollback_to(cache_snapshot); + + return result; + } + match procs.get_partial_proc(original) { None => { match arg_var { @@ -6566,7 +6730,7 @@ fn assign_to_symbol<'a>( ) -> Stmt<'a> { use ReuseSymbol::*; match can_reuse_symbol(env, procs, &loc_arg.value) { - Imported(original) | LocalFunction(original) => { + Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => { // for functions we must make sure they are specialized correctly reuse_function_symbol( env, diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 55845c777e..db050da940 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -2631,3 +2631,22 @@ fn list_find_empty_layout() { i64 ); } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_lists() { + assert_evals_to!( + indoc!( + r#" + l = [1, 2, 3] + + f : List U8, List U16 -> Nat + f = \_, _ -> 18 + + f l l + "# + ), + 18, + u64 + ) +} diff --git a/compiler/test_gen/src/gen_num.rs b/compiler/test_gen/src/gen_num.rs index e026c4b292..c6c33b94e6 100644 --- a/compiler/test_gen/src/gen_num.rs +++ b/compiler/test_gen/src/gen_num.rs @@ -2317,3 +2317,92 @@ fn sub_saturated() { u8 ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_ints() { + assert_evals_to!( + indoc!( + r#" + x = 100 + + f : U8, U32 -> Nat + f = \_, _ -> 18 + + f x x + "# + ), + 18, + u64 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_floats() { + assert_evals_to!( + indoc!( + r#" + x = 100.0 + + f : F32, F64 -> Nat + f = \_, _ -> 18 + + f x x + "# + ), + 18, + u64 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_ints_names_dont_conflict() { + assert_evals_to!( + indoc!( + r#" + f : U8 -> Nat + f = \_ -> 9 + x = + n = 100 + f n + + y = + n = 100 + f n + + x + y + "# + ), + 18, + u64 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_ints_aliased() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + y = 100 + w1 = y + w2 = y + + f1 : U8, U32 -> U8 + f1 = \_, _ -> 1 + + f2 : U32, U8 -> U8 + f2 = \_, _ -> 2 + + f1 w1 w2 + f2 w1 w2 + "# + ), + 3, + u8 + ) +} diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index f396534be8..b8062dbde2 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -1262,3 +1262,108 @@ fn recursive_tag_union_into_flat_tag_union() { |_| 0 ) } + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_tag() { + assert_evals_to!( + indoc!( + r#" + b = False + f : Bool, [True, False, Idk] -> U8 + f = \_, _ -> 18 + f b b + "# + ), + 18, + u8 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_applied_tag() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [ main ] to "./platform" + + main = + a = A "abc" + f = \x -> + when x is + A y -> y + B y -> y + f a + "# + ), + RocStr::from_slice(b"abc"), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_tag_with_polymorphic_arg() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + a = A + wrap = Wrapped a + + useWrap1 : [Wrapped [A], Other] -> U8 + useWrap1 = + \w -> when w is + Wrapped A -> 2 + Other -> 3 + + useWrap2 : [Wrapped [A, B]] -> U8 + useWrap2 = + \w -> when w is + Wrapped A -> 5 + Wrapped B -> 7 + + useWrap1 wrap * useWrap2 wrap + "# + ), + 10, + u8 + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn monomorphized_tag_with_polymorphic_arg_and_monomorphic_arg() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + mono : U8 + mono = 15 + poly = A + wrap = Wrapped poly mono + + useWrap1 : [Wrapped [A] U8, Other] -> U8 + useWrap1 = + \w -> when w is + Wrapped A n -> n + Other -> 0 + + useWrap2 : [Wrapped [A, B] U8] -> U8 + useWrap2 = + \w -> when w is + Wrapped A n -> n + Wrapped B _ -> 0 + + useWrap1 wrap * useWrap2 wrap + "# + ), + 225, + u8 + ) +} diff --git a/compiler/test_mono/generated/alias_variable.txt b/compiler/test_mono/generated/alias_variable.txt index 095d77af3a..657376d73a 100644 --- a/compiler/test_mono/generated/alias_variable.txt +++ b/compiler/test_mono/generated/alias_variable.txt @@ -1,4 +1,3 @@ procedure Test.0 (): - let Test.1 : Builtin(Int(I64)) = 5i64; let Test.3 : Builtin(Int(I64)) = 3i64; ret Test.3; diff --git a/compiler/test_mono/generated/alias_variable_and_return_it.txt b/compiler/test_mono/generated/alias_variable_and_return_it.txt index ff5f614740..9e5824a185 100644 --- a/compiler/test_mono/generated/alias_variable_and_return_it.txt +++ b/compiler/test_mono/generated/alias_variable_and_return_it.txt @@ -1,3 +1,3 @@ procedure Test.0 (): - let Test.1 : Builtin(Int(I64)) = 5i64; - ret Test.1; + let Test.2 : Builtin(Int(I64)) = 5i64; + ret Test.2; diff --git a/compiler/test_mono/generated/closure_in_list.txt b/compiler/test_mono/generated/closure_in_list.txt index 9fa9aaeae9..b4de3287e0 100644 --- a/compiler/test_mono/generated/closure_in_list.txt +++ b/compiler/test_mono/generated/closure_in_list.txt @@ -3,13 +3,13 @@ procedure List.7 (#Attr.2): ret Test.7; procedure Test.1 (Test.5): - let Test.2 : Builtin(Int(I64)) = 41i64; let Test.11 : LambdaSet([( Test.3, [Builtin(Int(I64))])]LambdaSet { set: Ok(()), representation: Struct([Builtin(Int(I64))]) }) = Struct {Test.2}; let Test.10 : Builtin(List(LambdaSet([( Test.3, [Builtin(Int(I64))])]LambdaSet { set: Ok(()), representation: Struct([Builtin(Int(I64))]) }))) = Array [Test.11]; ret Test.10; procedure Test.3 (Test.9, #Attr.12): let Test.2 : Builtin(Int(I64)) = StructAtIndex 0 #Attr.12; + let Test.2 : Builtin(Int(I64)) = 41i64; ret Test.2; procedure Test.0 (): diff --git a/compiler/test_mono/generated/empty_list_of_function_type.txt b/compiler/test_mono/generated/empty_list_of_function_type.txt index e68e0bf85d..e24846c6b4 100644 --- a/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -15,7 +15,6 @@ procedure Test.2 (Test.6): ret Test.24; procedure Test.0 (): - let Test.1 : Builtin(List(LambdaSet([]LambdaSet { set: Ok(()), representation: Struct([]) }))) = Array []; joinpoint Test.22 Test.3: let Test.14 : Builtin(Int(U64)) = 0i64; let Test.7 : Union(NonRecursive([[Struct([])], [LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) })]])) = CallByName List.3 Test.3 Test.14; @@ -35,9 +34,9 @@ procedure Test.0 (): in let Test.25 : Builtin(Bool) = false; if Test.25 then + let Test.1 : Builtin(List(LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) }))) = Array []; jump Test.22 Test.1; else - dec Test.1; let Test.23 : LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) }) = Struct {}; let Test.21 : Builtin(List(LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) }))) = Array [Test.23]; jump Test.22 Test.21; diff --git a/compiler/test_mono/generated/ir_int_add.txt b/compiler/test_mono/generated/ir_int_add.txt index 798fd708d7..2d49bd57c1 100644 --- a/compiler/test_mono/generated/ir_int_add.txt +++ b/compiler/test_mono/generated/ir_int_add.txt @@ -1,19 +1,19 @@ procedure List.7 (#Attr.2): - let Test.6 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2; - ret Test.6; + let Test.7 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2; + ret Test.7; procedure Num.24 (#Attr.2, #Attr.3): let Test.5 : Builtin(Int(U64)) = lowlevel NumAdd #Attr.2 #Attr.3; ret Test.5; procedure Test.0 (): - let Test.1 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64]; - let Test.9 : Builtin(Int(U64)) = 5i64; - let Test.10 : Builtin(Int(U64)) = 4i64; - let Test.7 : Builtin(Int(U64)) = CallByName Num.24 Test.9 Test.10; - let Test.8 : Builtin(Int(U64)) = 3i64; - let Test.3 : Builtin(Int(U64)) = CallByName Num.24 Test.7 Test.8; - let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.1; - dec Test.1; + let Test.10 : Builtin(Int(U64)) = 5i64; + let Test.11 : Builtin(Int(U64)) = 4i64; + let Test.8 : Builtin(Int(U64)) = CallByName Num.24 Test.10 Test.11; + let Test.9 : Builtin(Int(U64)) = 3i64; + let Test.3 : Builtin(Int(U64)) = CallByName Num.24 Test.8 Test.9; + let Test.6 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64]; + let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.6; + dec Test.6; let Test.2 : Builtin(Int(U64)) = CallByName Num.24 Test.3 Test.4; ret Test.2; diff --git a/compiler/test_mono/generated/ir_two_defs.txt b/compiler/test_mono/generated/ir_two_defs.txt index 1811869476..66b9bd2728 100644 --- a/compiler/test_mono/generated/ir_two_defs.txt +++ b/compiler/test_mono/generated/ir_two_defs.txt @@ -1,9 +1,9 @@ procedure Num.24 (#Attr.2, #Attr.3): - let Test.4 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3; - ret Test.4; + let Test.6 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3; + ret Test.6; procedure Test.0 (): - let Test.1 : Builtin(Int(I64)) = 3i64; - let Test.2 : Builtin(Int(I64)) = 4i64; - let Test.3 : Builtin(Int(I64)) = CallByName Num.24 Test.1 Test.2; + let Test.4 : Builtin(Int(I64)) = 3i64; + let Test.5 : Builtin(Int(I64)) = 4i64; + let Test.3 : Builtin(Int(I64)) = CallByName Num.24 Test.4 Test.5; ret Test.3; diff --git a/compiler/test_mono/generated/let_x_in_x.txt b/compiler/test_mono/generated/let_x_in_x.txt index 39b16fd536..1ae2b5dd47 100644 --- a/compiler/test_mono/generated/let_x_in_x.txt +++ b/compiler/test_mono/generated/let_x_in_x.txt @@ -1,5 +1,3 @@ procedure Test.0 (): - let Test.1 : Builtin(Int(I64)) = 5i64; - let Test.4 : Builtin(Int(I64)) = 17i64; let Test.2 : Builtin(Int(I64)) = 1337i64; ret Test.2; diff --git a/compiler/test_mono/generated/let_x_in_x_indirect.txt b/compiler/test_mono/generated/let_x_in_x_indirect.txt index 761529956b..8897207353 100644 --- a/compiler/test_mono/generated/let_x_in_x_indirect.txt +++ b/compiler/test_mono/generated/let_x_in_x_indirect.txt @@ -1,8 +1,6 @@ procedure Test.0 (): - let Test.1 : Builtin(Int(I64)) = 5i64; - let Test.4 : Builtin(Int(I64)) = 17i64; - let Test.5 : Builtin(Int(I64)) = 1i64; let Test.2 : Builtin(Int(I64)) = 1337i64; - let Test.7 : Struct([Builtin(Int(I64)), Builtin(Int(I64))]) = Struct {Test.2, Test.4}; + let Test.3 : Builtin(Int(I64)) = 17i64; + let Test.7 : Struct([Builtin(Int(I64)), Builtin(Int(I64))]) = Struct {Test.2, Test.3}; let Test.6 : Builtin(Int(I64)) = StructAtIndex 0 Test.7; ret Test.6; diff --git a/compiler/test_mono/generated/list_len.txt b/compiler/test_mono/generated/list_len.txt index 41e4167e0f..c210581d86 100644 --- a/compiler/test_mono/generated/list_len.txt +++ b/compiler/test_mono/generated/list_len.txt @@ -1,6 +1,6 @@ procedure List.7 (#Attr.2): - let Test.7 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2; - ret Test.7; + let Test.10 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2; + ret Test.10; procedure List.7 (#Attr.2): let Test.8 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2; @@ -11,11 +11,11 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Test.6; procedure Test.0 (): - let Test.1 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64, 3i64]; - let Test.2 : Builtin(List(Builtin(Float(F64)))) = Array [1f64]; - let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.1; - dec Test.1; - let Test.5 : Builtin(Int(U64)) = CallByName List.7 Test.2; - dec Test.2; + let Test.9 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64, 3i64]; + let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.9; + dec Test.9; + let Test.7 : Builtin(List(Builtin(Float(F64)))) = Array [1f64]; + let Test.5 : Builtin(Int(U64)) = CallByName List.7 Test.7; + dec Test.7; let Test.3 : Builtin(Int(U64)) = CallByName Num.24 Test.4 Test.5; ret Test.3; diff --git a/compiler/test_mono/generated/monomorphized_applied_tag.txt b/compiler/test_mono/generated/monomorphized_applied_tag.txt new file mode 100644 index 0000000000..90dd27c931 --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_applied_tag.txt @@ -0,0 +1,20 @@ +procedure Test.2 (Test.4): + let Test.11 : Builtin(Int(U8)) = 0i64; + let Test.12 : Builtin(Int(U8)) = GetTagId Test.4; + let Test.13 : Builtin(Bool) = lowlevel Eq Test.11 Test.12; + if Test.13 then + let Test.5 : Builtin(Str) = UnionAtIndex (Id 0) (Index 0) Test.4; + inc Test.5; + dec Test.4; + ret Test.5; + else + let Test.6 : Builtin(Str) = UnionAtIndex (Id 1) (Index 0) Test.4; + inc Test.6; + dec Test.4; + ret Test.6; + +procedure Test.0 (): + let Test.14 : Builtin(Str) = "A"; + let Test.8 : Union(NonRecursive([[Builtin(Str)], [Builtin(Str)]])) = A Test.14; + let Test.7 : Builtin(Str) = CallByName Test.2 Test.8; + ret Test.7; diff --git a/compiler/test_mono/generated/monomorphized_floats.txt b/compiler/test_mono/generated/monomorphized_floats.txt new file mode 100644 index 0000000000..90eef8acc7 --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_floats.txt @@ -0,0 +1,9 @@ +procedure Test.2 (Test.3, Test.4): + let Test.8 : Builtin(Int(U64)) = 18i64; + ret Test.8; + +procedure Test.0 (): + let Test.6 : Builtin(Float(F32)) = 100f64; + let Test.7 : Builtin(Float(F64)) = 100f64; + let Test.5 : Builtin(Int(U64)) = CallByName Test.2 Test.6 Test.7; + ret Test.5; diff --git a/compiler/test_mono/generated/monomorphized_ints.txt b/compiler/test_mono/generated/monomorphized_ints.txt new file mode 100644 index 0000000000..5ffdd51f13 --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_ints.txt @@ -0,0 +1,9 @@ +procedure Test.2 (Test.3, Test.4): + let Test.8 : Builtin(Int(U64)) = 18i64; + ret Test.8; + +procedure Test.0 (): + let Test.6 : Builtin(Int(U8)) = 100i64; + let Test.7 : Builtin(Int(U32)) = 100i64; + let Test.5 : Builtin(Int(U64)) = CallByName Test.2 Test.6 Test.7; + ret Test.5; diff --git a/compiler/test_mono/generated/monomorphized_ints_aliased.txt b/compiler/test_mono/generated/monomorphized_ints_aliased.txt new file mode 100644 index 0000000000..3b9735dfa5 --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_ints_aliased.txt @@ -0,0 +1,23 @@ +procedure Num.24 (#Attr.2, #Attr.3): + let Test.12 : Builtin(Int(U64)) = lowlevel NumAdd #Attr.2 #Attr.3; + ret Test.12; + +procedure Test.4 (Test.7, Test.8): + let Test.17 : Builtin(Int(U64)) = 1i64; + ret Test.17; + +procedure Test.4 (Test.7, Test.8): + let Test.18 : Builtin(Int(U64)) = 1i64; + ret Test.18; + +procedure Test.0 (): + let Test.4 : LambdaSet([( Test.4, [])]LambdaSet { set: Ok(()), representation: Struct([]) }) = Struct {}; + let Test.4 : LambdaSet([( Test.4, [])]LambdaSet { set: Ok(()), representation: Struct([]) }) = Struct {}; + let Test.15 : Builtin(Int(U8)) = 100i64; + let Test.16 : Builtin(Int(U32)) = 100i64; + let Test.10 : Builtin(Int(U64)) = CallByName Test.4 Test.15 Test.16; + let Test.13 : Builtin(Int(U32)) = 100i64; + let Test.14 : Builtin(Int(U8)) = 100i64; + let Test.11 : Builtin(Int(U64)) = CallByName Test.4 Test.13 Test.14; + let Test.9 : Builtin(Int(U64)) = CallByName Num.24 Test.10 Test.11; + ret Test.9; diff --git a/compiler/test_mono/generated/monomorphized_list.txt b/compiler/test_mono/generated/monomorphized_list.txt new file mode 100644 index 0000000000..d38a0682ff --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_list.txt @@ -0,0 +1,11 @@ +procedure Test.2 (Test.3, Test.4): + let Test.8 : Builtin(Int(U64)) = 18i64; + ret Test.8; + +procedure Test.0 (): + let Test.6 : Builtin(List(Builtin(Int(U8)))) = Array [1i64, 2i64, 3i64]; + let Test.7 : Builtin(List(Builtin(Int(U16)))) = Array [1i64, 2i64, 3i64]; + let Test.5 : Builtin(Int(U64)) = CallByName Test.2 Test.6 Test.7; + dec Test.7; + dec Test.6; + ret Test.5; diff --git a/compiler/test_mono/generated/monomorphized_tag.txt b/compiler/test_mono/generated/monomorphized_tag.txt new file mode 100644 index 0000000000..a1cb92209d --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_tag.txt @@ -0,0 +1,9 @@ +procedure Test.2 (Test.4, Test.5): + let Test.9 : Builtin(Int(U8)) = 18i64; + ret Test.9; + +procedure Test.0 (): + let Test.7 : Builtin(Bool) = false; + let Test.8 : Builtin(Int(U8)) = 0u8; + let Test.6 : Builtin(Int(U8)) = CallByName Test.2 Test.7 Test.8; + ret Test.6; diff --git a/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt b/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt new file mode 100644 index 0000000000..02a88fb375 --- /dev/null +++ b/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt @@ -0,0 +1,10 @@ +procedure Test.4 (Test.8): + let Test.11 : Builtin(Int(U64)) = 1i64; + ret Test.11; + +procedure Test.0 (): + let Test.13 : Builtin(Bool) = false; + let Test.12 : Builtin(Bool) = false; + let Test.10 : Struct([Builtin(Bool), Builtin(Bool)]) = Struct {Test.12, Test.13}; + let Test.9 : Builtin(Int(U64)) = CallByName Test.4 Test.10; + ret Test.9; diff --git a/compiler/test_mono/generated/nested_closure.txt b/compiler/test_mono/generated/nested_closure.txt index f8413a0265..b7a572df71 100644 --- a/compiler/test_mono/generated/nested_closure.txt +++ b/compiler/test_mono/generated/nested_closure.txt @@ -1,10 +1,10 @@ procedure Test.1 (Test.5): - let Test.2 : Builtin(Int(I64)) = 42i64; let Test.3 : LambdaSet([( Test.3, [Builtin(Int(I64))])]LambdaSet { set: Ok(()), representation: Struct([Builtin(Int(I64))]) }) = Struct {Test.2}; ret Test.3; procedure Test.3 (Test.9, #Attr.12): let Test.2 : Builtin(Int(I64)) = StructAtIndex 0 #Attr.12; + let Test.2 : Builtin(Int(I64)) = 42i64; ret Test.2; procedure Test.0 (): diff --git a/compiler/test_mono/generated/record_optional_field_function_use_default.txt b/compiler/test_mono/generated/record_optional_field_function_use_default.txt index 327b3e2f02..8b3ec11a1d 100644 --- a/compiler/test_mono/generated/record_optional_field_function_use_default.txt +++ b/compiler/test_mono/generated/record_optional_field_function_use_default.txt @@ -1,13 +1,13 @@ procedure Num.24 (#Attr.2, #Attr.3): - let Test.8 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3; - ret Test.8; + let Test.9 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3; + ret Test.9; procedure Test.1 (Test.4): - let Test.2 : Builtin(Int(I64)) = 10i64; - let Test.7 : Builtin(Int(I64)) = CallByName Num.24 Test.2 Test.4; + let Test.8 : Builtin(Int(I64)) = 10i64; + let Test.7 : Builtin(Int(I64)) = CallByName Num.24 Test.8 Test.4; ret Test.7; procedure Test.0 (): - let Test.9 : Builtin(Int(I64)) = 9i64; - let Test.5 : Builtin(Int(I64)) = CallByName Test.1 Test.9; + let Test.10 : Builtin(Int(I64)) = 9i64; + let Test.5 : Builtin(Int(I64)) = CallByName Test.1 Test.10; ret Test.5; diff --git a/compiler/test_mono/src/tests.rs b/compiler/test_mono/src/tests.rs index 75f3c19201..8926a4942a 100644 --- a/compiler/test_mono/src/tests.rs +++ b/compiler/test_mono/src/tests.rs @@ -1111,6 +1111,131 @@ fn empty_list_of_function_type() { ) } +#[mono_test] +fn monomorphized_ints() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + x = 100 + + f : U8, U32 -> Nat + f = \_, _ -> 18 + + f x x + "# + ) +} + +#[mono_test] +fn monomorphized_floats() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + x = 100.0 + + f : F32, F64 -> Nat + f = \_, _ -> 18 + + f x x + "# + ) +} + +#[mono_test] +#[ignore = "TODO"] +fn monomorphized_ints_aliased() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + y = 100 + w1 = y + w2 = y + + f = \_, _ -> 1 + + f1 : U8, U32 -> Nat + f1 = f + + f2 : U32, U8 -> Nat + f2 = f + + f1 w1 w2 + f2 w1 w2 + "# + ) +} + +#[mono_test] +fn monomorphized_tag() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + b = False + f : Bool, [True, False, Idk] -> U8 + f = \_, _ -> 18 + f b b + "# + ) +} + +#[mono_test] +fn monomorphized_tag_with_aliased_args() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + b = False + c = False + a = A b c + f : [A Bool Bool] -> Nat + f = \_ -> 1 + f a + "# + ) +} + +#[mono_test] +fn monomorphized_list() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + main = + l = [1, 2, 3] + + f : List U8, List U16 -> Nat + f = \_, _ -> 18 + + f l l + "# + ) +} + +#[mono_test] +fn monomorphized_applied_tag() { + indoc!( + r#" + app "test" provides [ main ] to "./platform" + + main = + a = A "A" + f = \x -> + when x is + A y -> y + B y -> y + f a + "# + ) +} + // #[ignore] // #[mono_test] // fn static_str_closure() {