diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 7c566c01722..9eb08e08ef4 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -5,7 +5,7 @@ use noirc_errors::{Located, Location}; use rustc_hash::FxHashSet as HashSet; use crate::{ - DataType, Kind, Shared, Type, TypeAlias, + DataType, Kind, Type, TypeAlias, ast::{ERROR_IDENT, Ident, ItemVisibility, Path, PathSegment, Pattern}, elaborator::Turbofish, hir::{ @@ -264,12 +264,10 @@ impl Elaborator<'_> { source: Source::Assignment, }); - let typ = struct_type.clone(); let fields = self.resolve_constructor_pattern_fields( - typ, fields, location, - expected_type.clone(), + actual_type.clone(), definition, mutable, new_definitions, @@ -284,32 +282,33 @@ impl Elaborator<'_> { self.interner.add_struct_member_reference(struct_id, field_index, reference_location); } - HirPattern::Struct(expected_type, fields, location) + HirPattern::Struct(actual_type, fields, location) } /// Resolve all the fields of a struct constructor expression. /// Ensures all fields are present, none are repeated, and all /// are part of the struct. - #[allow(clippy::too_many_arguments)] fn resolve_constructor_pattern_fields( &mut self, - struct_type: Shared, fields: Vec<(Ident, Pattern)>, location: Location, - expected_type: Type, + typ: Type, definition: DefinitionKind, mutable: Option, new_definitions: &mut Vec, ) -> Vec<(Ident, HirPattern)> { let mut ret = Vec::with_capacity(fields.len()); let mut seen_fields = HashSet::default(); + let Type::DataType(struct_type, _) = &typ else { + unreachable!("Should be validated as struct before getting here") + }; let mut unseen_fields = struct_type .borrow() .field_names() .expect("This type should already be validated to be a struct"); for (field, pattern) in fields { - let (field_type, visibility) = expected_type + let (field_type, visibility) = typ .get_field_type_and_visibility(field.as_str()) .unwrap_or((Type::Error, ItemVisibility::Public)); let resolved = self.elaborate_pattern_mut( diff --git a/compiler/noirc_frontend/src/tests/assignment.rs b/compiler/noirc_frontend/src/tests/assignment.rs index c488b76b5fc..49e786e4e37 100644 --- a/compiler/noirc_frontend/src/tests/assignment.rs +++ b/compiler/noirc_frontend/src/tests/assignment.rs @@ -401,6 +401,131 @@ fn nested_struct_patterns() { assert_no_errors(src); } +#[test] +fn struct_pattern_with_mismatched_type() { + let src = r#" + struct Foo { x: Field } + struct Bar { y: u32 } + + fn main() { + let value: Bar = Bar { y: 5 }; + let Foo { x } = value; + ^^^^^^^^^ Cannot assign an expression of type Foo to a value of type Bar + // x should have type Field (from Foo), so the next line should error + let _check: u32 = x; + ^ Expected type u32, found type Field + } + "#; + check_errors(src); +} + +#[test] +fn struct_pattern_with_non_struct_type() { + let src = r#" + fn main() { + let x: Field = 1; + let MyStruct { a: _, b: _ } = x; + ^^^^^^^^^^^^^^^^^^^^^^^ Cannot assign an expression of type MyStruct to a value of type Field + } + + struct MyStruct { + a: Field, + b: Field, + } + "#; + check_errors(src); +} + +#[test] +fn struct_pattern_with_error_type() { + let src = r#" + fn main() { + let value = MyStruct { x: 1, y: 2 }; + let MyStruct { x: _, y: _ }: DoesNotExist = value; + ^^^^^^^^^^^^ Could not resolve 'DoesNotExist' in path + } + + struct MyStruct { + x: Field, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn struct_pattern_with_error_type_and_missing_fields() { + let src = r#" + fn main() { + let value = MyStruct { x: 1, y: 2 }; + let MyStruct { x: _ }: DoesNotExist = value; + ^^^^^^^^^^^^ Could not resolve 'DoesNotExist' in path + ^^^^^^^^^^^^^^^^^ missing field y in struct MyStruct + } + + struct MyStruct { + x: Field, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn struct_pattern_with_nonexistent_struct() { + let src = r#" + fn main() { + let value = (1, 2); + let NonExistent { x, y } = value; + ^^^^^^^^^^^ Could not resolve 'NonExistent' in path + } + "#; + check_errors(src); +} + +#[test] +fn struct_pattern_with_non_struct_type_name() { + let src = r#" + fn main() { + let x = 5; + let Field { value } = x; + ^^^^^ expected type got primitive type + } + "#; + check_errors(src); +} + +#[test] +fn parenthesized_pattern() { + let src = r#" + fn main() { + // Parenthesized identifier + let (x) = 5; + assert(x == 5); + + // Parenthesized tuple pattern + let ((a, b)) = (1, 2); + assert(a == 1); + assert(b == 2); + + // Multiple levels of parentheses + let (((c))) = 10; + assert(c == 10); + + // Parenthesized struct pattern + let (Foo { x, y }) = Foo { x: 1, y: 2 }; + assert(x == 1); + assert(y == 2); + } + + struct Foo { + x: Field, + y: Field, + } + "#; + assert_no_errors(src); +} + #[test] fn tuple_pattern_arity_mismatch() { let src = r#" @@ -415,7 +540,7 @@ fn tuple_pattern_arity_mismatch() { } #[test] -fn tuple_pattern_for_non_tuple_type() { +fn tuple_pattern_with_non_tuple_type() { let src = r#" fn main() { let x: Field = 1; @@ -428,6 +553,8 @@ fn tuple_pattern_for_non_tuple_type() { #[test] fn tuple_pattern_with_error_type() { + // When the expected type is Error (from a previous type resolution failure), + // we should not issue confusing cascading errors about invalid fields. let src = r#" fn main() { let x = 1;