mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-23 14:35:12 +00:00
Lose rigidity of annotated optional fields before generalization
We have this idea of "rigid optional" fields to annotate record fields that must necessarily be optional. That avoids the admission of programs we cannot faithfully compile, like ``` f : {a: Str, b ? U64} f = {a: "b", b: 1} ``` We want to lose the rigidity restriction when a generalized symbol is used as at a specialized site; for example it should be possible to call `f : {x ? Str} -> {}` with both `{}` and `{x : Str}`, neither of which have a rigidly optional field unless they were to be annotated. Prior to this commit we would loosen the rigidity restriction upon specialization of a generalized type at a use site. However, what we really want to do is apply the loosening during calculation of generalization. The reason is that otherwise, we must make types that would be ground (like `{x ? Str} -> {}`) generalized just for the sake of the optional field annotation. But since the rigidity constraint is irrelevant after an annotated body has been checked, we can loosen the rigidity restriction then, which conveniently happens to coincide with the generalization calculation. Closes #3955
This commit is contained in:
parent
1a9a0d5d01
commit
c2452ff751
3 changed files with 24 additions and 22 deletions
|
@ -3124,9 +3124,16 @@ fn adjust_rank_content(
|
|||
Record(fields, ext_var) => {
|
||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *ext_var);
|
||||
|
||||
for index in fields.iter_variables() {
|
||||
let var = subs[index];
|
||||
for (_, var_index, field_index) in fields.iter_all() {
|
||||
let var = subs[var_index];
|
||||
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||
|
||||
// When generalizing annotations with rigid optionals, we want to promote
|
||||
// them to non-rigid, so that usages at specialized sites don't have to
|
||||
// exactly include the optional field.
|
||||
if let RecordField::RigidOptional(()) = subs[field_index] {
|
||||
subs[field_index] = RecordField::Optional(());
|
||||
}
|
||||
}
|
||||
|
||||
rank
|
||||
|
@ -3499,7 +3506,7 @@ fn deep_copy_var_help(
|
|||
let slice = SubsSlice::extend_new(
|
||||
&mut subs.record_fields,
|
||||
field_types.into_iter().map(|f| match f {
|
||||
RecordField::RigidOptional(()) => RecordField::Optional(()),
|
||||
RecordField::RigidOptional(()) => internal_error!("RigidOptionals should be generalized to non-rigid by this point"),
|
||||
|
||||
RecordField::Demanded(_)
|
||||
| RecordField::Required(_)
|
||||
|
|
|
@ -7761,4 +7761,18 @@ mod solve_expr {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unify_optional_record_fields_in_two_closed_records() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x ? Str, y ? Str } -> {}
|
||||
|
||||
f {x : ""}
|
||||
"#
|
||||
),
|
||||
"{}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue