diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 68e13688b1c..c76fee835e7 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -8,7 +8,8 @@ use crate::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, ItemVisibility, Lambda, Literal, MemberAccessExpression, MethodCallExpression, Path, - PrefixExpression, StatementKind, UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, + PathSegment, PrefixExpression, StatementKind, UnaryOp, UnresolvedTypeData, + UnresolvedTypeExpression, }, hir::{ comptime::{self, InterpreterError}, @@ -603,6 +604,12 @@ impl<'context> Elaborator<'context> { if let UnresolvedTypeData::Interned(id) = typ { typ = self.interner.get_unresolved_type_data(id).clone(); } + if let UnresolvedTypeData::Resolved(id) = typ { + // If this type is already resolved we can skip the rest of this function + // which just resolves the type, and go straight to resolving the fields. + let resolved = self.interner.get_quoted_type(id).clone(); + return self.elaborate_constructor_with_type(resolved, constructor.fields, span, None); + } let UnresolvedTypeData::Named(mut path, generics, _) = typ else { self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), span }); return (HirExpression::Error, Type::Error); @@ -614,58 +621,78 @@ impl<'context> Elaborator<'context> { } let last_segment = path.last_segment(); - let is_self_type = last_segment.ident.is_self_type_name(); - let (r#type, struct_generics) = if let Some(struct_id) = constructor.struct_type { + let typ = if let Some(struct_id) = constructor.struct_type { let typ = self.interner.get_type(struct_id); let generics = typ.borrow().instantiate(self.interner); - (typ, generics) + Type::DataType(typ, generics) } else { match self.lookup_type_or_error(path) { - Some(Type::DataType(r#type, struct_generics)) if r#type.borrow().is_struct() => { - (r#type, struct_generics) - } - Some(typ) => { - self.push_err(ResolverError::NonStructUsedInConstructor { - typ: typ.to_string(), - span, - }); - return (HirExpression::Error, Type::Error); - } + Some(typ) => typ, None => return (HirExpression::Error, Type::Error), } }; - self.mark_struct_as_constructed(r#type.clone()); + self.elaborate_constructor_with_type(typ, constructor.fields, span, Some(last_segment)) + } - let turbofish_span = last_segment.turbofish_span(); + fn elaborate_constructor_with_type( + &mut self, + typ: Type, + fields: Vec<(Ident, Expression)>, + span: Span, + last_segment: Option, + ) -> (HirExpression, Type) { + let typ = typ.follow_bindings_shallow(); + let (r#type, generics) = match typ.as_ref() { + Type::DataType(r#type, struct_generics) if r#type.borrow().is_struct() => { + (r#type, struct_generics) + } + typ => { + self.push_err(ResolverError::NonStructUsedInConstructor { + typ: typ.to_string(), + span, + }); + return (HirExpression::Error, Type::Error); + } + }; + self.mark_struct_as_constructed(r#type.clone()); - let struct_generics = self.resolve_struct_turbofish_generics( - &r#type.borrow(), - struct_generics, - last_segment.generics, - turbofish_span, - ); + // `last_segment` is optional if this constructor was resolved from a quoted type + let mut generics = generics.clone(); + let mut is_self_type = false; + let mut constructor_type_span = span; + + if let Some(last_segment) = last_segment { + let turbofish_span = last_segment.turbofish_span(); + is_self_type = last_segment.ident.is_self_type_name(); + constructor_type_span = last_segment.ident.span(); + + generics = self.resolve_struct_turbofish_generics( + &r#type.borrow(), + generics, + last_segment.generics, + turbofish_span, + ); + } let struct_type = r#type.clone(); - let generics = struct_generics.clone(); - let fields = constructor.fields; let field_types = r#type .borrow() - .get_fields_with_visibility(&struct_generics) + .get_fields_with_visibility(&generics) .expect("This type should already be validated to be a struct"); let fields = self.resolve_constructor_expr_fields(struct_type.clone(), field_types, fields, span); let expr = HirExpression::Constructor(HirConstructorExpression { fields, - r#type, - struct_generics, + r#type: struct_type.clone(), + struct_generics: generics.clone(), }); let struct_id = struct_type.borrow().id; - let reference_location = Location::new(last_segment.ident.span(), self.file); + let reference_location = Location::new(constructor_type_span, self.file); self.interner.add_type_reference(struct_id, reference_location, is_self_type); (expr, Type::DataType(struct_type, generics)) diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index de8e4f6f864..8788f7284cb 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -777,13 +777,12 @@ impl<'interner> Monomorphizer<'interner> { } /// For an enum like: - /// ``` /// enum Foo { /// A(i32, u32), /// B(Field), /// C /// } - /// ``` + /// /// this will translate the call `Foo::A(1, 2)` into `(0, (1, 2), (0,), ())` where /// the first field `0` is the tag value, the second is `A`, third is `B`, and fourth is `C`. /// Each variant that isn't the desired variant has zeroed values filled in for its data. diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 15be2155a15..825d61de126 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -295,11 +295,13 @@ impl<'a> Parser<'a> { } // A constructor where the type is an interned unresolved type data is valid - if matches!(self.token.token(), Token::InternedUnresolvedTypeData(..)) - && self.next_is(Token::LeftBrace) + if matches!( + self.token.token(), + Token::InternedUnresolvedTypeData(..) | Token::QuotedType(..) + ) && self.next_is(Token::LeftBrace) { let span = self.current_token_span; - let typ = self.parse_interned_type().unwrap(); + let typ = self.parse_interned_type().or_else(|| self.parse_resolved_type()).unwrap(); self.eat_or_error(Token::LeftBrace); let typ = UnresolvedType { typ, span }; return Some(self.parse_constructor(typ)); diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index f325c7e60ca..f7b4b81ad91 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -316,7 +316,7 @@ impl<'a> Parser<'a> { Some(UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path))) } - fn parse_resolved_type(&mut self) -> Option { + pub(super) fn parse_resolved_type(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::QuotedType) { match token.into_token() { Token::QuotedType(id) => { diff --git a/test_programs/compile_success_empty/resolved_type_in_constructor/Nargo.toml b/test_programs/compile_success_empty/resolved_type_in_constructor/Nargo.toml new file mode 100644 index 00000000000..2b26a729f87 --- /dev/null +++ b/test_programs/compile_success_empty/resolved_type_in_constructor/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "resolved_type_in_constructor" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/resolved_type_in_constructor/src/main.nr b/test_programs/compile_success_empty/resolved_type_in_constructor/src/main.nr new file mode 100644 index 00000000000..5f1cbfd490f --- /dev/null +++ b/test_programs/compile_success_empty/resolved_type_in_constructor/src/main.nr @@ -0,0 +1,10 @@ +fn main() { + let _ = my_macro!(); +} + +comptime fn my_macro() -> Quoted { + let typ = quote { Foo }.as_type(); + quote [$typ {}] +} + +struct Foo {}