Hash record field name order in generated layouts

Closes #2535

See the referenced issue for longer discussion - here's the synopsis.
Consider this program

```
app "test" provides [ nums ] to "./platform"

alpha = { a: 1, b: 2 }

nums : List U8
nums =
    [
        alpha.a,
        alpha.b,
    ]
```

Here's its IR:

```
procedure : `#UserApp.alpha` {I64, U8}
procedure = `#UserApp.alpha` ():
    let `#UserApp.5` : Builtin(Int(I64)) = 1i64;
    let `#UserApp.6` : Builtin(Int(U8)) = 2i64;
    let `#UserApp.4` : Struct([Builtin(Int(I64)), Builtin(Int(U8))]) = Struct {`#UserApp.5`, `#UserApp.6`};
    ret `#UserApp.4`;

procedure : `#UserApp.nums` List U8
procedure = `#UserApp.nums` ():
    let `#UserApp.7` : Struct([Builtin(Int(I64)), Builtin(Int(U8))]) = CallByName `#UserApp.alpha`;
    let `#UserApp.1` : Builtin(Int(U8)) = StructAtIndex 1 `#UserApp.7`;
    let `#UserApp.3` : Struct([Builtin(Int(I64)), Builtin(Int(U8))]) = CallByName `#UserApp.alpha`;
    let `#UserApp.2` : Builtin(Int(U8)) = StructAtIndex 1 `#UserApp.3`;
    let `#UserApp.0` : Builtin(List(Builtin(Int(U8)))) = Array [`#UserApp.1`, `#UserApp.2`];
    ret `#UserApp.0`;
