diff --git a/compiler/gen/tests/gen_tags.rs b/compiler/gen/tests/gen_tags.rs index eabbd1803a..9aa5468e6e 100644 --- a/compiler/gen/tests/gen_tags.rs +++ b/compiler/gen/tests/gen_tags.rs @@ -481,7 +481,7 @@ mod gen_tags { assert_evals_to!( indoc!( r#" - wrapper = \{} -> + wrapper = \{} -> when 2 is 2 if False -> 0 _ -> 42 @@ -499,7 +499,7 @@ mod gen_tags { assert_evals_to!( indoc!( r#" - wrapper = \{} -> + wrapper = \{} -> when 2 is 2 if True -> 42 _ -> 0 @@ -517,7 +517,7 @@ mod gen_tags { assert_evals_to!( indoc!( r#" - wrapper = \{} -> + wrapper = \{} -> when 2 is _ if False -> 0 _ -> 42 @@ -637,7 +637,7 @@ mod gen_tags { x : Maybe (Maybe Int) x = Just (Just 41) - main = + main = x "# ), @@ -701,11 +701,11 @@ mod gen_tags { assert_evals_to!( indoc!( r#" - wrapper = \{} -> + wrapper = \{} -> x : [ Red, White, Blue ] x = Blue - y = + y = when x is Red -> 1 White -> 2 @@ -726,8 +726,8 @@ mod gen_tags { assert_evals_to!( indoc!( r#" - wrapper = \{} -> - y = + wrapper = \{} -> + y = when 1 + 2 is 3 -> 3 1 -> 1 @@ -745,7 +745,7 @@ mod gen_tags { assert_evals_to!( indoc!( r#" - y = + y = if 1 + 2 > 0 then 3 else @@ -778,7 +778,7 @@ mod gen_tags { x = Three (1 == 1) 32 when x is - Three bool int -> + Three bool int -> { bool, int } #" ), @@ -792,7 +792,7 @@ mod gen_tags { x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32 when x is - Three bool color int -> + Three bool color int -> { bool, color, int } #" ), @@ -800,4 +800,72 @@ mod gen_tags { (i64, bool, u8) ); } + + #[test] + fn alignment_in_multi_tag_construction() { + assert_evals_to!( + indoc!( + r"# + x : [ Three Bool Int, Empty ] + x = Three (1 == 1) 32 + + x + + #" + ), + (1, 32i64, true), + (i64, i64, bool) + ); + + assert_evals_to!( + indoc!( + r"# + x : [ Three Bool [ Red, Green, Blue ] Int, Empty ] + x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32 + + x + #" + ), + (1, 32i64, true, 2u8), + (i64, i64, bool, u8) + ); + } + + #[test] + fn alignment_in_multi_tag_pattern_match() { + assert_evals_to!( + indoc!( + r"# + x : [ Three Bool Int, Empty ] + x = Three (1 == 1) 32 + + when x is + Three bool int -> + { bool, int } + + Empty -> + { bool: False, int: 0 } + #" + ), + (32i64, true), + (i64, bool) + ); + + assert_evals_to!( + indoc!( + r"# + x : [ Three Bool [ Red, Green, Blue ] Int, Empty ] + x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32 + + when x is + Three bool color int -> + { bool, color, int } + Empty -> + { bool: False, color: Red, int: 0 } + #" + ), + (32i64, true, 2u8), + (i64, bool, u8) + ); + } } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 4ee3501a6f..89c7a9cd40 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2472,12 +2472,33 @@ pub fn with_hole<'a>( .find(|(_, (key, _))| key == &tag_name) .expect("tag must be in its own type"); + let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena); + + for (var, arg) in args.drain(..) { + // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field + let layout = layout_cache + .from_var(env.arena, var, env.subs) + .unwrap_or_else(|err| { + panic!("TODO turn fn_var into a RuntimeError {:?}", err) + }); + + let alignment = layout.alignment_bytes(8); + + let symbol = possible_reuse_symbol(env, procs, &arg.value); + field_symbols_temp.push(( + alignment, + symbol, + ((var, arg), &*env.arena.alloc(symbol)), + )); + } + field_symbols_temp.sort_by(|a, b| b.0.cmp(&a.0)); + let mut field_symbols: Vec = Vec::with_capacity_in(args.len(), arena); let tag_id_symbol = env.unique_symbol(); field_symbols.push(tag_id_symbol); - for (_, arg) in args.iter() { - field_symbols.push(possible_reuse_symbol(env, procs, &arg.value)); + for (_, symbol, _) in field_symbols_temp.iter() { + field_symbols.push(*symbol); } let mut layouts: Vec<&'a [Layout<'a>]> = @@ -2498,7 +2519,11 @@ pub fn with_hole<'a>( }; let mut stmt = Stmt::Let(assigned, tag, layout, hole); - let iter = args.into_iter().rev().zip(field_symbols.iter().rev()); + let iter = field_symbols_temp + .drain(..) + .map(|x| x.2 .0) + .rev() + .zip(field_symbols.iter().rev()); stmt = assign_to_symbols(env, procs, layout_cache, iter, stmt); @@ -5370,6 +5395,20 @@ pub fn from_can_pattern<'a>( let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena); // disregard the tag discriminant layout + let mut arguments = arguments.clone(); + + arguments.sort_by(|arg1, arg2| { + let ptr_bytes = 8; + + let layout1 = layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap(); + let layout2 = layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap(); + + let size1 = layout1.alignment_bytes(ptr_bytes); + let size2 = layout2.alignment_bytes(ptr_bytes); + + size2.cmp(&size1) + }); + // TODO make this assert pass, it currently does not because // 0-sized values are dropped out // debug_assert_eq!(arguments.len(), argument_layouts[1..].len()); diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index be9409796b..336a0103e7 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1099,6 +1099,15 @@ pub fn union_sorted_tags_help<'a>( } } + arg_layouts.sort_by(|layout1, layout2| { + let ptr_bytes = 8; + + let size1 = layout1.alignment_bytes(ptr_bytes); + let size2 = layout2.alignment_bytes(ptr_bytes); + + size2.cmp(&size1) + }); + answer.push((tag_name, arg_layouts.into_bump_slice())); }