From aa85564a6c11ae97e070bec6e4cc110c0b521bff Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 24 Jun 2024 15:32:55 -0500 Subject: [PATCH 1/3] Fix type tokenization --- compiler/noirc_frontend/src/ast/mod.rs | 5 ++++ .../noirc_frontend/src/elaborator/types.rs | 1 + .../src/hir/comptime/interpreter/builtin.rs | 23 ++----------------- .../src/hir/resolution/resolver.rs | 1 + compiler/noirc_frontend/src/lexer/token.rs | 11 +++++++++ .../noirc_frontend/src/parser/parser/types.rs | 14 ++++++++++- tooling/nargo_fmt/src/rewrite/typ.rs | 2 ++ 7 files changed, 35 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index a1ae349b537..b1383b219e5 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -119,6 +119,10 @@ pub enum UnresolvedTypeData { // The type of quoted code for metaprogramming Quoted(crate::QuotedType), + /// An already resolved type. These can only be parsed if they were present in the token stream + /// as a result of being spliced into a macro's token stream input. + Resolved(crate::Type), + Unspecified, // This is for when the user declares a variable without specifying it's type Error, } @@ -221,6 +225,7 @@ impl std::fmt::Display for UnresolvedTypeData { Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), Parenthesized(typ) => write!(f, "({typ})"), + Resolved(typ) => write!(f, "{typ}"), } } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index fcb7ac94c26..b93074d468e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -109,6 +109,7 @@ impl<'context> Elaborator<'context> { Type::MutableReference(Box::new(self.resolve_type_inner(*element))) } Parenthesized(typ) => self.resolve_type_inner(*typ), + Resolved(typ) => typ, }; if let Type::Struct(_, _) = resolved_type { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index cccc9c6d545..f413ea4d19a 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -4,7 +4,6 @@ use noirc_errors::Location; use crate::{ hir::comptime::{errors::IResult, InterpreterError, Value}, - lexer::Lexer, macros_api::NodeInterner, token::{SpannedToken, Token, Tokens}, QuotedType, Type, @@ -145,7 +144,8 @@ fn type_def_fields( for (name, typ) in struct_def.get_fields_as_written() { let name = make_quoted(vec![make_token(name)]); - let typ = Value::Code(Rc::new(type_to_tokens(&typ)?)); + let typ = SpannedToken::new(Token::QuotedType(typ), span); + let typ = Value::Code(Rc::new(Tokens(vec![typ]))); fields.push_back(Value::Tuple(vec![name, typ])); } @@ -155,22 +155,3 @@ fn type_def_fields( ]))); Ok(Value::Slice(fields, typ)) } - -/// FIXME(https://github.com/noir-lang/noir/issues/5309): This code is temporary. -/// It will produce poor results for type variables and will result in incorrect -/// spans on the returned tokens. -fn type_to_tokens(typ: &Type) -> IResult { - let (mut tokens, mut errors) = Lexer::lex(&typ.to_string()); - - if let Some(last) = tokens.0.last() { - if matches!(last.token(), Token::EOF) { - tokens.0.pop(); - } - } - - if !errors.is_empty() { - let error = errors.swap_remove(0); - todo!("Got lexer error: {error}") - } - Ok(tokens) -} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 5706e62e193..9a90b620441 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -607,6 +607,7 @@ impl<'a> Resolver<'a> { Type::MutableReference(Box::new(self.resolve_type_inner(*element))) } Parenthesized(typ) => self.resolve_type_inner(*typ), + Resolved(typ) => typ, }; if let Type::Struct(_, _) = resolved_type { diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 6830ee528d6..706c786f75c 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -24,6 +24,7 @@ pub enum BorrowedToken<'input> { LineComment(&'input str, Option), BlockComment(&'input str, Option), Quote(&'input Tokens), + QuotedType(&'input Type), /// < Less, /// <= @@ -125,6 +126,11 @@ pub enum Token { BlockComment(String, Option), // A `quote { ... }` along with the tokens in its token stream. Quote(Tokens), + /// A quoted type resulting from a `Type` object in noir code being + /// spliced into a macro's token stream. We preserve the original type + /// to avoid having to tokenize it, re-parse it, and re-resolve it which + /// may change the underlying type. + QuotedType(Type), /// < Less, /// <= @@ -223,6 +229,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::LineComment(ref s, _style) => BorrowedToken::LineComment(s, *_style), Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), Token::Quote(stream) => BorrowedToken::Quote(stream), + Token::QuotedType(ref typ) => BorrowedToken::QuotedType(typ), Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, @@ -343,6 +350,7 @@ impl fmt::Display for Token { } write!(f, "}}") } + Token::QuotedType(ref typ) => write!(f, "{typ}"), Token::IntType(ref i) => write!(f, "{i}"), Token::Less => write!(f, "<"), Token::LessEqual => write!(f, "<="), @@ -394,6 +402,7 @@ pub enum TokenKind { Keyword, Attribute, Quote, + QuotedType, UnquoteMarker, } @@ -406,6 +415,7 @@ impl fmt::Display for TokenKind { TokenKind::Keyword => write!(f, "keyword"), TokenKind::Attribute => write!(f, "attribute"), TokenKind::Quote => write!(f, "quote"), + TokenKind::QuotedType => write!(f, "quoted type"), TokenKind::UnquoteMarker => write!(f, "macro result"), } } @@ -424,6 +434,7 @@ impl Token { Token::Attribute(_) => TokenKind::Attribute, Token::UnquoteMarker(_) => TokenKind::UnquoteMarker, Token::Quote(_) => TokenKind::Quote, + Token::QuotedType(_) => TokenKind::QuotedType, tok => TokenKind::Token(tok.clone()), } } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 14840bafa04..523720ac967 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,3 +1,4 @@ +use super::primitives::token_kind; use super::{ expression_with_precedence, keyword, nothing, parenthesized, path, NoirParser, ParserError, ParserErrorReason, Precedence, @@ -6,7 +7,7 @@ use crate::ast::{Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedType use crate::QuotedType; use crate::parser::labels::ParsingRuleLabel; -use crate::token::{Keyword, Token}; +use crate::token::{Keyword, Token, TokenKind}; use chumsky::prelude::*; use noirc_errors::Span; @@ -28,6 +29,7 @@ pub(super) fn parse_type_inner<'a>( top_level_item_type(), type_of_quoted_types(), quoted_type(), + resolved_type(), format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), @@ -105,6 +107,16 @@ fn quoted_type() -> impl NoirParser { .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Quoted).with_span(span)) } +/// This is the type of an already resolved type. +/// The only way this can appear in the token input is if an already resolved `Type` object +/// was spliced into a macro's token stream via the `$` operator. +fn resolved_type() -> impl NoirParser { + token_kind(TokenKind::QuotedType).map_with_span(|token, span| match token { + Token::QuotedType(typ) => UnresolvedTypeData::Resolved(typ).with_span(span), + _ => unreachable!("token_kind(QuotedType) guarantees we parse a quoted type"), + }) +} + pub(super) fn string_type() -> impl NoirParser { keyword(Keyword::String) .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index 3eb398346c3..a97380c0156 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -55,6 +55,8 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) format!("fn{env}({args}) -> {return_type}") } + UnresolvedTypeData::Resolved(typ) => typ.to_string(), + UnresolvedTypeData::Unspecified => todo!(), UnresolvedTypeData::FieldElement | UnresolvedTypeData::Integer(_, _) From 5763f21ed148e12e7260f75bd1d7fd9b85886dde Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 25 Jun 2024 10:16:20 -0500 Subject: [PATCH 2/3] Intern QuotedTypeId --- compiler/noirc_frontend/src/ast/mod.rs | 6 +++--- .../noirc_frontend/src/elaborator/types.rs | 2 +- .../src/hir/comptime/interpreter.rs | 3 ++- .../src/hir/comptime/interpreter/builtin.rs | 7 ++++--- .../src/hir/resolution/resolver.rs | 2 +- compiler/noirc_frontend/src/lexer/token.rs | 11 ++++++----- compiler/noirc_frontend/src/node_interner.rs | 18 ++++++++++++++++++ .../noirc_frontend/src/parser/parser/types.rs | 2 +- tooling/nargo_fmt/src/rewrite/typ.rs | 4 +++- 9 files changed, 39 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index b1383b219e5..5ce2fcd4872 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -24,7 +24,7 @@ pub use type_alias::*; use crate::{ parser::{ParserError, ParserErrorReason}, token::IntType, - BinaryTypeOperator, + BinaryTypeOperator, node_interner::QuotedTypeId, }; use acvm::acir::AcirField; use iter_extended::vecmap; @@ -121,7 +121,7 @@ pub enum UnresolvedTypeData { /// An already resolved type. These can only be parsed if they were present in the token stream /// as a result of being spliced into a macro's token stream input. - Resolved(crate::Type), + Resolved(QuotedTypeId), Unspecified, // This is for when the user declares a variable without specifying it's type Error, @@ -225,7 +225,7 @@ impl std::fmt::Display for UnresolvedTypeData { Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), Parenthesized(typ) => write!(f, "({typ})"), - Resolved(typ) => write!(f, "{typ}"), + Resolved(_) => write!(f, "(resolved type)"), } } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b93074d468e..8314a437be3 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -109,7 +109,7 @@ impl<'context> Elaborator<'context> { Type::MutableReference(Box::new(self.resolve_type_inner(*element))) } Parenthesized(typ) => self.resolve_type_inner(*typ), - Resolved(typ) => typ, + Resolved(id) => self.interner.get_quoted_type(id).clone(), }; if let Type::Struct(_, _) = resolved_type { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 5e236a2b980..ccfd446d6ac 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -98,7 +98,8 @@ impl<'a> Interpreter<'a> { .expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); if let Some(builtin) = func_attrs.builtin() { - builtin::call_builtin(self.interner, builtin, arguments, location) + let builtin = builtin.clone(); + builtin::call_builtin(self.interner, &builtin, arguments, location) } else if let Some(foreign) = func_attrs.foreign() { let item = format!("Comptime evaluation for foreign functions like {foreign}"); Err(InterpreterError::Unimplemented { item, location }) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index f413ea4d19a..b69ada611f1 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -10,7 +10,7 @@ use crate::{ }; pub(super) fn call_builtin( - interner: &NodeInterner, + interner: &mut NodeInterner, name: &str, arguments: Vec<(Value, Location)>, location: Location, @@ -123,7 +123,7 @@ fn type_def_generics( /// fn fields(self) -> [(Quoted, Quoted)] /// Returns (name, type) pairs of each field of this TypeDefinition fn type_def_fields( - interner: &NodeInterner, + interner: &mut NodeInterner, mut arguments: Vec<(Value, Location)>, ) -> IResult { assert_eq!(arguments.len(), 1, "ICE: `generics` should only receive a single argument"); @@ -144,7 +144,8 @@ fn type_def_fields( for (name, typ) in struct_def.get_fields_as_written() { let name = make_quoted(vec![make_token(name)]); - let typ = SpannedToken::new(Token::QuotedType(typ), span); + let id = interner.push_quoted_type(typ); + let typ = SpannedToken::new(Token::QuotedType(id), span); let typ = Value::Code(Rc::new(Tokens(vec![typ]))); fields.push_back(Value::Tuple(vec![name, typ])); } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 9a90b620441..5af7ceb315c 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -607,7 +607,7 @@ impl<'a> Resolver<'a> { Type::MutableReference(Box::new(self.resolve_type_inner(*element))) } Parenthesized(typ) => self.resolve_type_inner(*typ), - Resolved(typ) => typ, + Resolved(id) => self.interner.get_quoted_type(id).clone(), }; if let Type::Struct(_, _) = resolved_type { diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 706c786f75c..6b0b897a7db 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -2,7 +2,7 @@ use acvm::{acir::AcirField, FieldElement}; use noirc_errors::{Position, Span, Spanned}; use std::{fmt, iter::Map, vec::IntoIter}; -use crate::{lexer::errors::LexerErrorKind, node_interner::ExprId}; +use crate::{lexer::errors::LexerErrorKind, node_interner::{ExprId, QuotedTypeId}}; /// Represents a token in noir's grammar - a word, number, /// or symbol that can be used in noir's syntax. This is the @@ -24,7 +24,7 @@ pub enum BorrowedToken<'input> { LineComment(&'input str, Option), BlockComment(&'input str, Option), Quote(&'input Tokens), - QuotedType(&'input Type), + QuotedType(QuotedTypeId), /// < Less, /// <= @@ -130,7 +130,7 @@ pub enum Token { /// spliced into a macro's token stream. We preserve the original type /// to avoid having to tokenize it, re-parse it, and re-resolve it which /// may change the underlying type. - QuotedType(Type), + QuotedType(QuotedTypeId), /// < Less, /// <= @@ -229,7 +229,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::LineComment(ref s, _style) => BorrowedToken::LineComment(s, *_style), Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), Token::Quote(stream) => BorrowedToken::Quote(stream), - Token::QuotedType(ref typ) => BorrowedToken::QuotedType(typ), + Token::QuotedType(id) => BorrowedToken::QuotedType(*id), Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, @@ -350,7 +350,8 @@ impl fmt::Display for Token { } write!(f, "}}") } - Token::QuotedType(ref typ) => write!(f, "{typ}"), + // Quoted types only have an ID so there is nothing to display + Token::QuotedType(_) => write!(f, "(type)"), Token::IntType(ref i) => write!(f, "{i}"), Token::Less => write!(f, "<"), Token::LessEqual => write!(f, "<="), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 78b2c056dfd..b73e7b67550 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -176,6 +176,12 @@ pub struct NodeInterner { /// Stores the [Location] of a [Type] reference pub(crate) type_ref_locations: Vec<(Type, Location)>, + + /// In Noir's metaprogramming, a noir type has the type `Type`. When these are spliced + /// into `quoted` expressions, we preserve the original type by assigning it a unique id + /// and creating a `Token::QuotedType(id)` from this id. We cannot create a token holding + /// the actual type since types do not implement Send or Sync. + quoted_types: noirc_arena::Arena, } /// A dependency in the dependency graph may be a type or a definition. @@ -472,6 +478,9 @@ pub struct GlobalInfo { pub value: Option, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct QuotedTypeId(noirc_arena::Index); + impl Default for NodeInterner { fn default() -> Self { let mut interner = NodeInterner { @@ -506,6 +515,7 @@ impl Default for NodeInterner { primitive_methods: HashMap::new(), type_alias_ref: Vec::new(), type_ref_locations: Vec::new(), + quoted_types: Default::default(), }; // An empty block expression is used often, we add this into the `node` on startup @@ -1738,6 +1748,14 @@ impl NodeInterner { cycle } + + pub fn push_quoted_type(&mut self, typ: Type) -> QuotedTypeId { + QuotedTypeId(self.quoted_types.insert(typ)) + } + + pub fn get_quoted_type(&self, id: QuotedTypeId) -> &Type { + &self.quoted_types[id.0] + } } impl Methods { diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 523720ac967..493ebd1fb2f 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -112,7 +112,7 @@ fn quoted_type() -> impl NoirParser { /// was spliced into a macro's token stream via the `$` operator. fn resolved_type() -> impl NoirParser { token_kind(TokenKind::QuotedType).map_with_span(|token, span| match token { - Token::QuotedType(typ) => UnresolvedTypeData::Resolved(typ).with_span(span), + Token::QuotedType(id) => UnresolvedTypeData::Resolved(id).with_span(span), _ => unreachable!("token_kind(QuotedType) guarantees we parse a quoted type"), }) } diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index a97380c0156..fa7668c7912 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -55,7 +55,9 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) format!("fn{env}({args}) -> {return_type}") } - UnresolvedTypeData::Resolved(typ) => typ.to_string(), + UnresolvedTypeData::Resolved(_) => { + unreachable!("Unexpected macro expansion of a type in nargo fmt input") + }, UnresolvedTypeData::Unspecified => todo!(), UnresolvedTypeData::FieldElement From 1e6a1e180e975580bfa4691f44f2c6d6278aab95 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 25 Jun 2024 10:28:19 -0500 Subject: [PATCH 3/3] Format --- compiler/noirc_frontend/src/ast/mod.rs | 3 ++- compiler/noirc_frontend/src/lexer/token.rs | 5 ++++- tooling/nargo_fmt/src/rewrite/typ.rs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index ca81369397d..dfe4258744a 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -22,9 +22,10 @@ pub use traits::*; pub use type_alias::*; use crate::{ + node_interner::QuotedTypeId, parser::{ParserError, ParserErrorReason}, token::IntType, - BinaryTypeOperator, node_interner::QuotedTypeId, + BinaryTypeOperator, }; use acvm::acir::AcirField; use iter_extended::vecmap; diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 6b0b897a7db..f98343ba52f 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -2,7 +2,10 @@ use acvm::{acir::AcirField, FieldElement}; use noirc_errors::{Position, Span, Spanned}; use std::{fmt, iter::Map, vec::IntoIter}; -use crate::{lexer::errors::LexerErrorKind, node_interner::{ExprId, QuotedTypeId}}; +use crate::{ + lexer::errors::LexerErrorKind, + node_interner::{ExprId, QuotedTypeId}, +}; /// Represents a token in noir's grammar - a word, number, /// or symbol that can be used in noir's syntax. This is the diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index fa7668c7912..3298ed8ae73 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -57,7 +57,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) } UnresolvedTypeData::Resolved(_) => { unreachable!("Unexpected macro expansion of a type in nargo fmt input") - }, + } UnresolvedTypeData::Unspecified => todo!(), UnresolvedTypeData::FieldElement