```

What's happening is that we need to specialize `alpha` twice - once for the
type of a narrowed to a U8, another time for the type of b narrowed to a U8.

We do the specialization for alpha.b first - record fields are sorted by
layout, so we generate a record of type {i64, u8}. But then we go to
specialize alpha.a, but this has the same layout - {i64, u8} - so we reuse
the existing one! So (at least for records), we need to include record field
order associated with the sorted layout fields, so that we don't reuse
monomorphizations like this incorrectly!
This commit is contained in:
ayazhafiz 2022-02-20 19:51:21 -05:00
parent 74daec84df
commit e52d427ac8
22 changed files with 225 additions and 105 deletions

View file

@ -374,7 +374,9 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
}
match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => {
Layout::Struct {
field_layouts: &[], ..
} => {
// nothing to add
}
other => {
@ -694,7 +696,9 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
let default = [value1.into(), value2.into()];
let arguments_cast = match closure_data_layout.runtime_representation() {
Layout::Struct(&[]) => {
Layout::Struct {
field_layouts: &[], ..
} => {
// nothing to add
&default
}

View file

@ -714,8 +714,7 @@ fn promote_to_main_function<'a, 'ctx, 'env>(
);
// NOTE fake layout; it is only used for debug prints
let roc_main_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::Struct(&[]));
let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::UNIT);
let main_fn_name = "$Test.main";
@ -1188,8 +1187,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
// extract field from a record
match (value, layout) {
(StructValue(argument), Layout::Struct(fields)) => {
debug_assert!(!fields.is_empty());
(StructValue(argument), Layout::Struct { field_layouts, .. }) => {
debug_assert!(!field_layouts.is_empty());
let field_value = env
.builder
@ -1201,14 +1200,14 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
)
.unwrap();
let field_layout = fields[*index as usize];
let field_layout = field_layouts[*index as usize];
use_roc_value(env, field_layout, field_value, "struct_field_tag")
}
(
PointerValue(argument),
Layout::Union(UnionLayout::NonNullableUnwrapped(fields)),
) => {
let struct_layout = Layout::Struct(fields);
let struct_layout = Layout::struct_no_name_order(fields);
let struct_type = basic_type_from_layout(env, &struct_layout);
let cast_argument = env
@ -1292,7 +1291,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
)
}
UnionLayout::NonNullableUnwrapped(field_layouts) => {
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
@ -1341,7 +1340,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
debug_assert_ne!(*tag_id != 0, *nullable_id);
let field_layouts = other_fields;
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
@ -2024,7 +2023,7 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let wrapper_type = env
@ -3522,7 +3521,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
call_roc_function(
env,
roc_wrapper_function,
&Layout::Struct(&[Layout::u64(), return_layout]),
&Layout::struct_no_name_order(&[Layout::u64(), return_layout]),
arguments_for_call,
)
};
@ -3903,7 +3902,7 @@ fn roc_result_layout<'a>(
) -> Layout<'a> {
let elements = [Layout::u64(), Layout::usize(target_info), return_layout];
Layout::Struct(arena.alloc(elements))
Layout::struct_no_name_order(arena.alloc(elements))
}
fn roc_result_type<'a, 'ctx, 'env>(
@ -5363,7 +5362,7 @@ fn run_low_level<'a, 'ctx, 'env>(
let (string, _string_layout) = load_symbol_and_layout(scope, &args[0]);
let number_layout = match layout {
Layout::Struct(fields) => fields[0], // TODO: why is it sometimes a struct?
Layout::Struct { field_layouts, .. } => field_layouts[0], // TODO: why is it sometimes a struct?
_ => unreachable!(),
};

View file

@ -735,8 +735,7 @@ pub fn set_from_list<'a, 'ctx, 'env>(
let result_alloca = builder.build_alloca(zig_dict_type(env), "result_alloca");
let alignment =
Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.target_info);
let alignment = Alignment::from_key_value_layout(key_layout, &Layout::UNIT, env.target_info);
let alignment_iv = alignment.as_int_value(env.context);
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);

View file

@ -50,10 +50,10 @@ fn build_hash_layout<'a, 'ctx, 'env>(
hash_builtin(env, layout_ids, seed, val, layout, builtin, when_recursive)
}
Layout::Struct(fields) => build_hash_struct(
Layout::Struct { field_layouts, .. } => build_hash_struct(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
seed,
val.into_struct_value(),
@ -166,7 +166,7 @@ fn build_hash_struct<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
@ -248,7 +248,7 @@ fn hash_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> {
let ptr_bytes = env.target_info;
let layout = Layout::Struct(field_layouts);
let layout = Layout::struct_no_name_order(field_layouts);
// Optimization: if the bit representation of equal values is the same
// just hash the bits. Caveat here is tags: e.g. `Nothing` in `Just a`
@ -818,7 +818,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
.build_struct_gep(wrapper_ptr, TAG_DATA_INDEX, "get_tag_data")
.unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let struct_type = basic_type_from_layout(env, &struct_layout);
let struct_ptr = env
.builder

View file

@ -161,10 +161,10 @@ fn build_eq<'a, 'ctx, 'env>(
build_eq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive)
}
Layout::Struct(fields) => build_struct_eq(
Layout::Struct { field_layouts, .. } => build_struct_eq(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -330,11 +330,11 @@ fn build_neq<'a, 'ctx, 'env>(
build_neq_builtin(env, layout_ids, lhs_val, rhs_val, builtin, when_recursive)
}
Layout::Struct(fields) => {
Layout::Struct { field_layouts, .. } => {
let is_equal = build_struct_eq(
env,
layout_ids,
fields,
field_layouts,
when_recursive,
lhs_val.into_struct_value(),
rhs_val.into_struct_value(),
@ -587,7 +587,7 @@ fn build_struct_eq<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
@ -1208,7 +1208,7 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
tag1: PointerValue<'ctx>,
tag2: PointerValue<'ctx>,
) -> IntValue<'ctx> {
let struct_layout = Layout::Struct(field_layouts);
let struct_layout = Layout::struct_no_name_order(field_layouts);
let wrapper_type = basic_type_from_layout(env, &struct_layout);
debug_assert!(wrapper_type.is_struct_type());

View file

@ -28,7 +28,10 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
use Layout::*;
match layout {
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
Struct {
field_layouts: sorted_fields,
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()),
Union(union_layout) => {
use UnionLayout::*;
@ -86,7 +89,10 @@ pub fn basic_type_from_layout_1<'a, 'ctx, 'env>(
use Layout::*;
match layout {
Struct(sorted_fields) => basic_type_from_record(env, sorted_fields),
Struct {
field_layouts: sorted_fields,
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => {
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
}

View file

@ -280,7 +280,7 @@ fn modify_refcount_struct<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let layout = Layout::Struct(layouts);
let layout = Layout::struct_no_name_order(layouts);
let (_, fn_name) = function_name_from_mode(
layout_ids,
@ -440,7 +440,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
}
Set(element_layout) => {
let key_layout = element_layout;
let value_layout = &Layout::Struct(&[]);
let value_layout = &Layout::UNIT;
let function = modify_refcount_dict(
env,
@ -619,8 +619,9 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
}
}
Struct(layouts) => {
let function = modify_refcount_struct(env, layout_ids, layouts, mode, when_recursive);
Struct { field_layouts, .. } => {
let function =
modify_refcount_struct(env, layout_ids, &field_layouts, mode, when_recursive);
Some(function)
}
@ -1312,7 +1313,8 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
env.builder.position_at_end(block);
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
let wrapper_type =
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
// cast the opaque pointer to a pointer of the correct shape
let struct_ptr = env
@ -1720,7 +1722,8 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
let block = env.context.append_basic_block(parent, "tag_id_modify");
env.builder.position_at_end(block);
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
let wrapper_type =
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
debug_assert!(wrapper_type.is_struct_type());
let opaque_tag_data_ptr = env