Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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,
Expand All @@ -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<DataType>,
fields: Vec<(Ident, Pattern)>,
location: Location,
expected_type: Type,
typ: Type,
definition: DefinitionKind,
mutable: Option<Location>,
new_definitions: &mut Vec<HirIdent>,
) -> 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(
Expand Down
129 changes: 128 additions & 1 deletion compiler/noirc_frontend/src/tests/assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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#"
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Loading