fix(check): record field access properly restricted to single constr types

This commit is contained in:
rvcas 2023-03-08 23:36:13 -05:00 committed by Lucas
parent ba4635a8b0
commit 553eb88d3d
3 changed files with 9 additions and 29 deletions

View File

@ -9,6 +9,7 @@
### Changed ### Changed
- **aiken-lang**: block `Data` and `String` from unifying when casting - **aiken-lang**: block `Data` and `String` from unifying when casting
- **aiken-lang**: remove ability for a type with many variants with matching field labels and types to support field access
- **aiken-project**: tests filtering with `-m` during check now happens in `Project::collect_tests` - **aiken-project**: tests filtering with `-m` during check now happens in `Project::collect_tests`
## [v0.0.29] - 2023-MM-DD ## [v0.0.29] - 2023-MM-DD

View File

@ -177,6 +177,7 @@ impl<'a> Environment<'a> {
}, },
); );
} }
Ok(Some(fields)) Ok(Some(fields))
} }
@ -1780,44 +1781,22 @@ fn get_compatible_record_fields<A>(
) -> Vec<(usize, &str, &Annotation)> { ) -> Vec<(usize, &str, &Annotation)> {
let mut compatible = vec![]; let mut compatible = vec![];
if constructors.len() > 1 {
return compatible;
}
let first = match constructors.get(0) { let first = match constructors.get(0) {
Some(first) => first, Some(first) => first,
None => return compatible, None => return compatible,
}; };
'next_argument: for (index, first_argument) in first.arguments.iter().enumerate() { for (index, first_argument) in first.arguments.iter().enumerate() {
// Fields without labels do not have accessors // Fields without labels do not have accessors
let label = match first_argument.label.as_ref() { let label = match first_argument.label.as_ref() {
Some(label) => label.as_str(), Some(label) => label.as_str(),
None => continue 'next_argument, None => continue,
}; };
// Check each variant to see if they have an field in the same position
// with the same label and the same type
for constructor in constructors.iter().skip(1) {
// The field must exist in all variants
let argument = match constructor.arguments.get(index) {
Some(argument) => argument,
None => continue 'next_argument,
};
// The labels must be the same
if argument.label != first_argument.label {
continue 'next_argument;
}
// The types must be the same
if !argument
.annotation
.is_logically_equal(&first_argument.annotation)
{
continue 'next_argument;
}
}
// The previous loop did not find any incompatible fields in the other
// variants so this field is compatible across variants and we should
// generate an accessor for it.
compatible.push((index, label, &first_argument.annotation)) compatible.push((index, label, &first_argument.annotation))
} }

View File

@ -745,7 +745,7 @@ Perhaps, try the following:
#[diagnostic(code("unknown::record_field"))] #[diagnostic(code("unknown::record_field"))]
#[diagnostic(help( #[diagnostic(help(
"{}", "{}",
suggest_neighbor(label, fields.iter(), "Did you forget to make it public?") suggest_neighbor(label, fields.iter(), "Did you forget to make it public?\n\nAlso record access is only supported on types with one constructor.")
))] ))]
UnknownRecordField { UnknownRecordField {
#[label] #[label]