Turn invalid record field types into runtime errors

By emitting a runtime error rather than panicking when we can't layout
a record, we help programs like

```
main =
    get = \{a} -> a
    get {b: "hello world"}
```

execute as

```
Mismatch in compiler/unify/src/unify.rs Line 1071 Column 13
Trying to unify two flat types that are incompatible: EmptyRecord ~ { 'a' : Demanded(122), }<130>

🔨 Rebuilding host...
── TYPE MISMATCH ───────────────────────────────────────────────────────────────

The 1st argument to get is not what I expect:

8│      get {b: "hello world"}
            ^^^^^^^^^^^^^^^^^^

This argument is a record of type:

    { b : Str }

But get needs the 1st argument to be:

    { a : a }b

Tip: Seems like a record field typo. Maybe a should be b?

Tip: Can more type annotations be added? Type annotations always help
me give more specific messages, and I think they could help a lot in
this case

────────────────────────────────────────────────────────────────────────────────

'+fast-variable-shuffle' is not a recognized feature for this target (ignoring feature)
'+fast-variable-shuffle' is not a recognized feature for this target (ignoring feature)
Done!
Application crashed with message

    Can't create record with improper layout

Shutting down
```

rather than the hanging

```
Mismatch in compiler/unify/src/unify.rs Line 1071 Column 13
Trying to unify two flat types that are incompatible: EmptyRecord ~ { 'a' : Demanded(122), }<130>

thread '<unnamed>' panicked at 'invalid layout from var: UnresolvedTypeVar(104)', compiler/mono/s
rc/layout.rs:1510:52
```

that was previously produced.

Part of #2227
This commit is contained in:
ayazhafiz 2021-12-21 19:11:59 -06:00
parent e728e319c6
commit 576f1293fd
7 changed files with 96 additions and 51 deletions

View file

@ -519,7 +519,8 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin
let RecordStructure {
fields: sorted_fields,
ext,
} = gather_fields(subs, *fields, *ext_var);
} = gather_fields(subs, *fields, *ext_var)
.expect("Something ended up weird in this record type");
let ext_var = ext;
if fields.is_empty() {

View file

@ -1968,7 +1968,8 @@ impl RecordFields {
subs: &'a Subs,
ext: Variable,
) -> impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + 'a {
let (it, _) = crate::types::gather_fields_unsorted_iter(subs, *self, ext);
let (it, _) = crate::types::gather_fields_unsorted_iter(subs, *self, ext)
.expect("Something weird ended up in a record type");
it
}
@ -2003,7 +2004,8 @@ impl RecordFields {
ext,
)
} else {
let record_structure = crate::types::gather_fields(subs, *self, ext);
let record_structure = crate::types::gather_fields(subs, *self, ext)
.expect("Something ended up weird in this record type");
(
Box::new(record_structure.fields.into_iter()),

View file

@ -1701,14 +1701,20 @@ pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lower
}
}
#[derive(Debug, Copy, Clone)]
pub struct RecordFieldsError;
pub fn gather_fields_unsorted_iter(
subs: &Subs,
other_fields: RecordFields,
mut var: Variable,
) -> (
impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + '_,
Variable,
) {
) -> Result<
(
impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + '_,
Variable,
),
RecordFieldsError,
> {
use crate::subs::Content::*;
use crate::subs::FlatType::*;
@ -1733,7 +1739,7 @@ pub fn gather_fields_unsorted_iter(
// TODO investigate apparently this one pops up in the reporting tests!
RigidVar(_) => break,
other => unreachable!("something weird ended up in a record type: {:?}", other),
_ => return Err(RecordFieldsError),
}
}
@ -1749,11 +1755,15 @@ pub fn gather_fields_unsorted_iter(
(field_name, record_field)
});
(it, var)
Ok((it, var))
}
pub fn gather_fields(subs: &Subs, other_fields: RecordFields, var: Variable) -> RecordStructure {
let (it, ext) = gather_fields_unsorted_iter(subs, other_fields, var);
pub fn gather_fields(
subs: &Subs,
other_fields: RecordFields,
var: Variable,
) -> Result<RecordStructure, RecordFieldsError> {
let (it, ext) = gather_fields_unsorted_iter(subs, other_fields, var)?;
let mut result: Vec<_> = it
.map(|(ref_label, field)| (ref_label.clone(), field))
@ -1761,10 +1771,10 @@ pub fn gather_fields(subs: &Subs, other_fields: RecordFields, var: Variable) ->
result.sort_by(|(a, _), (b, _)| a.cmp(b));
RecordStructure {
Ok(RecordStructure {
fields: result,
ext,
}
})
}
pub fn gather_tags_unsorted_iter(