diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 2af2a5fc7b..bb17149cf6 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -265,6 +265,8 @@ fn get_tags_vars_and_variant<'a>( (vars_of_tag, union_variant) } +const FAKE_EXPR: &'static Loc = &Loc::at_zero(Expr::Crash); + fn expr_of_tag<'a, M: ReplAppMemory>( env: &mut Env<'a, '_>, mem: &'a M, @@ -279,9 +281,35 @@ fn expr_of_tag<'a, M: ReplAppMemory>( debug_assert_eq!(arg_layouts.len(), arg_vars.len()); - // NOTE assumes the data bytes are the first bytes - let it = arg_vars.iter().copied().zip(arg_layouts.iter()); - let output = sequence_of_expr(env, mem, data_addr, it, when_recursive); + // The type checker stores payloads in definition order, but the memory representation sorts + // first by size (and tie-breaks by definition order). + let mut layouts: Vec<_> = arg_vars + .iter() + .enumerate() + .map(|(i, v)| { + let layout = env.layout_cache.from_var(env.arena, *v, env.subs).unwrap(); + (i, *v, layout) + }) + .collect_in(env.arena); + + layouts.sort_by(|(i1, _, lay1), (i2, _, lay2)| { + cmp_fields(&env.layout_cache.interner, i1, *lay1, i2, *lay2) + }); + + let mut output: Vec<&Loc> = + Vec::from_iter_in(std::iter::repeat(FAKE_EXPR).take(layouts.len()), env.arena); + let mut field_addr = data_addr; + for (i, var, lay) in layouts { + let repr = env.layout_cache.interner.get_repr(lay); + let expr = addr_to_ast(env, mem, field_addr, repr, when_recursive, var); + let loc_expr = Loc::at_zero(expr); + + output[i] = &*env.arena.alloc(loc_expr); + + // Advance the field pointer to the next field. + field_addr += env.layout_cache.interner.stack_size(lay) as usize; + } + let output = output.into_bump_slice(); Expr::Apply(loc_tag_expr, output, CalledVia::Space) @@ -962,11 +990,14 @@ fn single_tag_union_to_ast<'a, M: ReplAppMemory>( let loc_tag_expr = &*arena.alloc(Loc::at_zero(tag_expr)); let output = if field_layouts.len() == payload_vars.len() { - let it = payload_vars.iter().copied().zip(field_layouts); + let it = payload_vars + .iter() + .copied() + .zip(field_layouts.iter().copied()); sequence_of_expr(env, mem, addr, it, WhenRecursive::Unreachable).into_bump_slice() } else if field_layouts.is_empty() && !payload_vars.is_empty() { // happens for e.g. `Foo Bar` where unit structures are nested and the inner one is dropped - let it = payload_vars.iter().copied().zip([&Layout::UNIT]); + let it = payload_vars.iter().copied().zip([Layout::UNIT]); sequence_of_expr(env, mem, addr, it, WhenRecursive::Unreachable).into_bump_slice() } else { unreachable!() @@ -983,8 +1014,7 @@ fn sequence_of_expr<'a, 'env, I, M: ReplAppMemory>( when_recursive: WhenRecursive<'a>, ) -> Vec<'a, &'a Loc>> where - I: Iterator)>, - I: ExactSizeIterator)>, + I: ExactSizeIterator)>, { let arena = env.arena; let mut output = Vec::with_capacity_in(sequence.len(), arena); @@ -997,7 +1027,7 @@ where env, mem, field_addr, - env.layout_cache.get_repr(*layout), + env.layout_cache.get_repr(layout), when_recursive, var, ); @@ -1006,7 +1036,7 @@ where output.push(&*arena.alloc(loc_expr)); // Advance the field pointer to the next field. - field_addr += env.layout_cache.interner.stack_size(*layout) as usize; + field_addr += env.layout_cache.interner.stack_size(layout) as usize; } output diff --git a/crates/repl_test/src/tests.rs b/crates/repl_test/src/tests.rs index 90b8d632b1..3769319140 100644 --- a/crates/repl_test/src/tests.rs +++ b/crates/repl_test/src/tests.rs @@ -1304,3 +1304,20 @@ fn nested_tuple() { r#"("a", (2, 3)) : ( Str, ( U32, U32 )a )a"#, ); } + +#[test] +fn ordered_tag_union_memory_layout() { + expect_success( + indoc!( + r#" + Loc : { line: U32, column: U32 } + + Node : [ A Loc, Height U8 Loc ] + + x : Node + x = Height 1 { line: 2, column: 3 } + "# + ), + r#""#, + ); +}