Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow resolved types in constructors #7223

Merged
merged 3 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
83 changes: 55 additions & 28 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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);
Expand All @@ -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<PathSegment>,
) -> (HirExpression, Type) {
let typ = typ.follow_bindings_shallow();
let (r#type, struct_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 = struct_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(struct_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))
Expand Down
8 changes: 5 additions & 3 deletions compiler/noirc_frontend/src/parser/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,13 @@
}

// 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));
Expand Down Expand Up @@ -599,7 +601,7 @@
/// = bool
/// | int
/// | str
/// | rawstr

Check warning on line 604 in compiler/noirc_frontend/src/parser/parser/expression.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (rawstr)
/// | fmtstr
/// | QuoteExpression
/// | ArrayExpression
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/parser/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
Some(UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path)))
}

fn parse_resolved_type(&mut self) -> Option<UnresolvedTypeData> {
pub(super) fn parse_resolved_type(&mut self) -> Option<UnresolvedTypeData> {
if let Some(token) = self.eat_kind(TokenKind::QuotedType) {
match token.into_token() {
Token::QuotedType(id) => {
Expand Down Expand Up @@ -501,10 +501,10 @@
for quoted_type in QuotedType::iter() {
let src = quoted_type.to_string();
let typ = parse_type_no_errors(&src);
let UnresolvedTypeData::Quoted(parsed_qouted_type) = typ.typ else {

Check warning on line 504 in compiler/noirc_frontend/src/parser/parser/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (qouted)
panic!("Expected a quoted type for {}", quoted_type)
};
assert_eq!(parsed_qouted_type, quoted_type);

Check warning on line 507 in compiler/noirc_frontend/src/parser/parser/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (qouted)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "resolved_type_in_constructor"
type = "bin"
authors = [""]

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn main() {
let _ = my_macro!();
}

comptime fn my_macro() -> Quoted {
let typ = quote { Foo }.as_type();
quote [$typ {}]
}

struct Foo {}
Loading