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

@ -1125,7 +1125,7 @@ impl<'a> Param<'a> {
pub const EMPTY: Self = Param {
symbol: Symbol::EMPTY_PARAM,
borrow: false,
layout: Layout::Struct(&[]),
layout: Layout::UNIT,
};
}
@ -2436,7 +2436,7 @@ fn specialize_external<'a>(
let closure_data_layout = match opt_closure_layout {
Some(lambda_set) => Layout::LambdaSet(lambda_set),
None => Layout::Struct(&[]),
None => Layout::UNIT,
};
// I'm not sure how to handle the closure case, does it ever occur?
@ -3985,7 +3985,7 @@ pub fn with_hole<'a>(
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
let field_layouts = match &record_layout {
Layout::Struct(layouts) => *layouts,
Layout::Struct { field_layouts, .. } => *field_layouts,
other => arena.alloc([*other]),
};
@ -4701,7 +4701,9 @@ fn construct_closure_data<'a>(
Vec::from_iter_in(combined.iter().map(|(_, b)| **b), env.arena).into_bump_slice();
debug_assert_eq!(
Layout::Struct(field_layouts),
// NB: this may be wrong! If it comes up, we may need to hash the closure
// argument name order.
Layout::struct_no_name_order(field_layouts,),
lambda_set.runtime_representation()
);
@ -4785,9 +4787,7 @@ fn convert_tag_union<'a>(
"The `[]` type has no constructors, source var {:?}",
variant_var
),
Unit | UnitWithArguments => {
Stmt::Let(assigned, Expr::Struct(&[]), Layout::Struct(&[]), hole)
}
Unit | UnitWithArguments => Stmt::Let(assigned, Expr::Struct(&[]), Layout::UNIT, hole),
BoolUnion { ttrue, .. } => Stmt::Let(
assigned,
Expr::Literal(Literal::Bool(tag_name == ttrue)),
@ -5096,7 +5096,7 @@ fn sorted_field_symbols<'a>(
// Note it does not catch the use of `[]` currently.
use roc_can::expr::Expr;
arg.value = Expr::RuntimeError(RuntimeError::VoidValue);
Layout::Struct(&[])
Layout::UNIT
}
Err(LayoutProblem::Erroneous) => {
// something went very wrong
@ -5191,7 +5191,10 @@ fn register_capturing_closure<'a>(
Content::Structure(FlatType::Func(_, closure_var, _)) => {
match LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info) {
Ok(lambda_set) => {
if let Layout::Struct(&[]) = lambda_set.runtime_representation() {
if let Layout::Struct {
field_layouts: &[], ..
} = lambda_set.runtime_representation()
{
CapturedSymbols::None
} else {
let mut temp = Vec::from_iter_in(captured_symbols, env.arena);
@ -6255,7 +6258,7 @@ fn store_pattern_help<'a>(
let mut fields = Vec::with_capacity_in(arguments.len(), env.arena);
fields.extend(arguments.iter().map(|x| x.1));
let layout = Layout::Struct(fields.into_bump_slice());
let layout = Layout::struct_no_name_order(fields.into_bump_slice());
return store_newtype_pattern(
env,
@ -6676,7 +6679,7 @@ fn force_thunk<'a>(
}
fn let_empty_struct<'a>(assigned: Symbol, hole: &'a Stmt<'a>) -> Stmt<'a> {
Stmt::Let(assigned, Expr::Struct(&[]), Layout::Struct(&[]), hole)
Stmt::Let(assigned, Expr::Struct(&[]), Layout::UNIT, hole)
}
/// If the symbol is a function, make sure it is properly specialized
@ -8457,7 +8460,7 @@ where
env.arena.alloc(result),
)
}
Layout::Struct(_) => match lambda_set.set.get(0) {
Layout::Struct { .. } => match lambda_set.set.get(0) {
Some((function_symbol, _)) => {
let call_spec_id = env.next_call_specialization_id();
let update_mode = env.next_update_mode_id();
@ -8630,7 +8633,10 @@ fn match_on_lambda_set<'a>(
env.arena.alloc(result),
)
}
Layout::Struct(fields) => {
Layout::Struct {
field_layouts,
field_order_hash,
} => {
let function_symbol = lambda_set.set[0].0;
union_lambda_set_branch_help(
@ -8638,7 +8644,10 @@ fn match_on_lambda_set<'a>(
function_symbol,
lambda_set,
closure_data_symbol,
Layout::Struct(fields),
Layout::Struct {
field_layouts,
field_order_hash,
},
argument_symbols,
argument_layouts,
return_layout,
@ -8797,7 +8806,9 @@ fn union_lambda_set_branch_help<'a>(
hole: &'a Stmt<'a>,
) -> Stmt<'a> {
let (argument_layouts, argument_symbols) = match closure_data_layout {
Layout::Struct(&[])
Layout::Struct {
field_layouts: &[], ..
}
| Layout::Builtin(Builtin::Bool)
| Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
(argument_layouts_slice, argument_symbols_slice)
@ -8924,7 +8935,9 @@ fn enum_lambda_set_branch<'a>(
let assigned = result_symbol;
let (argument_layouts, argument_symbols) = match closure_data_layout {
Layout::Struct(&[])
Layout::Struct {
field_layouts: &[], ..
}
| Layout::Builtin(Builtin::Bool)
| Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
(argument_layouts_slice, argument_symbols_slice)