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:
Ayaz Hafiz 2022-09-06 17:42:38 -05:00
parent 1a9a0d5d01
commit c2452ff751
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
3 changed files with 24 additions and 22 deletions

View file

@ -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(_)

View file

@ -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 : ""}
"#
),
"{}",
);
}
}