diff --git a/.github/benchmark_projects.yml b/.github/benchmark_projects.yml index e7f0713dd68..61dee6890cc 100644 --- a/.github/benchmark_projects.yml +++ b/.github/benchmark_projects.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT b67afe429d431c7516b95f9c6d1b6e358907ef33 +define: &AZ_COMMIT 9f0dcc87221b4ccbe61713a77a52527176bad535 projects: private-kernel-inner: repo: AztecProtocol/aztec-packages diff --git a/EXTERNAL_NOIR_LIBRARIES.yml b/EXTERNAL_NOIR_LIBRARIES.yml index 17f2f6a4819..211c86d398f 100644 --- a/EXTERNAL_NOIR_LIBRARIES.yml +++ b/EXTERNAL_NOIR_LIBRARIES.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT b67afe429d431c7516b95f9c6d1b6e358907ef33 +define: &AZ_COMMIT 9f0dcc87221b4ccbe61713a77a52527176bad535 libraries: noir_check_shuffle: repo: noir-lang/noir_check_shuffle diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 66e02603f37..03620e368cf 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -7,6 +7,7 @@ use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, }; +use crate::elaborator::PrimitiveType; use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; use crate::shared::Visibility; use crate::signed_field::SignedField; @@ -116,17 +117,24 @@ impl UnresolvedGeneric { &self, typ: &UnresolvedType, ) -> Result { - use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; - - match typ.typ { - FieldElement => Ok(Type::FieldElement), - Integer(sign, bits) => Ok(Type::Integer(sign, bits)), - // Only fields and integers are supported for numeric kinds - _ => Err(UnsupportedNumericGenericType { - ident: self.ident().clone(), - typ: typ.typ.clone(), - }), + // TODO: this should be done with resolved types + // See https://github.com/noir-lang/noir/issues/8504 + use crate::ast::UnresolvedTypeData::Named; + + if let Named(path, _generics, _) = &typ.typ { + if path.segments.len() == 1 { + if let Some(primitive_type) = + PrimitiveType::lookup_by_name(path.segments[0].ident.as_str()) + { + if let Some(typ) = primitive_type.to_integer_or_field() { + return Ok(typ); + } + } + } } + + // Only fields and integers are supported for numeric kinds + Err(UnsupportedNumericGenericType { ident: self.ident().clone(), typ: typ.typ.clone() }) } pub(crate) fn ident(&self) -> &Ident { diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 3ad6d180815..809b6070f3e 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -33,12 +33,12 @@ pub use structure::*; pub use traits::*; pub use type_alias::*; +use crate::QuotedType; use crate::{ BinaryTypeOperator, node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, shared::Signedness, - token::IntType, }; use acvm::acir::AcirField; @@ -122,14 +122,9 @@ impl core::fmt::Display for IntegerBitSize { /// for structs within, but are otherwise identical to Types. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeData { - FieldElement, Array(UnresolvedTypeExpression, Box), // [Field; 4] = Array(4, Field) Slice(Box), - Integer(Signedness, IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo) - Bool, Expression(UnresolvedTypeExpression), - String(UnresolvedTypeExpression), - FormatString(UnresolvedTypeExpression, Box), Unit, Parenthesized(Box), @@ -153,9 +148,6 @@ pub enum UnresolvedTypeData { /*unconstrained:*/ bool, ), - /// The type of quoted code for metaprogramming - Quoted(crate::QuotedType), - /// An "as Trait" path leading to an associated type. /// E.g. `::Bar` AsTraitPath(Box), @@ -281,13 +273,8 @@ impl std::fmt::Display for UnresolvedTypeData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use UnresolvedTypeData::*; match self { - FieldElement => write!(f, "Field"), Array(len, typ) => write!(f, "[{typ}; {len}]"), Slice(typ) => write!(f, "[{typ}]"), - Integer(sign, num_bits) => match sign { - Signedness::Signed => write!(f, "i{num_bits}"), - Signedness::Unsigned => write!(f, "u{num_bits}"), - }, Named(s, args, _) => write!(f, "{s}{args}"), TraitAsType(s, args) => write!(f, "impl {s}{args}"), Tuple(elements) => { @@ -295,9 +282,6 @@ impl std::fmt::Display for UnresolvedTypeData { write!(f, "({})", elements.join(", ")) } Expression(expression) => expression.fmt(f), - Bool => write!(f, "bool"), - String(len) => write!(f, "str<{len}>"), - FormatString(len, elements) => write!(f, "fmtstr<{len}, {elements}>"), Function(args, ret, env, unconstrained) => { if *unconstrained { write!(f, "unconstrained ")?; @@ -318,7 +302,6 @@ impl std::fmt::Display for UnresolvedTypeData { } Reference(element, false) => write!(f, "&{element}"), Reference(element, true) => write!(f, "&mut {element}"), - Quoted(quoted) => write!(f, "{}", quoted), Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), @@ -385,24 +368,82 @@ impl UnresolvedType { } impl UnresolvedTypeData { - pub fn from_int_token( - token: IntType, - ) -> Result { - use {IntType::*, UnresolvedTypeData::Integer}; - match token { - Signed(num_bits) => { - if num_bits == 128 { - Err(InvalidIntegerBitSizeError(128)) - } else if num_bits == 1 { - Err(InvalidIntegerBitSizeError(1)) - } else { - Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) - } - } - Unsigned(num_bits) => { - Ok(Integer(Signedness::Unsigned, IntegerBitSize::try_from(num_bits)?)) - } - } + pub fn bool(location: Location) -> Self { + Self::named("bool".to_string(), location) + } + + pub fn integer(signedness: Signedness, size: IntegerBitSize, location: Location) -> Self { + let name = match signedness { + Signedness::Signed => match size { + IntegerBitSize::One => "i1", + IntegerBitSize::Eight => "i8", + IntegerBitSize::Sixteen => "i16", + IntegerBitSize::ThirtyTwo => "i32", + IntegerBitSize::SixtyFour => "i64", + IntegerBitSize::HundredTwentyEight => "i128", + }, + Signedness::Unsigned => match size { + IntegerBitSize::One => "u1", + IntegerBitSize::Eight => "u8", + IntegerBitSize::Sixteen => "u16", + IntegerBitSize::ThirtyTwo => "u32", + IntegerBitSize::SixtyFour => "u64", + IntegerBitSize::HundredTwentyEight => "u128", + }, + }; + Self::named(name.to_string(), location) + } + + pub fn field(location: Location) -> Self { + Self::named("Field".to_string(), location) + } + + pub fn quoted(quoted: QuotedType, location: Location) -> Self { + Self::named(quoted.to_string(), location) + } + + pub fn str(length: UnresolvedTypeExpression, location: Location) -> Self { + let ident = Ident::new("str".to_string(), location); + let path = Path::from_ident(ident); + Self::Named( + path, + GenericTypeArgs { + ordered_args: vec![UnresolvedType { + typ: UnresolvedTypeData::Expression(length), + location, + }], + named_args: vec![], + kinds: vec![GenericTypeArgKind::Ordered], + }, + false, + ) + } + + pub fn fmtstr( + length: UnresolvedTypeExpression, + element: UnresolvedType, + location: Location, + ) -> Self { + let ident = Ident::new("str".to_string(), location); + let path = Path::from_ident(ident); + Self::Named( + path, + GenericTypeArgs { + ordered_args: vec![ + UnresolvedType { typ: UnresolvedTypeData::Expression(length), location }, + element, + ], + named_args: vec![], + kinds: vec![GenericTypeArgKind::Ordered], + }, + false, + ) + } + + fn named(name: String, location: Location) -> Self { + let ident = Ident::new(name, location); + let path = Path::from_ident(ident); + Self::Named(path, GenericTypeArgs::default(), false) } pub fn with_location(&self, location: Location) -> UnresolvedType { @@ -420,10 +461,6 @@ impl UnresolvedTypeData { } UnresolvedTypeData::Slice(typ) => typ.contains_unspecified(), UnresolvedTypeData::Expression(expr) => expr.contains_unspecified(), - UnresolvedTypeData::String(length) => length.contains_unspecified(), - UnresolvedTypeData::FormatString(typ, length) => { - typ.contains_unspecified() || length.contains_unspecified() - } UnresolvedTypeData::Parenthesized(typ) => typ.contains_unspecified(), UnresolvedTypeData::Named(path, args, _is_synthesized) => { // '_' is unspecified @@ -442,11 +479,7 @@ impl UnresolvedTypeData { } UnresolvedTypeData::Unspecified => true, - UnresolvedTypeData::FieldElement - | UnresolvedTypeData::Integer(_, _) - | UnresolvedTypeData::Bool - | UnresolvedTypeData::Unit - | UnresolvedTypeData::Quoted(_) + UnresolvedTypeData::Unit | UnresolvedTypeData::AsTraitPath(_) | UnresolvedTypeData::Resolved(_) | UnresolvedTypeData::Interned(_) diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 456068e22fa..e85b3589a1f 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use noirc_errors::Span; use crate::{ - BinaryTypeOperator, ParsedModule, QuotedType, + BinaryTypeOperator, ParsedModule, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, @@ -25,10 +25,10 @@ use crate::{ }; use super::{ - ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, - MatchExpression, NoirEnumeration, Pattern, Signedness, TraitBound, TraitImplItemKind, TypePath, - UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, - UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, + ForBounds, FunctionReturnType, GenericTypeArgs, ItemVisibility, MatchExpression, + NoirEnumeration, Pattern, TraitBound, TraitImplItemKind, TypePath, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, UnsafeExpression, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -424,29 +424,8 @@ pub trait Visitor { true } - fn visit_format_string_type( - &mut self, - _: &UnresolvedTypeExpression, - _: &UnresolvedType, - _: Span, - ) -> bool { - true - } - - fn visit_string_type(&mut self, _: &UnresolvedTypeExpression, _: Span) -> bool { - true - } - fn visit_unspecified_type(&mut self, _: Span) {} - fn visit_quoted_type(&mut self, _: &QuotedType, _: Span) {} - - fn visit_field_element_type(&mut self, _: Span) {} - - fn visit_integer_type(&mut self, _: Signedness, _: IntegerBitSize, _: Span) {} - - fn visit_bool_type(&mut self, _: Span) {} - fn visit_unit_type(&mut self, _: Span) {} fn visit_resolved_type(&mut self, _: QuotedTypeId, _: Span) {} @@ -1466,26 +1445,7 @@ impl UnresolvedType { expr.accept(visitor); } } - UnresolvedTypeData::FormatString(expr, typ) => { - if visitor.visit_format_string_type(expr, typ, self.location.span) { - expr.accept(visitor); - typ.accept(visitor); - } - } - UnresolvedTypeData::String(expr) => { - if visitor.visit_string_type(expr, self.location.span) { - expr.accept(visitor); - } - } UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.location.span), - UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.location.span), - UnresolvedTypeData::FieldElement => { - visitor.visit_field_element_type(self.location.span); - } - UnresolvedTypeData::Integer(signdness, size) => { - visitor.visit_integer_type(*signdness, *size, self.location.span); - } - UnresolvedTypeData::Bool => visitor.visit_bool_type(self.location.span), UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span), UnresolvedTypeData::Resolved(id) => { visitor.visit_resolved_type(*id, self.location.span); diff --git a/compiler/noirc_frontend/src/elaborator/enums.rs b/compiler/noirc_frontend/src/elaborator/enums.rs index 40c6339c365..1ab595a9baa 100644 --- a/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/compiler/noirc_frontend/src/elaborator/enums.rs @@ -707,10 +707,12 @@ impl Elaborator<'_> { PathResolutionItem::Module(_) | PathResolutionItem::Type(_) | PathResolutionItem::TypeAlias(_) + | PathResolutionItem::PrimitiveType(_) | PathResolutionItem::Trait(_) | PathResolutionItem::ModuleFunction(_) | PathResolutionItem::TypeAliasFunction(_, _, _) - | PathResolutionItem::TraitFunction(_, _, _) => { + | PathResolutionItem::TraitFunction(_, _, _) + | PathResolutionItem::PrimitiveFunction(..) => { // This variable refers to an existing item if let Some(name) = name { // If name is set, shadow the existing item diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 33a7f45b14d..89d008fbf0e 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -57,6 +57,7 @@ mod lints; mod options; mod path_resolution; mod patterns; +mod primitive_types; mod scope; mod statements; mod trait_impls; @@ -77,6 +78,7 @@ use types::bind_ordered_generics; use self::traits::check_trait_impl_method_matches_declaration; pub(crate) use path_resolution::{TypedPath, TypedPathSegment}; +pub use primitive_types::PrimitiveType; /// ResolverMetas are tagged onto each definition to track how many times they are used #[derive(Debug, PartialEq, Eq)] diff --git a/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/compiler/noirc_frontend/src/elaborator/path_resolution.rs index 2e605d069b4..fe3543c5d36 100644 --- a/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -13,6 +13,7 @@ use crate::node_interner::{FuncId, GlobalId, TraitId, TypeAliasId, TypeId}; use crate::{Shared, Type, TypeAlias}; use super::Elaborator; +use super::primitive_types::PrimitiveType; use super::types::SELF_TYPE_NAME; #[derive(Debug)] @@ -30,6 +31,7 @@ pub(crate) enum PathResolutionItem { Module(ModuleId), Type(TypeId), TypeAlias(TypeAliasId), + PrimitiveType(PrimitiveType), Trait(TraitId), // These are values @@ -39,6 +41,7 @@ pub(crate) enum PathResolutionItem { SelfMethod(FuncId), TypeAliasFunction(TypeAliasId, Option, FuncId), TraitFunction(TraitId, Option, FuncId), + PrimitiveFunction(PrimitiveType, Option, FuncId), } impl PathResolutionItem { @@ -48,10 +51,12 @@ impl PathResolutionItem { | PathResolutionItem::Method(_, _, func_id) | PathResolutionItem::SelfMethod(func_id) | PathResolutionItem::TypeAliasFunction(_, _, func_id) - | PathResolutionItem::TraitFunction(_, _, func_id) => Some(*func_id), + | PathResolutionItem::TraitFunction(_, _, func_id) + | PathResolutionItem::PrimitiveFunction(_, _, func_id) => Some(*func_id), PathResolutionItem::Module(..) | PathResolutionItem::Type(..) | PathResolutionItem::TypeAlias(..) + | PathResolutionItem::PrimitiveType(..) | PathResolutionItem::Trait(..) | PathResolutionItem::Global(..) => None, } @@ -62,13 +67,15 @@ impl PathResolutionItem { PathResolutionItem::Module(..) => "module", PathResolutionItem::Type(..) => "type", PathResolutionItem::TypeAlias(..) => "type alias", + PathResolutionItem::PrimitiveType(..) => "primitive type", PathResolutionItem::Trait(..) => "trait", PathResolutionItem::Global(..) => "global", PathResolutionItem::ModuleFunction(..) | PathResolutionItem::Method(..) | PathResolutionItem::SelfMethod(..) | PathResolutionItem::TypeAliasFunction(..) - | PathResolutionItem::TraitFunction(..) => "function", + | PathResolutionItem::TraitFunction(..) + | PathResolutionItem::PrimitiveFunction(..) => "function", } } } @@ -374,12 +381,14 @@ impl Elaborator<'_> { } PathResolutionItem::Type(..) | PathResolutionItem::TypeAlias(..) + | PathResolutionItem::PrimitiveType(..) | PathResolutionItem::Trait(..) | PathResolutionItem::ModuleFunction(..) | PathResolutionItem::Method(..) | PathResolutionItem::SelfMethod(..) | PathResolutionItem::TypeAliasFunction(..) - | PathResolutionItem::TraitFunction(..) => (), + | PathResolutionItem::TraitFunction(..) + | PathResolutionItem::PrimitiveFunction(..) => (), } resolution }) @@ -400,16 +409,27 @@ impl Elaborator<'_> { } else { None }; - let (path, module_id, _) = - resolve_path_kind(path, importing_module, self.def_maps, references_tracker)?; - self.resolve_name_in_module( - path, - module_id, - importing_module, - intermediate_item, - target, - mode, - ) + let res = + resolve_path_kind(path.clone(), importing_module, self.def_maps, references_tracker); + match res { + Ok((path, module_id, _)) => self.resolve_name_in_module( + path, + module_id, + importing_module, + intermediate_item, + target, + mode, + ), + Err(PathResolutionError::Unresolved(err)) => { + if let Some(result) = + self.resolve_primitive_type_or_function(path, importing_module) + { + return result; + } + Err(PathResolutionError::Unresolved(err)) + } + Err(error) => Err(error), + } } /// Resolves a Path assuming we are inside `starting_module`. @@ -719,6 +739,103 @@ impl Elaborator<'_> { let per_ns = PerNs { types: None, values: Some(*item) }; MethodLookupResult::FoundTraitMethod(per_ns, name.clone()) } + + fn resolve_primitive_type_or_function( + &mut self, + path: TypedPath, + importing_module_id: ModuleId, + ) -> Option { + if path.segments.len() != 1 && path.segments.len() != 2 { + return None; + } + + let object_name = path.segments[0].ident.as_str(); + let turbofish = path.segments[0].turbofish(); + let primitive_type = PrimitiveType::lookup_by_name(object_name)?; + let typ = primitive_type.to_type(); + let mut errors = Vec::new(); + + if primitive_type == PrimitiveType::StructDefinition { + errors.push(PathResolutionError::StructDefinitionDeprecated { + location: path.segments[0].ident.location(), + }); + } + + if path.segments.len() == 1 { + let item = PathResolutionItem::PrimitiveType(primitive_type); + return Some(Ok(PathResolution { item, errors })); + } + + let method_name_ident = &path.segments[1].ident; + let method_name = method_name_ident.as_str(); + + // Note: the logic here is similar to that of resolve_method, except that that one works by + // searching through modules, and this one works by searching through primitive types. + // It would be nice to refactor this to a common logic though it's a bit hard. + // That said, the logic is "just" searching through direct methods, then through trait methods + // checking which ones are in scope, and is unlikely to change. + + if let Some(func_id) = self.interner.lookup_direct_method(&typ, method_name, false) { + let item = PathResolutionItem::PrimitiveFunction(primitive_type, turbofish, func_id); + return Some(Ok(PathResolution { item, errors })); + } + + let starting_module = self.get_module(importing_module_id); + + let trait_methods = self.interner.lookup_trait_methods(&typ, method_name, false); + + let mut results = Vec::new(); + for (func_id, trait_id) in &trait_methods { + if let Some(name) = starting_module.find_trait_in_scope(*trait_id) { + results.push((*trait_id, *func_id, name)); + }; + } + + if results.is_empty() { + if trait_methods.len() == 1 { + // This is the backwards-compatible case where there's a single trait method but it's not in scope + let (func_id, trait_id) = trait_methods.first().expect("Expected an item"); + let trait_ = self.interner.get_trait(*trait_id); + let trait_name = self.fully_qualified_trait_path(trait_); + let ident = method_name_ident.clone(); + errors.push(PathResolutionError::TraitMethodNotInScope { ident, trait_name }); + let item = + PathResolutionItem::PrimitiveFunction(primitive_type, turbofish, *func_id); + return Some(Ok(PathResolution { item, errors })); + } else { + let trait_ids = vecmap(trait_methods, |(_, trait_id)| trait_id); + if trait_ids.is_empty() { + return Some(Err(PathResolutionError::Unresolved(method_name_ident.clone()))); + } else { + let traits = vecmap(trait_ids, |trait_id| { + self.fully_qualified_trait_path(self.interner.get_trait(trait_id)) + }); + let ident = method_name_ident.clone(); + let error = + PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, traits }; + return Some(Err(error)); + } + } + } + + if results.len() > 1 { + let trait_ids = vecmap(results, |(trait_id, _, name)| (trait_id, name.clone())); + let traits = vecmap(trait_ids, |(trait_id, name)| { + let trait_ = self.interner.get_trait(trait_id); + self.usage_tracker.mark_as_used(importing_module_id, &name); + self.fully_qualified_trait_path(trait_) + }); + let ident = method_name_ident.clone(); + let error = PathResolutionError::MultipleTraitsInScope { ident, traits }; + return Some(Err(error)); + } + + let (_, func_id, _) = results.remove(0); + Some(Ok(PathResolution { + item: PathResolutionItem::PrimitiveFunction(primitive_type, turbofish, func_id), + errors, + })) + } } fn merge_intermediate_path_resolution_item_with_module_def_id( diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 2a0b46dc794..4ab132036b9 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -11,7 +11,10 @@ use crate::{ hir::{ def_collector::dc_crate::CompilationError, resolution::{errors::ResolverError, import::PathResolutionError}, - type_check::{Source, TypeCheckError}, + type_check::{ + Source, TypeCheckError, + generics::{FmtstrPrimitiveType, Generic, StrPrimitiveType}, + }, }, hir_def::{ expr::{HirExpression, HirIdent, HirLiteral, HirMethodReference, ImplKind, TraitMethod}, @@ -25,7 +28,7 @@ use crate::{ }; use super::{ - Elaborator, ResolverMeta, + Elaborator, PrimitiveType, ResolverMeta, path_resolution::{PathResolutionItem, TypedPath, TypedPathSegment}, }; @@ -793,11 +796,89 @@ impl Elaborator<'_> { generics.location, ) } + PathResolutionItem::PrimitiveFunction(primitive_type, turbofish, _func_id) => { + match primitive_type { + PrimitiveType::Bool + | PrimitiveType::CtString + | PrimitiveType::Expr + | PrimitiveType::Field + | PrimitiveType::FunctionDefinition + | PrimitiveType::I8 + | PrimitiveType::I16 + | PrimitiveType::I32 + | PrimitiveType::I64 + | PrimitiveType::U1 + | PrimitiveType::U8 + | PrimitiveType::U16 + | PrimitiveType::U32 + | PrimitiveType::U64 + | PrimitiveType::U128 + | PrimitiveType::Module + | PrimitiveType::Quoted + | PrimitiveType::StructDefinition + | PrimitiveType::TraitConstraint + | PrimitiveType::TraitDefinition + | PrimitiveType::TraitImpl + | PrimitiveType::TypeDefinition + | PrimitiveType::TypedExpr + | PrimitiveType::Type + | PrimitiveType::UnresolvedType => { + if let Some(turbofish) = turbofish { + self.push_err(CompilationError::TypeError( + TypeCheckError::GenericCountMismatch { + item: primitive_type.name().to_string(), + expected: 0, + found: turbofish.generics.len(), + location: turbofish.location, + }, + )); + } + Vec::new() + } + PrimitiveType::Str => { + if let Some(turbofish) = turbofish { + let item = StrPrimitiveType; + let item_generic_kinds = item.generic_kinds(self.interner); + let kind = item_generic_kinds[0].clone(); + let generics = vec![self.interner.next_type_variable_with_kind(kind)]; + self.resolve_item_turbofish_generics( + item.item_kind(), + &item.item_name(self.interner), + item_generic_kinds, + generics, + Some(turbofish.generics), + turbofish.location, + ) + } else { + Vec::new() + } + } + PrimitiveType::Fmtstr => { + if let Some(turbofish) = turbofish { + let item_generic_kinds = + FmtstrPrimitiveType {}.generic_kinds(self.interner); + let kind = item_generic_kinds[0].clone(); + let generics = vec![self.interner.next_type_variable_with_kind(kind)]; + self.resolve_item_turbofish_generics( + "primitive type", + "fmtstr", + item_generic_kinds, + generics, + Some(turbofish.generics), + turbofish.location, + ) + } else { + Vec::new() + } + } + } + } PathResolutionItem::Method(_, None, _) | PathResolutionItem::TraitFunction(_, None, _) | PathResolutionItem::Module(..) | PathResolutionItem::Type(..) | PathResolutionItem::TypeAlias(..) + | PathResolutionItem::PrimitiveType(..) | PathResolutionItem::Trait(..) | PathResolutionItem::Global(..) | PathResolutionItem::ModuleFunction(..) => Vec::new(), @@ -810,26 +891,26 @@ impl Elaborator<'_> { self.push_err(error); } - ( + return ( HirIdent { location: path.location, id: self.interner.trait_method_id(trait_path_resolution.method.method_id), impl_kind: ImplKind::TraitMethod(trait_path_resolution.method), }, trait_path_resolution.item, - ) - } else { - // If the Path is being used as an Expression, then it is referring to a global from a separate module - // Otherwise, then it is referring to an Identifier - // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; - // If the expression is a singular indent, we search the resolver's current scope as normal. - let location = path.location; - let ((hir_ident, var_scope_index), item) = self.get_ident_from_path(path); + ); + } - self.handle_hir_ident(&hir_ident, var_scope_index, location); + // If the Path is being used as an Expression, then it is referring to a global from a separate module + // Otherwise, then it is referring to an Identifier + // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; + // If the expression is a singular indent, we search the resolver's current scope as normal. + let location = path.location; + let ((hir_ident, var_scope_index), item) = self.get_ident_from_path(path); - (hir_ident, item) - } + self.handle_hir_ident(&hir_ident, var_scope_index, location); + + (hir_ident, item) } pub(crate) fn handle_hir_ident( diff --git a/compiler/noirc_frontend/src/elaborator/primitive_types.rs b/compiler/noirc_frontend/src/elaborator/primitive_types.rs new file mode 100644 index 00000000000..88a0695b454 --- /dev/null +++ b/compiler/noirc_frontend/src/elaborator/primitive_types.rs @@ -0,0 +1,166 @@ +use crate::{QuotedType, Type, ast::IntegerBitSize, shared::Signedness}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, strum_macros::EnumIter)] +pub enum PrimitiveType { + Bool, + CtString, + Expr, + Field, + Fmtstr, + FunctionDefinition, + I8, + I16, + I32, + I64, + U1, + U8, + U16, + U32, + U64, + U128, + Module, + Quoted, + Str, + StructDefinition, + TraitConstraint, + TraitDefinition, + TraitImpl, + TypeDefinition, + TypedExpr, + Type, + UnresolvedType, +} + +impl PrimitiveType { + pub fn lookup_by_name(name: &str) -> Option { + match name { + "bool" => Some(Self::Bool), + "CtString" => Some(Self::CtString), + "Expr" => Some(Self::Expr), + "fmtstr" => Some(Self::Fmtstr), + "Field" => Some(Self::Field), + "FunctionDefinition" => Some(Self::FunctionDefinition), + "i8" => Some(Self::I8), + "i16" => Some(Self::I16), + "i32" => Some(Self::I32), + "i64" => Some(Self::I64), + "u1" => Some(Self::U1), + "u8" => Some(Self::U8), + "u16" => Some(Self::U16), + "u32" => Some(Self::U32), + "u64" => Some(Self::U64), + "u128" => Some(Self::U128), + "Module" => Some(Self::Module), + "Quoted" => Some(Self::Quoted), + "str" => Some(Self::Str), + "StructDefinition" => Some(Self::StructDefinition), + "TraitConstraint" => Some(Self::TraitConstraint), + "TraitDefinition" => Some(Self::TraitDefinition), + "TraitImpl" => Some(Self::TraitImpl), + "TypeDefinition" => Some(Self::TypeDefinition), + "TypedExpr" => Some(Self::TypedExpr), + "Type" => Some(Self::Type), + "UnresolvedType" => Some(Self::UnresolvedType), + _ => None, + } + } + + pub fn to_type(self) -> Type { + match self { + Self::Bool => Type::Bool, + Self::CtString => Type::Quoted(QuotedType::CtString), + Self::Expr => Type::Quoted(QuotedType::Expr), + Self::Fmtstr => Type::FmtString(Box::new(Type::Error), Box::new(Type::Error)), + Self::Field => Type::FieldElement, + Self::FunctionDefinition => Type::Quoted(QuotedType::FunctionDefinition), + Self::I8 => Type::Integer(Signedness::Signed, IntegerBitSize::Eight), + Self::I16 => Type::Integer(Signedness::Signed, IntegerBitSize::Sixteen), + Self::I32 => Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo), + Self::I64 => Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour), + Self::U1 => Type::Integer(Signedness::Unsigned, IntegerBitSize::One), + Self::U8 => Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + Self::U16 => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen), + Self::U32 => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), + Self::U64 => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Self::U128 => Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight), + Self::Module => Type::Quoted(QuotedType::Module), + Self::Quoted => Type::Quoted(QuotedType::Quoted), + Self::Str => Type::String(Box::new(Type::Error)), + Self::TraitConstraint => Type::Quoted(QuotedType::TraitConstraint), + Self::TraitDefinition => Type::Quoted(QuotedType::TraitDefinition), + Self::TraitImpl => Type::Quoted(QuotedType::TraitImpl), + Self::StructDefinition | Self::TypeDefinition => { + Type::Quoted(QuotedType::TypeDefinition) + } + Self::TypedExpr => Type::Quoted(QuotedType::TypedExpr), + Self::Type => Type::Quoted(QuotedType::Type), + Self::UnresolvedType => Type::Quoted(QuotedType::UnresolvedType), + } + } + + pub fn to_integer_or_field(self) -> Option { + match self { + Self::I8 => Some(Type::Integer(Signedness::Signed, IntegerBitSize::Eight)), + Self::I16 => Some(Type::Integer(Signedness::Signed, IntegerBitSize::Sixteen)), + Self::I32 => Some(Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo)), + Self::I64 => Some(Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour)), + Self::U1 => Some(Type::Integer(Signedness::Unsigned, IntegerBitSize::One)), + Self::U8 => Some(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), + Self::U16 => Some(Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen)), + Self::U32 => Some(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo)), + Self::U64 => Some(Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour)), + Self::U128 => { + Some(Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight)) + } + Self::Field => Some(Type::FieldElement), + Self::Bool + | Self::CtString + | Self::Expr + | Self::Fmtstr + | Self::FunctionDefinition + | Self::Module + | Self::Quoted + | Self::Str + | Self::StructDefinition + | Self::TraitConstraint + | Self::TraitDefinition + | Self::TraitImpl + | Self::TypeDefinition + | Self::TypedExpr + | Self::Type + | Self::UnresolvedType => None, + } + } + + pub fn name(&self) -> &'static str { + match self { + Self::Bool => "bool", + Self::CtString => "CtString", + Self::Expr => "Expr", + Self::Field => "Field", + Self::Fmtstr => "fmtstr", + Self::FunctionDefinition => "FunctionDefinition", + Self::I8 => "i8", + Self::I16 => "i16", + Self::I32 => "i32", + Self::I64 => "i64", + Self::U1 => "u1", + Self::U8 => "u8", + Self::U16 => "u16", + Self::U32 => "u32", + Self::U64 => "u64", + Self::U128 => "u128", + Self::Module => "Module", + Self::Quoted => "Quoted", + Self::Str => "str", + Self::StructDefinition => "StructDefinition", + Self::TraitConstraint => "TraitConstraint", + Self::TraitDefinition => "TraitDefinition", + Self::TraitImpl => "TraitImpl", + Self::TypeDefinition => "TypeDefinition", + Self::TypedExpr => "TypedExpr", + Self::Type => "Type", + Self::UnresolvedType => "UnresolvedType", + } + } +} diff --git a/compiler/noirc_frontend/src/elaborator/scope.rs b/compiler/noirc_frontend/src/elaborator/scope.rs index 09ca952bf1f..96f16bfcfdc 100644 --- a/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/compiler/noirc_frontend/src/elaborator/scope.rs @@ -157,33 +157,6 @@ impl Elaborator<'_> { } } - /// Lookup a given struct type by name. - pub(super) fn lookup_datatype_or_error( - &mut self, - path: TypedPath, - mode: PathResolutionMode, - ) -> Option> { - let location = path.location; - match self.resolve_path_or_error_inner(path, PathResolutionTarget::Type, mode) { - Ok(item) => { - if let PathResolutionItem::Type(struct_id) = item { - Some(self.get_type(struct_id)) - } else { - self.push_err(ResolverError::Expected { - expected: "type", - got: item.description(), - location, - }); - None - } - } - Err(err) => { - self.push_err(err); - None - } - } - } - /// Looks up a given type by name. /// This will also instantiate any struct types found. pub(super) fn lookup_type_or_error(&mut self, path: TypedPath) -> Option { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index edec0c65800..93608c63906 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -20,7 +20,7 @@ use crate::{ resolution::{errors::ResolverError, import::PathResolutionError}, type_check::{ NoMatchingImplFoundError, Source, TypeCheckError, - generics::{Generic, TraitGenerics}, + generics::{FmtstrPrimitiveType, Generic, StrPrimitiveType, TraitGenerics}, }, }, hir_def::{ @@ -44,6 +44,7 @@ use crate::{ use super::{ Elaborator, FunctionContext, PathResolutionTarget, UnsafeBlockStatus, lints, path_resolution::{PathResolutionItem, PathResolutionMode, TypedPath}, + primitive_types::PrimitiveType, }; pub const SELF_TYPE_NAME: &str = "Self"; @@ -109,7 +110,6 @@ impl Elaborator<'_> { }; let resolved_type = match typ.typ { - FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_with_kind_inner(*elem, kind, mode)); let size = self.convert_expression_type(size, &Kind::u32(), location); @@ -120,26 +120,6 @@ impl Elaborator<'_> { Type::Slice(elem) } Expression(expr) => self.convert_expression_type(expr, kind, location), - Integer(sign, bits) => Type::Integer(sign, bits), - Bool => Type::Bool, - String(size) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); - Type::String(Box::new(resolved_size)) - } - FormatString(size, fields) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); - let fields = self.resolve_type_with_kind_inner(*fields, kind, mode); - Type::FmtString(Box::new(resolved_size), Box::new(fields)) - } - Quoted(quoted) => { - let in_function = matches!(self.current_item, Some(DependencyId::Function(_))); - if in_function && !self.in_comptime_context() { - let location = typ.location; - let typ = quoted.to_string(); - self.push_err(ResolverError::ComptimeTypeInRuntimeCode { location, typ }); - } - Type::Quoted(quoted) - } Unit => Type::Unit, Unspecified => { let location = typ.location; @@ -261,25 +241,7 @@ impl Elaborator<'_> { // Check if the path is a type variable first. We currently disallow generics on type // variables since we do not support higher-kinded types. - if path.segments.len() == 1 { - let name = path.last_name(); - - if name == SELF_TYPE_NAME { - if let Some(self_type) = self.self_type.clone() { - if !args.is_empty() { - self.push_err(ResolverError::GenericsOnSelfType { - location: path.location, - }); - } - return self_type; - } - } else if name == WILDCARD_TYPE { - return self.interner.next_type_variable_with_kind(Kind::Any); - } - } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { - if !args.is_empty() { - self.push_err(ResolverError::GenericsOnAssociatedType { location: path.location }); - } + if let Some(typ) = self.lookup_type_variable(&path, &args) { return typ; } @@ -305,8 +267,11 @@ impl Elaborator<'_> { return Type::Alias(type_alias, args); } - match self.lookup_datatype_or_error(path, mode) { - Some(data_type) => { + let location = path.location; + match self.resolve_path_or_error_inner(path.clone(), PathResolutionTarget::Type, mode) { + Ok(PathResolutionItem::Type(type_id)) => { + let data_type = self.get_type(type_id); + if self.resolving_ids.contains(&data_type.borrow().id) { self.push_err(ResolverError::SelfReferentialType { location: data_type.borrow().name.location(), @@ -337,10 +302,127 @@ impl Elaborator<'_> { Type::DataType(data_type, args) } - None => Type::Error, + Ok(PathResolutionItem::PrimitiveType(primitive_type)) => { + let typ = self.instantiate_primitive_type(primitive_type, args, location); + if let Type::Quoted(quoted) = typ { + let in_function = matches!(self.current_item, Some(DependencyId::Function(_))); + if in_function && !self.in_comptime_context() { + let typ = quoted.to_string(); + self.push_err(ResolverError::ComptimeTypeInRuntimeCode { location, typ }); + } + } + typ + } + Ok(item) => { + self.push_err(ResolverError::Expected { + expected: "type", + got: item.description(), + location, + }); + + Type::Error + } + Err(err) => { + self.push_err(err); + + Type::Error + } } } + fn lookup_type_variable(&mut self, path: &TypedPath, args: &GenericTypeArgs) -> Option { + if path.segments.len() != 1 { + return None; + } + + let name = path.last_name(); + match name { + SELF_TYPE_NAME => { + let self_type = self.self_type.clone()?; + if !args.is_empty() { + self.push_err(ResolverError::GenericsOnSelfType { location: path.location }); + } + Some(self_type) + } + WILDCARD_TYPE => Some(self.interner.next_type_variable_with_kind(Kind::Any)), + _ => None, + } + } + + fn instantiate_primitive_type( + &mut self, + primitive_type: PrimitiveType, + args: GenericTypeArgs, + location: Location, + ) -> Type { + match primitive_type { + PrimitiveType::Bool + | PrimitiveType::CtString + | PrimitiveType::Expr + | PrimitiveType::Field + | PrimitiveType::FunctionDefinition + | PrimitiveType::I8 + | PrimitiveType::I16 + | PrimitiveType::I32 + | PrimitiveType::I64 + | PrimitiveType::U1 + | PrimitiveType::U8 + | PrimitiveType::U16 + | PrimitiveType::U32 + | PrimitiveType::U64 + | PrimitiveType::U128 + | PrimitiveType::Module + | PrimitiveType::Quoted + | PrimitiveType::StructDefinition + | PrimitiveType::TraitConstraint + | PrimitiveType::TraitDefinition + | PrimitiveType::TraitImpl + | PrimitiveType::TypeDefinition + | PrimitiveType::TypedExpr + | PrimitiveType::Type + | PrimitiveType::UnresolvedType => { + if !args.is_empty() { + let found = args.ordered_args.len() + args.named_args.len(); + self.push_err(CompilationError::TypeError( + TypeCheckError::GenericCountMismatch { + item: primitive_type.name().to_string(), + expected: 0, + found, + location, + }, + )); + } + } + PrimitiveType::Str => { + let item = StrPrimitiveType; + let (mut args, _) = self.resolve_type_args_inner( + args, + item, + location, + PathResolutionMode::MarkAsReferenced, + ); + assert_eq!(args.len(), 1); + let length = args.pop().unwrap(); + return Type::String(Box::new(length)); + } + PrimitiveType::Fmtstr => { + let item = FmtstrPrimitiveType; + let (mut args, _) = self.resolve_type_args_inner( + args, + item, + location, + PathResolutionMode::MarkAsReferenced, + ); + assert_eq!(args.len(), 2); + let element = args.pop().unwrap(); + let length = args.pop().unwrap(); + return Type::FmtString(Box::new(length), Box::new(element)); + } + } + + primitive_type.to_type() + } + fn resolve_trait_as_type( &mut self, path: TypedPath, @@ -402,7 +484,7 @@ impl Elaborator<'_> { allow_implicit_named_args: bool, mode: PathResolutionMode, ) -> (Vec, Vec) { - let expected_kinds = item.generics(self.interner); + let expected_kinds = item.generic_kinds(self.interner); if args.ordered_args.len() != expected_kinds.len() { self.push_err(TypeCheckError::GenericCountMismatch { @@ -416,9 +498,8 @@ impl Elaborator<'_> { } let ordered_args = expected_kinds.iter().zip(args.ordered_args); - let ordered = vecmap(ordered_args, |(generic, typ)| { - self.resolve_type_with_kind_inner(typ, &generic.kind(), mode) - }); + let ordered = + vecmap(ordered_args, |(kind, typ)| self.resolve_type_with_kind_inner(typ, kind, mode)); let mut associated = Vec::new(); diff --git a/compiler/noirc_frontend/src/hir/comptime/display.rs b/compiler/noirc_frontend/src/hir/comptime/display.rs index 83d907288ee..3fe887f8232 100644 --- a/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -208,11 +208,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { let value = Value::TypedExpr(TypedExpr::ExprId(*id)); self.print_value(&value, last_was_alphanumeric, f) } - Token::Keyword(..) - | Token::Ident(..) - | Token::IntType(..) - | Token::Int(..) - | Token::Bool(..) => { + Token::Keyword(..) | Token::Ident(..) | Token::Int(..) | Token::Bool(..) => { if last_was_alphanumeric { write!(f, " ")?; } @@ -874,10 +870,6 @@ fn remove_interned_in_unresolved_type_data( UnresolvedTypeData::Slice(typ) => { UnresolvedTypeData::Slice(Box::new(remove_interned_in_unresolved_type(interner, *typ))) } - UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( - expr, - Box::new(remove_interned_in_unresolved_type(interner, *typ)), - ), UnresolvedTypeData::Parenthesized(typ) => UnresolvedTypeData::Parenthesized(Box::new( remove_interned_in_unresolved_type(interner, *typ), )), @@ -920,13 +912,8 @@ fn remove_interned_in_unresolved_type_data( })) } UnresolvedTypeData::Interned(id) => interner.get_unresolved_type_data(id).clone(), - UnresolvedTypeData::FieldElement - | UnresolvedTypeData::Integer(_, _) - | UnresolvedTypeData::Bool - | UnresolvedTypeData::Unit - | UnresolvedTypeData::String(_) + UnresolvedTypeData::Unit | UnresolvedTypeData::Resolved(_) - | UnresolvedTypeData::Quoted(_) | UnresolvedTypeData::Expression(_) | UnresolvedTypeData::Unspecified | UnresolvedTypeData::Error => typ, diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index f95d4001dc9..f718c71f277 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -376,7 +376,7 @@ impl Type { /// Convert to AST for display (some details lost) fn to_display_ast(&self) -> UnresolvedType { let typ = match self { - Type::FieldElement => UnresolvedTypeData::FieldElement, + Type::FieldElement => UnresolvedTypeData::field(Location::dummy()), Type::Array(length, element) => { let length = length.to_type_expression(); let element = Box::new(element.to_display_ast()); @@ -386,16 +386,18 @@ impl Type { let element = Box::new(element.to_display_ast()); UnresolvedTypeData::Slice(element) } - Type::Integer(sign, bit_size) => UnresolvedTypeData::Integer(*sign, *bit_size), - Type::Bool => UnresolvedTypeData::Bool, + Type::Integer(sign, bit_size) => { + UnresolvedTypeData::integer(*sign, *bit_size, Location::dummy()) + } + Type::Bool => UnresolvedTypeData::bool(Location::dummy()), Type::String(length) => { let length = length.to_type_expression(); - UnresolvedTypeData::String(length) + UnresolvedTypeData::str(length, Location::dummy()) } Type::FmtString(length, element) => { let length = length.to_type_expression(); - let element = Box::new(element.to_display_ast()); - UnresolvedTypeData::FormatString(length, element) + let element = element.to_display_ast(); + UnresolvedTypeData::fmtstr(length, element, Location::dummy()) } Type::Unit => UnresolvedTypeData::Unit, Type::Tuple(fields) => { @@ -460,7 +462,9 @@ impl Type { // this to ignore this case since it shouldn't be needed anyway. Type::Forall(_, typ) => return typ.to_display_ast(), Type::Constant(..) => panic!("Type::Constant where a type was expected: {self:?}"), - Type::Quoted(quoted_type) => UnresolvedTypeData::Quoted(*quoted_type), + Type::Quoted(quoted_type) => { + UnresolvedTypeData::quoted(*quoted_type, Location::dummy()) + } Type::Error => UnresolvedTypeData::Error, Type::InfixExpr(lhs, op, rhs, _) => { let lhs = Box::new(lhs.to_type_expression()); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index ac2ea51eec3..88e4405baf4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -24,7 +24,7 @@ use crate::{ Pattern, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, UnsafeExpression, }, - elaborator::{ElaborateReason, Elaborator}, + elaborator::{ElaborateReason, Elaborator, PrimitiveType}, hir::{ comptime::{ InterpreterError, Value, @@ -1365,7 +1365,22 @@ fn unresolved_type_is_bool( ) -> IResult { let self_argument = check_one_argument(arguments, location)?; let typ = get_unresolved_type(interner, self_argument)?; - Ok(Value::Bool(matches!(typ, UnresolvedTypeData::Bool))) + + // TODO: we should resolve the type here instead of just checking the name + // See https://github.com/noir-lang/noir/issues/8505 + let UnresolvedTypeData::Named(path, generics, _) = typ else { + return Ok(Value::Bool(false)); + }; + if !generics.is_empty() { + return Ok(Value::Bool(false)); + } + let Some(ident) = path.as_ident() else { + return Ok(Value::Bool(false)); + }; + let Some(primitive_type) = PrimitiveType::lookup_by_name(ident.as_str()) else { + return Ok(Value::Bool(false)); + }; + Ok(Value::Bool(primitive_type == PrimitiveType::Bool)) } // fn is_field(self) -> bool @@ -1376,7 +1391,22 @@ fn unresolved_type_is_field( ) -> IResult { let self_argument = check_one_argument(arguments, location)?; let typ = get_unresolved_type(interner, self_argument)?; - Ok(Value::Bool(matches!(typ, UnresolvedTypeData::FieldElement))) + + // TODO: we should resolve the type here instead of just checking the name + // See https://github.com/noir-lang/noir/issues/8505 + let UnresolvedTypeData::Named(path, generics, _) = typ else { + return Ok(Value::Bool(false)); + }; + if !generics.is_empty() { + return Ok(Value::Bool(false)); + } + let Some(ident) = path.as_ident() else { + return Ok(Value::Bool(false)); + }; + let Some(primitive_type) = PrimitiveType::lookup_by_name(ident.as_str()) else { + return Ok(Value::Bool(false)); + }; + Ok(Value::Bool(primitive_type == PrimitiveType::Field)) } // fn is_unit(self) -> bool diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index f1bfbcedc27..05489228d54 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -16,6 +16,7 @@ use crate::ast::{ NoirTypeAlias, Pattern, TraitImplItemKind, TraitItem, TypeImpl, UnresolvedType, UnresolvedTypeData, desugar_generic_trait_bounds, }; +use crate::elaborator::PrimitiveType; use crate::hir::resolution::errors::ResolverError; use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, TypeId}; use crate::token::{SecondaryAttribute, TestScope}; @@ -892,16 +893,23 @@ impl ModCollector<'_> { typ: &UnresolvedType, errors: &mut Vec, ) -> Type { - match &typ.typ { - UnresolvedTypeData::FieldElement => Type::FieldElement, - UnresolvedTypeData::Integer(sign, bits) => Type::Integer(*sign, *bits), - _ => { - let error = - ResolverError::AssociatedConstantsMustBeNumeric { location: typ.location }; - errors.push(error.into()); - Type::Error + // TODO: delay this to the Elaborator + // See https://github.com/noir-lang/noir/issues/8504 + if let UnresolvedTypeData::Named(path, _generics, _) = &typ.typ { + if path.segments.len() == 1 { + if let Some(primitive_type) = + PrimitiveType::lookup_by_name(path.segments[0].ident.as_str()) + { + if let Some(typ) = primitive_type.to_integer_or_field() { + return typ; + } + } } } + + let error = ResolverError::AssociatedConstantsMustBeNumeric { location: typ.location }; + errors.push(error.into()); + Type::Error } } diff --git a/compiler/noirc_frontend/src/hir/resolution/import.rs b/compiler/noirc_frontend/src/hir/resolution/import.rs index 455258cd403..b48e35347c3 100644 --- a/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -58,13 +58,16 @@ pub enum PathResolutionError { UnresolvedWithPossibleTraitsToImport { ident: Ident, traits: Vec }, #[error("Multiple applicable items in scope")] MultipleTraitsInScope { ident: Ident, traits: Vec }, + #[error("`StructDefinition` is deprecated. It has been renamed to `TypeDefinition`")] + StructDefinitionDeprecated { location: Location }, } impl PathResolutionError { pub fn location(&self) -> Location { match self { PathResolutionError::NoSuper(location) - | PathResolutionError::TurbofishNotAllowedOnItem { location, .. } => *location, + | PathResolutionError::TurbofishNotAllowedOnItem { location, .. } + | PathResolutionError::StructDefinitionDeprecated { location } => *location, PathResolutionError::Unresolved(ident) | PathResolutionError::Private(ident) | PathResolutionError::NotAModule { ident, .. } @@ -139,6 +142,14 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic { ident.location(), ) } + PathResolutionError::StructDefinitionDeprecated { location } => { + CustomDiagnostic::simple_warning( + "`StructDefinition` is deprecated. It has been renamed to `TypeDefinition`" + .to_string(), + String::new(), + *location, + ) + } } } } @@ -334,7 +345,9 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> let current_ident = ¤t_segment.ident; let (typ, visibility) = match current_ns.types { - None => return Err(PathResolutionError::Unresolved(last_ident.clone())), + None => { + return Err(PathResolutionError::Unresolved(last_ident.clone())); + } Some((typ, visibility, _)) => (typ, visibility), }; diff --git a/compiler/noirc_frontend/src/hir/type_check/generics.rs b/compiler/noirc_frontend/src/hir/type_check/generics.rs index 29baee8c30a..c7fa6920296 100644 --- a/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -3,9 +3,11 @@ use std::cell::Ref; use iter_extended::vecmap; use crate::{ - DataType, ResolvedGeneric, Type, + DataType, Kind, ResolvedGeneric, Type, + ast::IntegerBitSize, hir_def::traits::NamedType, node_interner::{FuncId, NodeInterner, TraitId, TypeAliasId}, + shared::Signedness, }; /// Represents something that can be generic over type variables @@ -20,8 +22,8 @@ pub trait Generic { /// The name of this item, usually named by a user. E.g. "Foo" for "struct Foo {}" fn item_name(&self, interner: &NodeInterner) -> String; - /// Each ordered generic on this type, excluding any named generics. - fn generics(&self, interner: &NodeInterner) -> Vec; + /// Each ordered generic kind on this type, excluding any named generics. + fn generic_kinds(&self, interner: &NodeInterner) -> Vec; /// True if this item kind can ever accept named type arguments. /// Currently, this is only true for traits. Structs & aliases can never have named args. @@ -39,8 +41,8 @@ impl Generic for TraitId { interner.get_trait(*self).name.to_string() } - fn generics(&self, interner: &NodeInterner) -> Vec { - interner.get_trait(*self).generics.clone() + fn generic_kinds(&self, interner: &NodeInterner) -> Vec { + interner.get_trait(*self).generics.iter().map(|generic| generic.kind()).collect() } fn accepts_named_type_args(&self) -> bool { @@ -61,8 +63,14 @@ impl Generic for TypeAliasId { interner.get_type_alias(*self).borrow().name.to_string() } - fn generics(&self, interner: &NodeInterner) -> Vec { - interner.get_type_alias(*self).borrow().generics.clone() + fn generic_kinds(&self, interner: &NodeInterner) -> Vec { + interner + .get_type_alias(*self) + .borrow() + .generics + .iter() + .map(|generic| generic.kind()) + .collect() } fn accepts_named_type_args(&self) -> bool { @@ -83,8 +91,8 @@ impl Generic for Ref<'_, DataType> { self.name.to_string() } - fn generics(&self, _interner: &NodeInterner) -> Vec { - self.generics.clone() + fn generic_kinds(&self, _interner: &NodeInterner) -> Vec { + self.generics.iter().map(|generic| generic.kind()).collect() } fn accepts_named_type_args(&self) -> bool { @@ -105,8 +113,61 @@ impl Generic for FuncId { interner.function_name(self).to_string() } - fn generics(&self, interner: &NodeInterner) -> Vec { - interner.function_meta(self).direct_generics.clone() + fn generic_kinds(&self, interner: &NodeInterner) -> Vec { + interner.function_meta(self).direct_generics.iter().map(|generic| generic.kind()).collect() + } + + fn accepts_named_type_args(&self) -> bool { + false + } + + fn named_generics(&self, _interner: &NodeInterner) -> Vec { + Vec::new() + } +} + +pub struct StrPrimitiveType; + +impl Generic for StrPrimitiveType { + fn item_kind(&self) -> &'static str { + "primitive type" + } + + fn item_name(&self, _interner: &NodeInterner) -> String { + "str".to_string() + } + + fn generic_kinds(&self, _interner: &NodeInterner) -> Vec { + let length = + Kind::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))); + vec![length] + } + + fn accepts_named_type_args(&self) -> bool { + false + } + + fn named_generics(&self, _interner: &NodeInterner) -> Vec { + Vec::new() + } +} + +pub struct FmtstrPrimitiveType; + +impl Generic for FmtstrPrimitiveType { + fn item_kind(&self) -> &'static str { + "primitive type" + } + + fn item_name(&self, _interner: &NodeInterner) -> String { + "fmtstr".to_string() + } + + fn generic_kinds(&self, _interner: &NodeInterner) -> Vec { + let length = + Kind::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))); + let element = Kind::Normal; + vec![length, element] } fn accepts_named_type_args(&self) -> bool { diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index 34515e61583..0bd114df3bc 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -2,7 +2,7 @@ use crate::token::DocStyle; use super::{ errors::LexerErrorKind, - token::{FmtStrFragment, IntType, Keyword, LocatedToken, SpannedToken, Token, Tokens}, + token::{FmtStrFragment, Keyword, LocatedToken, SpannedToken, Token, Tokens}, }; use acvm::{AcirField, FieldElement}; use fm::FileId; @@ -398,15 +398,6 @@ impl<'a> Lexer<'a> { return Ok(keyword_token.into_span(start, end)); } - // Check if word an int type - // if no error occurred, then it is either a valid integer type or it is not an int type - let parsed_token = IntType::lookup_int_type(&word); - - // Check if it is an int type - if let Some(int_type) = parsed_token { - return Ok(Token::IntType(int_type).into_span(start, end)); - } - // Else it is just an identifier let ident_token = Token::Ident(word); Ok(ident_token.into_span(start, end)) @@ -998,26 +989,6 @@ mod tests { assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: true }); } - #[test] - fn test_int_type() { - let input = "u16 i16 i108 u104.5"; - - let expected = vec![ - Token::IntType(IntType::Unsigned(16)), - Token::IntType(IntType::Signed(16)), - Token::IntType(IntType::Signed(108)), - Token::IntType(IntType::Unsigned(104)), - Token::Dot, - Token::Int(5_i128.into()), - ]; - - let mut lexer = Lexer::new_with_dummy_file(input); - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); - } - } - #[test] fn test_int_too_large() { let modulus = FieldElement::modulus(); @@ -1423,7 +1394,7 @@ mod tests { Token::Keyword(Keyword::Let), Token::Ident("ten".to_string()), Token::Colon, - Token::Keyword(Keyword::Field), + Token::Ident("Field".to_string()), Token::Assign, Token::Int(10_i128.into()), Token::Semicolon, diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 2644b5903d5..17d7214d584 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -25,7 +25,6 @@ pub enum BorrowedToken<'input> { RawStr(&'input str, u8), FmtStr(&'input [FmtStrFragment], u32 /* length */), Keyword(Keyword), - IntType(IntType), AttributeStart { is_inner: bool, is_tag: bool, @@ -140,7 +139,6 @@ pub enum Token { RawStr(String, u8), FmtStr(Vec, u32 /* length */), Keyword(Keyword), - IntType(IntType), AttributeStart { is_inner: bool, is_tag: bool, @@ -280,7 +278,6 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), Token::InternedPattern(id) => BorrowedToken::InternedPattern(*id), - Token::IntType(i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, Token::Greater => BorrowedToken::Greater, @@ -517,7 +514,6 @@ impl fmt::Display for Token { write!(f, "(expr)") } Token::InternedUnresolvedTypeData(_) => write!(f, "(type)"), - Token::IntType(ref i) => write!(f, "{i}"), Token::Less => write!(f, "<"), Token::LessEqual => write!(f, "<="), Token::Greater => write!(f, ">"), @@ -1149,26 +1145,18 @@ pub enum Keyword { As, Assert, AssertEq, - Bool, Break, CallData, - Char, Comptime, Constrain, Continue, Contract, Crate, - CtString, Dep, Else, Enum, - EnumDefinition, - Expr, - Field, Fn, For, - FormatString, - FunctionDefinition, Global, If, Impl, @@ -1177,28 +1165,16 @@ pub enum Keyword { Loop, Match, Mod, - Module, Mut, Pub, - Quoted, Return, ReturnData, - String, Struct, - StructDefinition, Super, - TopLevelItem, Trait, - TraitConstraint, - TraitDefinition, - TraitImpl, Type, - TypeDefinition, - TypedExpr, - TypeType, Unchecked, Unconstrained, - UnresolvedType, Unsafe, Use, Where, @@ -1211,26 +1187,18 @@ impl fmt::Display for Keyword { Keyword::As => write!(f, "as"), Keyword::Assert => write!(f, "assert"), Keyword::AssertEq => write!(f, "assert_eq"), - Keyword::Bool => write!(f, "bool"), Keyword::Break => write!(f, "break"), - Keyword::Char => write!(f, "char"), Keyword::CallData => write!(f, "call_data"), Keyword::Comptime => write!(f, "comptime"), Keyword::Constrain => write!(f, "constrain"), Keyword::Continue => write!(f, "continue"), Keyword::Contract => write!(f, "contract"), Keyword::Crate => write!(f, "crate"), - Keyword::CtString => write!(f, "CtString"), Keyword::Dep => write!(f, "dep"), Keyword::Else => write!(f, "else"), Keyword::Enum => write!(f, "enum"), - Keyword::EnumDefinition => write!(f, "EnumDefinition"), - Keyword::Expr => write!(f, "Expr"), - Keyword::Field => write!(f, "Field"), Keyword::Fn => write!(f, "fn"), Keyword::For => write!(f, "for"), - Keyword::FormatString => write!(f, "fmtstr"), - Keyword::FunctionDefinition => write!(f, "FunctionDefinition"), Keyword::Global => write!(f, "global"), Keyword::If => write!(f, "if"), Keyword::Impl => write!(f, "impl"), @@ -1239,28 +1207,16 @@ impl fmt::Display for Keyword { Keyword::Loop => write!(f, "loop"), Keyword::Match => write!(f, "match"), Keyword::Mod => write!(f, "mod"), - Keyword::Module => write!(f, "Module"), Keyword::Mut => write!(f, "mut"), Keyword::Pub => write!(f, "pub"), - Keyword::Quoted => write!(f, "Quoted"), Keyword::Return => write!(f, "return"), Keyword::ReturnData => write!(f, "return_data"), - Keyword::String => write!(f, "str"), Keyword::Struct => write!(f, "struct"), - Keyword::StructDefinition => write!(f, "StructDefinition"), Keyword::Super => write!(f, "super"), - Keyword::TopLevelItem => write!(f, "TopLevelItem"), Keyword::Trait => write!(f, "trait"), - Keyword::TraitConstraint => write!(f, "TraitConstraint"), - Keyword::TraitDefinition => write!(f, "TraitDefinition"), - Keyword::TraitImpl => write!(f, "TraitImpl"), Keyword::Type => write!(f, "type"), - Keyword::TypeDefinition => write!(f, "TypeDefinition"), - Keyword::TypedExpr => write!(f, "TypedExpr"), - Keyword::TypeType => write!(f, "Type"), Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), - Keyword::UnresolvedType => write!(f, "UnresolvedType"), Keyword::Unsafe => write!(f, "unsafe"), Keyword::Use => write!(f, "use"), Keyword::Where => write!(f, "where"), @@ -1276,26 +1232,18 @@ impl Keyword { "as" => Keyword::As, "assert" => Keyword::Assert, "assert_eq" => Keyword::AssertEq, - "bool" => Keyword::Bool, "break" => Keyword::Break, "call_data" => Keyword::CallData, - "char" => Keyword::Char, "comptime" => Keyword::Comptime, "constrain" => Keyword::Constrain, "continue" => Keyword::Continue, "contract" => Keyword::Contract, "crate" => Keyword::Crate, - "CtString" => Keyword::CtString, "dep" => Keyword::Dep, "else" => Keyword::Else, "enum" => Keyword::Enum, - "EnumDefinition" => Keyword::EnumDefinition, - "Expr" => Keyword::Expr, - "Field" => Keyword::Field, "fn" => Keyword::Fn, "for" => Keyword::For, - "fmtstr" => Keyword::FormatString, - "FunctionDefinition" => Keyword::FunctionDefinition, "global" => Keyword::Global, "if" => Keyword::If, "impl" => Keyword::Impl, @@ -1304,28 +1252,16 @@ impl Keyword { "loop" => Keyword::Loop, "match" => Keyword::Match, "mod" => Keyword::Mod, - "Module" => Keyword::Module, "mut" => Keyword::Mut, "pub" => Keyword::Pub, - "Quoted" => Keyword::Quoted, "return" => Keyword::Return, "return_data" => Keyword::ReturnData, - "str" => Keyword::String, "struct" => Keyword::Struct, "super" => Keyword::Super, - "TopLevelItem" => Keyword::TopLevelItem, "trait" => Keyword::Trait, - "TraitConstraint" => Keyword::TraitConstraint, - "TraitDefinition" => Keyword::TraitDefinition, - "TraitImpl" => Keyword::TraitImpl, "type" => Keyword::Type, - "Type" => Keyword::TypeType, - "TypeDefinition" => Keyword::TypeDefinition, - "TypedExpr" => Keyword::TypedExpr, - "StructDefinition" => Keyword::StructDefinition, "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, - "UnresolvedType" => Keyword::UnresolvedType, "unsafe" => Keyword::Unsafe, "use" => Keyword::Use, "where" => Keyword::Where, diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 7aa477dc9cc..50ff4929a0c 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -58,8 +58,6 @@ pub enum ParserErrorReason { #[error("Unexpected '{0}', expected a field name or number")] ExpectedFieldName(Token), - #[error("Expected a pattern but found a type - {0}")] - ExpectedPatternButFoundType(Token), #[error("Expected a ; separating these two statements")] MissingSeparatingSemi, #[error("Expected a ; after `let` statement")] @@ -94,8 +92,6 @@ pub enum ParserErrorReason { InvalidBitSize(u32), #[error("{0}")] Lexer(LexerErrorKind), - #[error("The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`")] - ForbiddenNumericGenericType, #[error("Invalid call data identifier, must be a number. E.g `call_data(0)`")] InvalidCallDataIdentifier, #[error("Associated types are not allowed in paths")] @@ -117,8 +113,6 @@ pub enum ParserErrorReason { MissingSafetyComment, #[error("Missing parameters for function definition")] MissingParametersForFunctionDefinition, - #[error("`StructDefinition` is deprecated. It has been renamed to `TypeDefinition`")] - StructDefinitionDeprecated, #[error("Missing angle brackets surrounding type in associated item path")] MissingAngleBrackets, #[error("Expected value, found built-in type `{typ}`")] @@ -127,8 +121,6 @@ pub enum ParserErrorReason { LogicalAnd, #[error("Trait bounds are not allowed here")] TraitBoundsNotAllowedHere, - #[error("Missing double colon before generic arguments")] - MissingDoubleColon, } /// Represents a parsing error, or a parsing error in the making. @@ -292,11 +284,6 @@ impl<'a> From<&'a ParserError> for Diagnostic { ParserErrorReason::TraitImplVisibilityIgnored => { Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } - ParserErrorReason::ExpectedPatternButFoundType(ty) => Diagnostic::simple_error( - format!("Expected a pattern but found a type - {ty}"), - format!("{ty} is a type and cannot be used as a variable name"), - error.location(), - ), ParserErrorReason::Lexer(error) => error.into(), ParserErrorReason::ExpectedMutAfterAmpersand { found } => Diagnostic::simple_error( format!("Expected `mut` after `&`, found `{found}`"), @@ -320,17 +307,10 @@ impl<'a> From<&'a ParserError> for Diagnostic { let secondary = "Consider changing it to a regular `//` comment".to_string(); Diagnostic::simple_warning(primary, secondary, error.location()) } - ParserErrorReason::StructDefinitionDeprecated => { - Diagnostic::simple_warning(format!("{reason}"), String::new(), error.location()) - } ParserErrorReason::MissingAngleBrackets => { let secondary = "Types that don't start with an identifier need to be surrounded with angle brackets: `<`, `>`".to_string(); Diagnostic::simple_error(format!("{reason}"), secondary, error.location()) } - ParserErrorReason::MissingDoubleColon => { - let secondary = String::new(); - Diagnostic::simple_warning(format!("{reason}"), secondary, error.location()) - } ParserErrorReason::LogicalAnd => { let primary = "Noir has no logical-and (&&) operator since short-circuiting is much less efficient when compiling to circuits".to_string(); let secondary = diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 88b1f3c3f7a..d4da9242efc 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -7,7 +7,7 @@ use crate::{ ast::{Ident, ItemVisibility}, lexer::{Lexer, lexer::LocatedTokenResult}, node_interner::ExprId, - token::{FmtStrFragment, IntType, Keyword, LocatedToken, Token, TokenKind, Tokens}, + token::{FmtStrFragment, Keyword, LocatedToken, Token, TokenKind, Tokens}, }; use super::{ParsedModule, ParserError, ParserErrorReason, labels::ParsingRuleLabel}; @@ -264,19 +264,6 @@ impl<'a> Parser<'a> { false } - fn eat_int_type(&mut self) -> Option { - let is_int_type = matches!(self.token.token(), Token::IntType(..)); - if is_int_type { - let token = self.bump(); - match token.into_token() { - Token::IntType(int_type) => Some(int_type), - _ => unreachable!(), - } - } else { - None - } - } - fn eat_int(&mut self) -> Option { if matches!(self.token.token(), Token::Int(..)) { let token = self.bump(); @@ -390,17 +377,6 @@ impl<'a> Parser<'a> { self.eat(Token::Comma) } - fn eat_commas(&mut self) -> bool { - if self.eat_comma() { - while self.eat_comma() { - self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_location); - } - true - } else { - false - } - } - fn eat_semicolon(&mut self) -> bool { self.eat(Token::Semicolon) } diff --git a/compiler/noirc_frontend/src/parser/parser/enums.rs b/compiler/noirc_frontend/src/parser/parser/enums.rs index 8cd6a1ba249..09809115ed1 100644 --- a/compiler/noirc_frontend/src/parser/parser/enums.rs +++ b/compiler/noirc_frontend/src/parser/parser/enums.rs @@ -122,13 +122,12 @@ mod tests { use insta::assert_snapshot; use crate::{ - ast::{IntegerBitSize, NoirEnumeration, UnresolvedGeneric, UnresolvedTypeData}, + ast::{NoirEnumeration, UnresolvedGeneric}, parse_program_with_dummy_file, parser::{ ItemKind, ParserErrorReason, parser::tests::{expect_no_errors, get_source_with_error_span}, }, - shared::Signedness, }; fn parse_enum_no_errors(src: &str) -> NoirEnumeration { @@ -171,10 +170,7 @@ mod tests { panic!("Expected generic numeric"); }; assert_eq!("B", ident.to_string()); - assert_eq!( - typ.typ, - UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) - ); + assert_eq!(typ.typ.to_string(), "u32"); } #[test] @@ -186,16 +182,13 @@ mod tests { let variant = noir_enum.variants.remove(0).item; assert_eq!("X", variant.name.to_string()); - assert!(matches!( - variant.parameters.as_ref().unwrap()[0].typ, - UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) - )); + assert_eq!(variant.parameters.as_ref().unwrap()[0].typ.to_string(), "i32"); let variant = noir_enum.variants.remove(0).item; assert_eq!("y", variant.name.to_string()); let parameters = variant.parameters.as_ref().unwrap(); - assert!(matches!(parameters[0].typ, UnresolvedTypeData::FieldElement)); - assert!(matches!(parameters[1].typ, UnresolvedTypeData::Integer(..))); + assert_eq!(parameters[0].typ.to_string(), "Field"); + assert_eq!(parameters[1].typ.to_string(), "u32"); let variant = noir_enum.variants.remove(0).item; assert_eq!("Z", variant.name.to_string()); diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 12f204d6a83..ab3bfac34c6 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -251,7 +251,11 @@ impl Parser<'_> { return (atom, false); } - let typ = self.parse_type_or_error(); + // Here we don't allow generics on a type so that `x as u8 < 3` parses without error. + // In Rust the above is a syntax error as `u8<` would denote a generic type. + // In Noir it's unlikely we'd want generic types in casts and, to avoid a breaking change, + // we disallow generics in that position. + let typ = self.parse_type_or_error_without_generics(); let kind = ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); let location = self.location_since(start_location); let atom = Expression { kind, location }; @@ -666,9 +670,7 @@ impl Parser<'_> { /// TypePathExpression = PrimitiveType '::' identifier ( '::' GenericTypeArgs )? fn parse_type_path_expr(&mut self) -> Option { let start_location = self.current_token_location; - let typ = self.parse_primitive_type( - true, // require_colons - )?; + let typ = self.parse_primitive_type()?; let location = self.location_since(start_location); let typ = UnresolvedType { typ, location }; @@ -1827,6 +1829,25 @@ mod tests { assert_snapshot!(error.to_string(), @"Expected a type but found end of input"); } + #[test] + fn parses_cast_comparison() { + // Note: in Rust this is a syntax error because `u8 <` is parsed as a generic type reference. + // In Noir we allow this syntax, for now, mainly to avoid a breaking change and because + // it's unlikely we'd want generic types in this context. + let src = "1 as u8 < 3"; + let expr = parse_expression_no_errors(src); + let ExpressionKind::Infix(infix_expr) = expr.kind else { + panic!("Expected infix"); + }; + let ExpressionKind::Cast(cast_expr) = infix_expr.lhs.kind else { + panic!("Expected cast"); + }; + assert_eq!(cast_expr.lhs.to_string(), "1"); + assert_eq!(cast_expr.r#type.to_string(), "u8"); + + assert_eq!(infix_expr.rhs.to_string(), "3"); + } + #[test] fn parses_index() { let src = "1[2]"; @@ -1945,7 +1966,7 @@ mod tests { let (pattern, typ) = lambda.parameters.remove(0); assert_eq!(pattern.to_string(), "y"); - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -1957,7 +1978,7 @@ mod tests { }; assert!(lambda.parameters.is_empty()); assert_eq!(lambda.body.to_string(), "1"); - assert!(matches!(lambda.return_type.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(lambda.return_type.typ.to_string(), "Field"); } #[test] @@ -1983,96 +2004,6 @@ mod tests { assert_eq!(block.statements.len(), 1); } - #[test] - fn parses_type_path() { - let src = "Field::foo"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::TypePath(type_path) = expr.kind else { - panic!("Expected type_path"); - }; - assert_eq!(type_path.typ.to_string(), "Field"); - assert_eq!(type_path.item.to_string(), "foo"); - assert!(type_path.turbofish.is_none()); - } - - #[test] - fn parses_type_path_with_generics() { - let src = "Field::foo::"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::TypePath(type_path) = expr.kind else { - panic!("Expected type_path"); - }; - assert_eq!(type_path.typ.to_string(), "Field"); - assert_eq!(type_path.item.to_string(), "foo"); - assert!(type_path.turbofish.is_some()); - } - - #[test] - fn parses_type_path_with_str_no_colons() { - let src = " - str::foo - ^ - "; - let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str_with_dummy_file(&src); - let expr = parser.parse_expression_or_error(); - - let ExpressionKind::TypePath(type_path) = expr.kind else { - panic!("Expected type_path"); - }; - assert_eq!(type_path.typ.to_string(), "str"); - assert_eq!(type_path.item.to_string(), "foo"); - assert!(type_path.turbofish.is_none()); - - let reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::MissingDoubleColon)); - } - - #[test] - fn parses_type_path_with_str_and_colons() { - let src = "str::::foo"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::TypePath(type_path) = expr.kind else { - panic!("Expected type_path"); - }; - assert_eq!(type_path.typ.to_string(), "str"); - assert_eq!(type_path.item.to_string(), "foo"); - assert!(type_path.turbofish.is_none()); - } - - #[test] - fn parses_type_path_with_fmtstr_no_colons() { - let src = " - fmtstr::foo - ^ - "; - let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str_with_dummy_file(&src); - let expr = parser.parse_expression_or_error(); - - let ExpressionKind::TypePath(type_path) = expr.kind else { - panic!("Expected type_path"); - }; - assert_eq!(type_path.typ.to_string(), "fmtstr"); - assert_eq!(type_path.item.to_string(), "foo"); - assert!(type_path.turbofish.is_none()); - - let reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::MissingDoubleColon)); - } - - #[test] - fn parses_type_path_with_fmtstr_and_colons() { - let src = "fmtstr::::foo"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::TypePath(type_path) = expr.kind else { - panic!("Expected type_path"); - }; - assert_eq!(type_path.typ.to_string(), "fmtstr"); - assert_eq!(type_path.item.to_string(), "foo"); - assert!(type_path.turbofish.is_none()); - } - #[test] fn parses_type_path_with_tuple() { let src = "<()>::foo"; @@ -2150,22 +2081,6 @@ mod tests { assert!(matches!(reason, ParserErrorReason::MissingAngleBrackets)); } - #[test] - fn parses_primitive_type_errors() { - let src = " - Field - ^^^^^ - "; - let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str_with_dummy_file(&src); - let expr = parser.parse_expression_or_error(); - let ExpressionKind::Error = expr.kind else { - panic!("Expected error"); - }; - let reason = get_single_error_reason(&parser.errors, span); - assert_eq!(reason.to_string(), "Expected value, found built-in type `Field`"); - } - #[test] fn parses_unquote_var() { let src = "$foo::bar"; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index bf6ab4396a7..d75dc445674 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -349,10 +349,7 @@ mod tests { use insta::assert_snapshot; use crate::{ - ast::{ - ExpressionKind, IntegerBitSize, ItemVisibility, NoirFunction, StatementKind, - UnresolvedTypeData, - }, + ast::{ExpressionKind, ItemVisibility, NoirFunction, StatementKind}, parse_program_with_dummy_file, parser::{ ItemKind, Parser, ParserErrorReason, @@ -361,7 +358,7 @@ mod tests { get_source_with_error_span, }, }, - shared::{Signedness, Visibility}, + shared::Visibility, }; fn parse_function_no_error(src: &str) -> NoirFunction { @@ -445,7 +442,7 @@ mod tests { let src = "fn foo() -> Field {}"; let noir_function = parse_function_no_error(src); assert_eq!(noir_function.def.return_visibility, Visibility::Private); - assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); + assert_eq!(noir_function.return_type().typ.to_string(), "Field"); } #[test] @@ -453,7 +450,7 @@ mod tests { let src = "fn foo() -> pub Field {}"; let noir_function = parse_function_no_error(src); assert_eq!(noir_function.def.return_visibility, Visibility::Public); - assert_eq!(noir_function.return_type().typ, UnresolvedTypeData::FieldElement); + assert_eq!(noir_function.return_type().typ.to_string(), "Field"); } #[test] @@ -588,14 +585,8 @@ mod tests { let params = noir_function.parameters(); assert_eq!(params.len(), 2); - assert_eq!( - params[0].typ.typ, - UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) - ); - assert_eq!( - params[1].typ.typ, - UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour) - ); + assert_eq!(params[0].typ.typ.to_string(), "i32",); + assert_eq!(params[1].typ.typ.to_string(), "i64",); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/generics.rs b/compiler/noirc_frontend/src/parser/parser/generics.rs index 255bf7a02c2..06c0e5ff39b 100644 --- a/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -95,21 +95,19 @@ impl Parser<'_> { ParserErrorReason::MissingTypeForNumericGeneric, self.current_token_location, ); + let location = self.location_at_previous_token_end(); let typ = UnresolvedType { - typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), - location: self.location_at_previous_token_end(), + typ: UnresolvedTypeData::integer( + Signedness::Unsigned, + IntegerBitSize::ThirtyTwo, + location, + ), + location, }; return Some(UnresolvedGeneric::Numeric { ident, typ }); } let typ = self.parse_type_or_error(); - if let UnresolvedTypeData::Integer(signedness, bit_size) = &typ.typ { - if matches!(signedness, Signedness::Signed) - || matches!(bit_size, IntegerBitSize::SixtyFour) - { - self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.location); - } - } Some(UnresolvedGeneric::Numeric { ident, typ }) } @@ -187,14 +185,13 @@ impl Parser<'_> { #[cfg(test)] mod tests { use crate::{ - ast::{GenericTypeArgs, IntegerBitSize, UnresolvedGeneric, UnresolvedTypeData}, + ast::{GenericTypeArgs, UnresolvedGeneric}, parser::{ Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, }, - shared::Signedness, }; fn parse_generics_no_errors(src: &str) -> Vec { @@ -236,10 +233,7 @@ mod tests { panic!("Expected generic numeric"); }; assert_eq!("B", ident.to_string()); - assert_eq!( - typ.typ, - UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) - ); + assert_eq!(typ.typ.to_string(), "u32",); let generic = generics.remove(0); let UnresolvedGeneric::Variable(ident, trait_bounds) = generic else { @@ -290,19 +284,6 @@ mod tests { assert_eq!(generics.ordered_args[0].to_string(), "1"); } - #[test] - fn parse_numeric_generic_error_if_invalid_integer() { - let src = " - - ^^^ - "; - let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str_with_dummy_file(&src); - parser.parse_generics(true); - let reason = get_single_error_reason(&parser.errors, span); - assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); - } - #[test] fn parse_arithmetic_generic_on_variable() { let src = ""; diff --git a/compiler/noirc_frontend/src/parser/parser/global.rs b/compiler/noirc_frontend/src/parser/parser/global.rs index ffa5a0c19a3..fe3fe172c45 100644 --- a/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/compiler/noirc_frontend/src/parser/parser/global.rs @@ -72,10 +72,7 @@ mod tests { use insta::assert_snapshot; use crate::{ - ast::{ - ExpressionKind, IntegerBitSize, ItemVisibility, LetStatement, Literal, Pattern, - UnresolvedTypeData, - }, + ast::{ExpressionKind, ItemVisibility, LetStatement, Literal, Pattern, UnresolvedTypeData}, parse_program_with_dummy_file, parser::{ ItemKind, ParserErrorReason, @@ -84,7 +81,6 @@ mod tests { get_source_with_error_span, }, }, - shared::Signedness, }; fn parse_global_no_errors(src: &str) -> (LetStatement, ItemVisibility) { @@ -120,10 +116,7 @@ mod tests { panic!("Expected identifier pattern"); }; assert_eq!("foo", name.to_string()); - assert!(matches!( - let_statement.r#type.typ, - UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) - )); + assert_eq!(let_statement.r#type.typ.to_string(), "i32"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/impls.rs b/compiler/noirc_frontend/src/parser/parser/impls.rs index 6c40c970644..10d573fa1e9 100644 --- a/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -438,7 +438,7 @@ mod tests { }; assert_eq!(trait_name.to_string(), "Foo"); - assert!(matches!(trait_impl.object_type.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(trait_impl.object_type.typ.to_string(), "Field"); assert!(trait_impl.items.is_empty()); assert!(trait_impl.impl_generics.is_empty()); } @@ -453,7 +453,7 @@ mod tests { }; assert_eq!(trait_name.to_string(), "Foo"); - assert!(matches!(trait_impl.object_type.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(trait_impl.object_type.typ.to_string(), "Field"); assert!(trait_impl.items.is_empty()); assert_eq!(trait_impl.impl_generics.len(), 1); } diff --git a/compiler/noirc_frontend/src/parser/parser/pattern.rs b/compiler/noirc_frontend/src/parser/parser/pattern.rs index a462803c20f..7d3950f2970 100644 --- a/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -125,15 +125,7 @@ impl Parser<'_> { return Some(pattern); } - let Some(mut path) = self.parse_path() else { - if self.at_built_in_type() { - self.push_error( - ParserErrorReason::ExpectedPatternButFoundType(self.token.token().clone()), - self.current_token_location, - ); - } - return None; - }; + let mut path = self.parse_path()?; if self.eat_left_brace() { return Some(self.parse_struct_pattern(path, start_location)); @@ -222,28 +214,6 @@ impl Parser<'_> { (ident.clone(), Pattern::Identifier(ident)) }) } - - fn at_built_in_type(&self) -> bool { - matches!( - self.token.token(), - Token::Bool(..) - | Token::IntType(..) - | Token::Keyword(Keyword::Bool) - | Token::Keyword(Keyword::CtString) - | Token::Keyword(Keyword::Expr) - | Token::Keyword(Keyword::Field) - | Token::Keyword(Keyword::FunctionDefinition) - | Token::Keyword(Keyword::Module) - | Token::Keyword(Keyword::Quoted) - | Token::Keyword(Keyword::StructDefinition) - | Token::Keyword(Keyword::TraitConstraint) - | Token::Keyword(Keyword::TraitDefinition) - | Token::Keyword(Keyword::TraitImpl) - | Token::Keyword(Keyword::TypedExpr) - | Token::Keyword(Keyword::TypeType) - | Token::Keyword(Keyword::UnresolvedType) - ) - } } #[cfg(test)] @@ -254,13 +224,9 @@ mod tests { use crate::{ ast::Pattern, parser::{ - Parser, ParserErrorReason, - parser::tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, + Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, - token::{Keyword, Token}, }; fn parse_pattern_no_errors(src: &str) -> Pattern { @@ -383,22 +349,4 @@ mod tests { let Pattern::Struct(path, _, _) = pattern else { panic!("Expected a struct pattern") }; assert_eq!(path.to_string(), "foo::Bar"); } - - #[test] - fn errors_on_reserved_type() { - let src = " - Field - ^^^^^ - "; - let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str_with_dummy_file(&src); - let pattern = parser.parse_pattern(); - assert!(pattern.is_none()); - - let reason = get_single_error_reason(&parser.errors, span); - assert!(matches!( - reason, - ParserErrorReason::ExpectedPatternButFoundType(Token::Keyword(Keyword::Field)) - )); - } } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index da4a2c5ae28..52dcab45e6f 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -131,7 +131,7 @@ mod tests { use insta::assert_snapshot; use crate::{ - ast::{IntegerBitSize, NoirStruct, UnresolvedGeneric, UnresolvedTypeData}, + ast::{NoirStruct, UnresolvedGeneric}, parse_program_with_dummy_file, parser::{ ItemKind, ParserErrorReason, @@ -140,7 +140,6 @@ mod tests { get_source_with_error_span, }, }, - shared::Signedness, }; fn parse_struct_no_errors(src: &str) -> NoirStruct { @@ -192,10 +191,7 @@ mod tests { panic!("Expected generic numeric"); }; assert_eq!("B", ident.to_string()); - assert_eq!( - typ.typ, - UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) - ); + assert_eq!(typ.typ.to_string(), "u32"); } #[test] @@ -207,14 +203,11 @@ mod tests { let field = noir_struct.fields.remove(0).item; assert_eq!("x", field.name.to_string()); - assert!(matches!( - field.typ.typ, - UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) - )); + assert_eq!(field.typ.typ.to_string(), "i32"); let field = noir_struct.fields.remove(0).item; assert_eq!("y", field.name.to_string()); - assert!(matches!(field.typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(field.typ.typ.to_string(), "Field"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 0c9824046cd..809651ede62 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -57,7 +57,7 @@ impl Parser<'_> { #[cfg(test)] mod tests { use crate::{ - ast::{NoirTypeAlias, UnresolvedTypeData}, + ast::NoirTypeAlias, parse_program_with_dummy_file, parser::{ItemKind, parser::tests::expect_no_errors}, }; @@ -79,7 +79,7 @@ mod tests { let alias = parse_type_alias_no_errors(src); assert_eq!("Foo", alias.name.to_string()); assert!(alias.generics.is_empty()); - assert_eq!(alias.typ.typ, UnresolvedTypeData::FieldElement); + assert_eq!(alias.typ.typ.to_string(), "Field"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/compiler/noirc_frontend/src/parser/parser/type_expression.rs index f6d6dbd8a25..d41e114e9a1 100644 --- a/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -533,9 +533,7 @@ mod tests { let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected parenthesized type"); }; - let UnresolvedTypeData::FieldElement = typ.typ else { - panic!("Expected field type"); - }; + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -555,12 +553,8 @@ mod tests { let UnresolvedTypeData::Tuple(types) = typ.typ else { panic!("Expected tuple type"); }; - let UnresolvedTypeData::FieldElement = types[0].typ else { - panic!("Expected field type"); - }; - let UnresolvedTypeData::Bool = types[1].typ else { - panic!("Expected bool type"); - }; + assert_eq!(types[0].typ.to_string(), "Field"); + assert_eq!(types[1].typ.to_string(), "bool"); } #[test] @@ -584,12 +578,8 @@ mod tests { let UnresolvedTypeData::Tuple(types) = typ.typ else { panic!("Expected tuple type"); }; - let UnresolvedTypeData::FieldElement = types[0].typ else { - panic!("Expected field type"); - }; - let UnresolvedTypeData::Bool = types[1].typ else { - panic!("Expected bool type"); - }; + assert_eq!(types[0].typ.to_string(), "Field"); + assert_eq!(types[1].typ.to_string(), "bool"); } #[test] @@ -602,9 +592,7 @@ mod tests { panic!("Expected tuple type"); }; assert_eq!(types.len(), 1); - let UnresolvedTypeData::FieldElement = types[0].typ else { - panic!("Expected field type"); - }; + assert_eq!(types[0].typ.to_string(), "Field"); } #[test] diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 77ce7a9165c..b0c9141f912 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -1,9 +1,6 @@ -use acvm::{AcirField, FieldElement}; - use crate::{ - QuotedType, - ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{ParserErrorReason, labels::ParsingRuleLabel}, + ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData}, + parser::labels::ParsingRuleLabel, token::{Keyword, Token, TokenKind}, }; @@ -11,7 +8,19 @@ use super::{Parser, parse_many::separated_by_comma_until_right_paren}; impl Parser<'_> { pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { - if let Some(typ) = self.parse_type() { + self.parse_type_or_error_impl( + true, // allow generics + ) + } + + pub(crate) fn parse_type_or_error_without_generics(&mut self) -> UnresolvedType { + self.parse_type_or_error_impl( + false, // allow generics + ) + } + + pub(crate) fn parse_type_or_error_impl(&mut self, allow_generics: bool) -> UnresolvedType { + if let Some(typ) = self.parse_type_allowing_generics(allow_generics) { typ } else { self.expected_label(ParsingRuleLabel::Type); @@ -40,16 +49,23 @@ impl Parser<'_> { } pub(crate) fn parse_type(&mut self) -> Option { + self.parse_type_allowing_generics( + true, // allow generics + ) + } + + pub(crate) fn parse_type_allowing_generics( + &mut self, + allow_generics: bool, + ) -> Option { let start_location = self.current_token_location; - let typ = self.parse_unresolved_type_data()?; + let typ = self.parse_unresolved_type_data(allow_generics)?; let location = self.location_since(start_location); Some(UnresolvedType { typ, location }) } - fn parse_unresolved_type_data(&mut self) -> Option { - if let Some(typ) = self.parse_primitive_type( - false, // require colons - ) { + fn parse_unresolved_type_data(&mut self, allow_generics: bool) -> Option { + if let Some(typ) = self.parse_primitive_type() { return Some(typ); } @@ -78,41 +94,18 @@ impl Parser<'_> { } if let Some(path) = self.parse_path_no_turbofish() { - let generics = self.parse_generic_type_args(); + let generics = if allow_generics { + self.parse_generic_type_args() + } else { + GenericTypeArgs::default() + }; return Some(UnresolvedTypeData::Named(path, generics, false)); } None } - pub(super) fn parse_primitive_type( - &mut self, - require_colons: bool, - ) -> Option { - if let Some(typ) = self.parse_field_type() { - return Some(typ); - } - - if let Some(typ) = self.parse_int_type() { - return Some(typ); - } - - if let Some(typ) = self.parse_bool_type() { - return Some(typ); - } - - if let Some(typ) = self.parse_str_type(require_colons) { - return Some(typ); - } - - if let Some(typ) = self.parse_fmtstr_type(require_colons) { - return Some(typ); - } - - if let Some(typ) = self.parse_comptime_type() { - return Some(typ); - } - + pub(super) fn parse_primitive_type(&mut self) -> Option { if let Some(typ) = self.parse_resolved_type() { return Some(typ); } @@ -124,170 +117,6 @@ impl Parser<'_> { None } - fn parse_bool_type(&mut self) -> Option { - if self.eat_keyword(Keyword::Bool) { - return Some(UnresolvedTypeData::Bool); - } - - None - } - - fn parse_field_type(&mut self) -> Option { - if self.eat_keyword(Keyword::Field) { - return Some(UnresolvedTypeData::FieldElement); - } - - None - } - - fn parse_int_type(&mut self) -> Option { - if let Some(int_type) = self.eat_int_type() { - return Some(match UnresolvedTypeData::from_int_token(int_type) { - Ok(typ) => typ, - Err(err) => { - self.push_error( - ParserErrorReason::InvalidBitSize(err.0), - self.previous_token_location, - ); - UnresolvedTypeData::Error - } - }); - } - - None - } - - fn parse_str_type(&mut self, require_colons: bool) -> Option { - if !self.eat_keyword(Keyword::String) { - return None; - } - - if require_colons { - let location = self.current_token_location; - if !self.eat_double_colon() { - self.push_error(ParserErrorReason::MissingDoubleColon, location); - } - } - - if !self.eat_less() { - self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant( - FieldElement::zero(), - self.current_token_location, - ); - return Some(UnresolvedTypeData::String(expr)); - } - - let expr = match self.parse_type_expression() { - Ok(expr) => expr, - Err(error) => { - self.errors.push(error); - UnresolvedTypeExpression::Constant( - FieldElement::zero(), - self.current_token_location, - ) - } - }; - - self.eat_or_error(Token::Greater); - - Some(UnresolvedTypeData::String(expr)) - } - - fn parse_fmtstr_type(&mut self, require_colons: bool) -> Option { - if !self.eat_keyword(Keyword::FormatString) { - return None; - } - - if require_colons { - let location = self.current_token_location; - if !self.eat_double_colon() { - self.push_error(ParserErrorReason::MissingDoubleColon, location); - } - } - - if !self.eat_less() { - self.expected_token(Token::Less); - let expr = UnresolvedTypeExpression::Constant( - FieldElement::zero(), - self.current_token_location, - ); - let typ = - UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()); - return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); - } - - let expr = match self.parse_type_expression() { - Ok(expr) => expr, - Err(error) => { - self.errors.push(error); - UnresolvedTypeExpression::Constant( - FieldElement::zero(), - self.current_token_location, - ) - } - }; - - if !self.eat_commas() { - self.expected_token(Token::Comma); - } - - let typ = self.parse_type_or_error(); - - self.eat_or_error(Token::Greater); - - Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))) - } - - fn parse_comptime_type(&mut self) -> Option { - if self.eat_keyword(Keyword::Expr) { - return Some(UnresolvedTypeData::Quoted(QuotedType::Expr)); - } - if self.eat_keyword(Keyword::Quoted) { - return Some(UnresolvedTypeData::Quoted(QuotedType::Quoted)); - } - if self.eat_keyword(Keyword::TopLevelItem) { - return Some(UnresolvedTypeData::Quoted(QuotedType::TopLevelItem)); - } - if self.eat_keyword(Keyword::TypeType) { - return Some(UnresolvedTypeData::Quoted(QuotedType::Type)); - } - if self.eat_keyword(Keyword::TypedExpr) { - return Some(UnresolvedTypeData::Quoted(QuotedType::TypedExpr)); - } - - let location = self.current_token_location; - if self.eat_keyword(Keyword::StructDefinition) { - self.push_error(ParserErrorReason::StructDefinitionDeprecated, location); - return Some(UnresolvedTypeData::Quoted(QuotedType::TypeDefinition)); - } - if self.eat_keyword(Keyword::TypeDefinition) { - return Some(UnresolvedTypeData::Quoted(QuotedType::TypeDefinition)); - } - if self.eat_keyword(Keyword::TraitConstraint) { - return Some(UnresolvedTypeData::Quoted(QuotedType::TraitConstraint)); - } - if self.eat_keyword(Keyword::TraitDefinition) { - return Some(UnresolvedTypeData::Quoted(QuotedType::TraitDefinition)); - } - if self.eat_keyword(Keyword::TraitImpl) { - return Some(UnresolvedTypeData::Quoted(QuotedType::TraitImpl)); - } - if self.eat_keyword(Keyword::UnresolvedType) { - return Some(UnresolvedTypeData::Quoted(QuotedType::UnresolvedType)); - } - if self.eat_keyword(Keyword::FunctionDefinition) { - return Some(UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition)); - } - if self.eat_keyword(Keyword::Module) { - return Some(UnresolvedTypeData::Quoted(QuotedType::Module)); - } - if self.eat_keyword(Keyword::CtString) { - return Some(UnresolvedTypeData::Quoted(QuotedType::CtString)); - } - None - } - fn parse_function_type(&mut self) -> Option { let unconstrained = self.eat_keyword(Keyword::Unconstrained); @@ -489,20 +318,14 @@ impl Parser<'_> { #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use insta::assert_snapshot; - use proptest::prelude::*; - use strum::IntoEnumIterator; use crate::{ - QuotedType, - ast::{IntegerBitSize, UnresolvedType, UnresolvedTypeData}, + ast::{UnresolvedType, UnresolvedTypeData}, parser::{ - Parser, ParserErrorReason, + Parser, parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, - shared::Signedness, }; fn parse_type_no_errors(src: &str) -> UnresolvedType { @@ -519,96 +342,18 @@ mod tests { assert!(matches!(typ.typ, UnresolvedTypeData::Unit)); } - #[test] - fn parses_bool_type() { - let src = "bool"; - let typ = parse_type_no_errors(src); - assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); - } - - #[test] - fn parses_int_type() { - let src = "u32"; - let typ = parse_type_no_errors(src); - assert!(matches!( - typ.typ, - UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo) - )); - } - - proptest! { - #[test] - fn parses_only_expected_types(sign in proptest::prop_oneof![Just('u'), Just('i')], width: u8) { - let accepted_types = BTreeMap::from([ - ("u1", UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::One)), - ("u8", UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), - ("u16", UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen)), - ("u32", UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo)), - ("u64", UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour)), - ( - "u128", - UnresolvedTypeData::Integer( - Signedness::Unsigned, - IntegerBitSize::HundredTwentyEight, - ), - ), - ("i8", UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::Eight)), - ("i16", UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::Sixteen)), - ("i32", UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo)), - ("i64", UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour)), - ]); - - let src = format!("{sign}{width}"); - let mut parser = Parser::for_str_with_dummy_file(&src); - let typ = parser.parse_type_or_error(); - - if let Some(expected_typ) = accepted_types.get(&src.as_str()) { - assert_eq!(&typ.typ, expected_typ); - } else { - assert_eq!(typ.typ, UnresolvedTypeData::Error); - assert_eq!(parser.errors.len(), 1); - let error = &parser.errors[0]; - assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); - } - } - } - - #[test] - fn parses_field_type() { - let src = "Field"; - let typ = parse_type_no_errors(src); - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); - } - #[test] fn parses_str_type() { let src = "str<10>"; let typ = parse_type_no_errors(src); - let UnresolvedTypeData::String(expr) = typ.typ else { panic!("Expected a string type") }; - assert_eq!(expr.to_string(), "10"); + assert_eq!(typ.to_string(), "str<10>"); } #[test] fn parses_fmtstr_type() { let src = "fmtstr<10, T>"; let typ = parse_type_no_errors(src); - let UnresolvedTypeData::FormatString(expr, typ) = typ.typ else { - panic!("Expected a format string type") - }; - assert_eq!(expr.to_string(), "10"); - assert_eq!(typ.to_string(), "T"); - } - - #[test] - fn parses_comptime_types() { - for quoted_type in QuotedType::iter() { - let src = quoted_type.to_string(); - let typ = parse_type_no_errors(&src); - let UnresolvedTypeData::Quoted(parsed_quoted_type) = typ.typ else { - panic!("Expected a quoted type for {}", quoted_type) - }; - assert_eq!(parsed_quoted_type, quoted_type); - } + assert_eq!(typ.to_string(), "fmtstr<10, T>"); } #[test] @@ -619,10 +364,10 @@ mod tests { assert_eq!(types.len(), 2); let typ = types.remove(0); - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); let typ = types.remove(0); - assert!(matches!(typ.typ, UnresolvedTypeData::Bool)); + assert_eq!(typ.typ.to_string(), "bool"); } #[test] @@ -633,7 +378,7 @@ mod tests { assert_eq!(types.len(), 1); let typ = types.remove(0); - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -643,7 +388,7 @@ mod tests { let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -655,7 +400,7 @@ mod tests { let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { panic!("Expected a parenthesized type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -665,7 +410,7 @@ mod tests { let UnresolvedTypeData::Reference(typ, false) = typ.typ else { panic!("Expected a reference type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -675,7 +420,7 @@ mod tests { let UnresolvedTypeData::Reference(typ, true) = typ.typ else { panic!("Expected a mutable reference type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -714,7 +459,7 @@ mod tests { let src = "[Field]"; let typ = parse_type_no_errors(src); let UnresolvedTypeData::Slice(typ) = typ.typ else { panic!("Expected a slice type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); } #[test] @@ -737,7 +482,7 @@ mod tests { let UnresolvedTypeData::Array(expr, typ) = typ.typ else { panic!("Expected an array type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); assert_eq!(expr.to_string(), "10"); } @@ -751,7 +496,7 @@ mod tests { let UnresolvedTypeData::Array(expr, typ) = typ.typ else { panic!("Expected an array type") }; - assert!(matches!(typ.typ, UnresolvedTypeData::FieldElement)); + assert_eq!(typ.typ.to_string(), "Field"); assert_eq!(expr.to_string(), "10"); } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 1bb7c4c41a3..a04ecf60253 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2997,11 +2997,11 @@ fn do_not_infer_globals_to_u32_from_type_use() { ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` ~~~~~~~~~~~~~~~~ expected `u32`, found `Field` let _b: str = "hi"; - ^^^^^^^^^^^^ The numeric generic is not of type `u32` - ~~~~~~~~~~~~ expected `u32`, found `Field` + ^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~ expected `u32`, found `Field` let _c: fmtstr = f"hi"; - ^^^^^^^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` - ~~~~~~~~~~~~~~~~~~~~~~ expected `u32`, found `Field` + ^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~ expected `u32`, found `Field` } "#; check_errors!(src); @@ -4415,19 +4415,6 @@ fn immutable_references_without_ownership_feature() { check_errors!(src); } -#[named] -#[test] -fn errors_on_invalid_integer_bit_size() { - let src = r#" - fn main() { - let _: u42 = 4; - ^^^ Use of invalid bit size 42 - ~~~ Allowed bit sizes for integers are 1, 8, 16, 32, 64, 128 - } - "#; - check_errors!(src); -} - #[named] #[test] fn mutable_reference_to_array_element_as_func_arg() { diff --git a/test_programs/compile_failure/generics_on_integer_type/Nargo.toml b/test_programs/compile_failure/generics_on_integer_type/Nargo.toml new file mode 100644 index 00000000000..2798da2c4cd --- /dev/null +++ b/test_programs/compile_failure/generics_on_integer_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "generics_on_integer_type" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/generics_on_integer_type/src/main.nr b/test_programs/compile_failure/generics_on_integer_type/src/main.nr new file mode 100644 index 00000000000..6f28ecb7884 --- /dev/null +++ b/test_programs/compile_failure/generics_on_integer_type/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + let _: i32 = 1; + let _ = i32::::default(); +} diff --git a/test_programs/compile_failure/generics_on_integer_type/stderr.txt b/test_programs/compile_failure/generics_on_integer_type/stderr.txt new file mode 100644 index 00000000000..48fdca5ffa8 --- /dev/null +++ b/test_programs/compile_failure/generics_on_integer_type/stderr.txt @@ -0,0 +1,15 @@ +error: i32 expects 0 generics but 1 was given + ┌─ src/main.nr:2:12 + │ +2 │ let _: i32 = 1; + │ --- + │ + +error: i32 expects 0 generics but 1 was given + ┌─ src/main.nr:3:16 + │ +3 │ let _ = i32::::default(); + │ -------- + │ + +Aborting due to 2 previous errors diff --git a/test_programs/compile_failure/noirc_frontend_tests_do_not_infer_globals_to_u32_from_type_use/stderr.txt b/test_programs/compile_failure/noirc_frontend_tests_do_not_infer_globals_to_u32_from_type_use/stderr.txt index 93d3c141647..becea39cd06 100644 --- a/test_programs/compile_failure/noirc_frontend_tests_do_not_infer_globals_to_u32_from_type_use/stderr.txt +++ b/test_programs/compile_failure/noirc_frontend_tests_do_not_infer_globals_to_u32_from_type_use/stderr.txt @@ -27,17 +27,17 @@ error: The numeric generic is not of type `u32` │ error: The numeric generic is not of type `u32` - ┌─ src/main.nr:8:21 + ┌─ src/main.nr:8:25 │ 8 │ let _b: str = "hi"; - │ ------------ expected `u32`, found `Field` + │ ------- expected `u32`, found `Field` │ error: The numeric generic is not of type `u32` - ┌─ src/main.nr:9:21 + ┌─ src/main.nr:9:28 │ 9 │ let _c: fmtstr = f"hi"; - │ ---------------------- expected `u32`, found `Field` + │ ----------- expected `u32`, found `Field` │ Aborting due to 6 previous errors diff --git a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/Nargo.toml b/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/Nargo.toml deleted file mode 100644 index 6fe0c532fd0..00000000000 --- a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ - - [package] - name = "noirc_frontend_tests_errors_on_invalid_integer_bit_size" - type = "bin" - authors = [""] - - [dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/src/main.nr b/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/src/main.nr deleted file mode 100644 index 1e233814546..00000000000 --- a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ - - fn main() { - let _: u42 = 4; - } - \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/src_hash.txt b/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/src_hash.txt deleted file mode 100644 index 8a99b5ec656..00000000000 --- a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/src_hash.txt +++ /dev/null @@ -1 +0,0 @@ -10798683815366631506 \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/stderr.txt b/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/stderr.txt deleted file mode 100644 index 4e7d2724e29..00000000000 --- a/test_programs/compile_failure/noirc_frontend_tests_errors_on_invalid_integer_bit_size/stderr.txt +++ /dev/null @@ -1,8 +0,0 @@ -error: Use of invalid bit size 42 - ┌─ src/main.nr:3:16 - │ -3 │ let _: u42 = 4; - │ --- Allowed bit sizes for integers are 1, 8, 16, 32, 64, 128 - │ - -Aborting due to 1 previous error diff --git a/test_programs/compile_failure/restricted_bit_sizes/stderr.txt b/test_programs/compile_failure/restricted_bit_sizes/stderr.txt index dca8018c215..dbce82387b7 100644 --- a/test_programs/compile_failure/restricted_bit_sizes/stderr.txt +++ b/test_programs/compile_failure/restricted_bit_sizes/stderr.txt @@ -5,11 +5,11 @@ warning: unused import assert_constant │ --------------- unused import │ -error: Use of invalid bit size 63 +error: Could not resolve 'u63' in path ┌─ src/main.nr:3:18 │ 3 │ fn main() -> pub u63 { - │ --- Allowed bit sizes for integers are 1, 8, 16, 32, 64, 128 + │ --- │ -Aborting due to 1 previous error \ No newline at end of file +Aborting due to 1 previous error diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 7fdd8212eed..4ab5f639d7c 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -26,6 +26,7 @@ use noirc_frontend::{ Statement, TraitBound, TraitImplItemKind, TypeImpl, TypePath, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, Visitor, }, + elaborator::PrimitiveType, graph::{CrateId, Dependency}, hir::{ def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}, @@ -319,7 +320,22 @@ impl<'a> NodeFinder<'a> { if idents.is_empty() { module_id = self.module_id; } else { - let Some(module_def_id) = self.resolve_path(idents) else { + let Some(module_def_id) = self.resolve_path(idents.clone()) else { + // Check if the first segment refers to a primitive type + if idents.len() != 1 { + return; + } + let Some(primitive_type) = PrimitiveType::lookup_by_name(idents[0].as_str()) else { + return; + }; + let typ = primitive_type.to_type(); + self.complete_type_methods( + &typ, + &prefix, + FunctionKind::Any, + FunctionCompletionKind::NameAndParameters, + false, // self_prefix + ); return; }; diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index 20c78a87a6a..1c7935a18d3 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -1,5 +1,5 @@ use async_lsp::lsp_types::CompletionItemKind; -use noirc_frontend::{ast::AttributeTarget, token::Keyword}; +use noirc_frontend::{ast::AttributeTarget, elaborator::PrimitiveType, token::Keyword}; use strum::IntoEnumIterator; use super::{ @@ -63,24 +63,13 @@ impl NodeFinder<'_> { } pub(super) fn builtin_types_completion(&mut self, prefix: &str) { - for keyword in Keyword::iter() { - if let Some(typ) = keyword_builtin_type(&keyword) { - if name_matches(typ, prefix) { - self.completion_items.push(simple_completion_item( - typ, - CompletionItemKind::STRUCT, - Some(typ.to_string()), - )); - } - } - } - - for typ in builtin_integer_types() { - if name_matches(typ, prefix) { + for primitive_type in PrimitiveType::iter() { + let name = primitive_type.name(); + if name_matches(name, prefix) { self.completion_items.push(simple_completion_item( - typ, + name, CompletionItemKind::STRUCT, - Some(typ.to_string()), + Some(name.to_string()), )); } } @@ -190,74 +179,6 @@ impl NodeFinder<'_> { } } -pub(super) fn builtin_integer_types() -> [&'static str; 9] { - ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "u128"] -} - -/// If a keyword corresponds to a built-in type, returns that type's name. -pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { - match keyword { - Keyword::Bool => Some("bool"), - Keyword::CtString => Some("CtString"), - Keyword::EnumDefinition => Some("EnumDefinition"), - Keyword::Expr => Some("Expr"), - Keyword::Field => Some("Field"), - Keyword::FunctionDefinition => Some("FunctionDefinition"), - Keyword::Module => Some("Module"), - Keyword::Quoted => Some("Quoted"), - Keyword::StructDefinition => Some("TypeDefinition"), - Keyword::TraitConstraint => Some("TraitConstraint"), - Keyword::TraitDefinition => Some("TraitDefinition"), - Keyword::TraitImpl => Some("TraitImpl"), - Keyword::TypeDefinition => Some("TypeDefinition"), - Keyword::TypedExpr => Some("TypedExpr"), - Keyword::TypeType => Some("Type"), - Keyword::UnresolvedType => Some("UnresolvedType"), - - Keyword::As - | Keyword::Assert - | Keyword::AssertEq - | Keyword::Break - | Keyword::CallData - | Keyword::Char - | Keyword::Comptime - | Keyword::Constrain - | Keyword::Continue - | Keyword::Contract - | Keyword::Crate - | Keyword::Dep - | Keyword::Else - | Keyword::Enum - | Keyword::Fn - | Keyword::For - | Keyword::FormatString - | Keyword::Global - | Keyword::If - | Keyword::Impl - | Keyword::In - | Keyword::Let - | Keyword::Loop - | Keyword::Match - | Keyword::Mod - | Keyword::Mut - | Keyword::Pub - | Keyword::Return - | Keyword::ReturnData - | Keyword::String - | Keyword::Struct - | Keyword::Super - | Keyword::TopLevelItem - | Keyword::Trait - | Keyword::Type - | Keyword::Unchecked - | Keyword::Unconstrained - | Keyword::Unsafe - | Keyword::Use - | Keyword::Where - | Keyword::While => None, - } -} - pub(super) struct BuiltInFunction { pub(super) name: &'static str, pub(super) parameters: &'static str, @@ -279,26 +200,18 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option Option Unre UnresolvedTypeData::Expression(expr) => { UnresolvedTypeData::Expression(unresolved_type_expression_with_file(expr, file)) } - UnresolvedTypeData::String(expr) => { - UnresolvedTypeData::String(unresolved_type_expression_with_file(expr, file)) - } - UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( - unresolved_type_expression_with_file(expr, file), - Box::new(unresolved_type_with_file(*typ, file)), - ), UnresolvedTypeData::Parenthesized(typ) => { UnresolvedTypeData::Parenthesized(Box::new(unresolved_type_with_file(*typ, file))) } @@ -503,13 +496,9 @@ fn unresolved_type_data_with_file(typ: UnresolvedTypeData, file: FileId) -> Unre UnresolvedTypeData::AsTraitPath(as_trait_path) => { UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path_with_file(*as_trait_path, file))) } - UnresolvedTypeData::Quoted(..) - | UnresolvedTypeData::Resolved(..) + UnresolvedTypeData::Resolved(..) | UnresolvedTypeData::Interned(..) | UnresolvedTypeData::Unit - | UnresolvedTypeData::Bool - | UnresolvedTypeData::Integer(..) - | UnresolvedTypeData::FieldElement | UnresolvedTypeData::Unspecified | UnresolvedTypeData::Error => typ, } diff --git a/tooling/nargo_cli/tests/snapshots/expand/compile_success_empty/numeric_generics_explicit/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/expand/compile_success_empty/numeric_generics_explicit/execute__tests__expanded.snap index 96132a2cdbb..15daeed2500 100644 --- a/tooling/nargo_cli/tests/snapshots/expand/compile_success_empty/numeric_generics_explicit/execute__tests__expanded.snap +++ b/tooling/nargo_cli/tests/snapshots/expand/compile_success_empty/numeric_generics_explicit/execute__tests__expanded.snap @@ -36,8 +36,8 @@ impl MyStruct { fn insert(mut self, index: Field, elem: Field) -> Self { assert((index as u32) < S); { - let i_3796: u32 = index as u32; - self.data[i_3796] = elem; + let i_3799: u32 = index as u32; + self.data[i_3799] = elem; }; self } diff --git a/tooling/nargo_fmt/src/formatter/expression.rs b/tooling/nargo_fmt/src/formatter/expression.rs index 74bb997a5c7..6bdff60cf3a 100644 --- a/tooling/nargo_fmt/src/formatter/expression.rs +++ b/tooling/nargo_fmt/src/formatter/expression.rs @@ -399,37 +399,7 @@ impl ChunkFormatter<'_, '_> { formatter.write_token(Token::Less); } - // Special handling so that `str::foo` is formatted as `str::::foo` (and similarly for `fmtstr`) - match type_path.typ.typ { - UnresolvedTypeData::String(length) => { - formatter.write_keyword(Keyword::String); - formatter.skip_comments_and_whitespace(); - if formatter.is_at(Token::DoubleColon) { - formatter.write_token(Token::DoubleColon); - } else { - formatter.write("::"); - } - formatter.write_token(Token::Less); - formatter.format_type_expression(length); - formatter.write_token(Token::Greater); - } - UnresolvedTypeData::FormatString(length, element) => { - formatter.write_keyword(Keyword::FormatString); - formatter.skip_comments_and_whitespace(); - if formatter.is_at(Token::DoubleColon) { - formatter.write_token(Token::DoubleColon); - } else { - formatter.write("::"); - } - formatter.write_token(Token::Less); - formatter.format_type_expression(length); - formatter.write_token(Token::Comma); - formatter.write_space(); - formatter.format_type(*element); - formatter.write_token(Token::Greater); - } - _ => formatter.format_type(type_path.typ), - } + formatter.format_type(type_path.typ); if nameless { formatter.write_token(Token::Greater); @@ -2174,34 +2144,6 @@ global y = 1; assert_format(src, expected); } - #[test] - fn format_type_path_for_str_with_colons() { - let src = "global x = str :: < N > :: max ;"; - let expected = "global x = str::::max;\n"; - assert_format(src, expected); - } - - #[test] - fn format_type_path_for_str_missing_colons() { - let src = "global x = str < N > :: max ;"; - let expected = "global x = str::::max;\n"; - assert_format(src, expected); - } - - #[test] - fn format_type_path_for_fmtstr_with_colons() { - let src = "global x = fmtstr :: < A , B > :: max ;"; - let expected = "global x = fmtstr::::max;\n"; - assert_format(src, expected); - } - - #[test] - fn format_type_path_for_fmtstr_missing_colons() { - let src = "global x = fmtstr < A , B > :: max ;"; - let expected = "global x = fmtstr::::max;\n"; - assert_format(src, expected); - } - #[test] fn format_if_expression_without_else_one_expression() { let src = "global x = if 1 { 2 } ;"; diff --git a/tooling/nargo_fmt/src/formatter/types.rs b/tooling/nargo_fmt/src/formatter/types.rs index 592c7bcdd8f..1e475614ba6 100644 --- a/tooling/nargo_fmt/src/formatter/types.rs +++ b/tooling/nargo_fmt/src/formatter/types.rs @@ -14,12 +14,6 @@ impl Formatter<'_> { self.write_left_paren(); self.write_right_paren(); } - UnresolvedTypeData::Bool => { - self.write_keyword(Keyword::Bool); - } - UnresolvedTypeData::Integer(..) | UnresolvedTypeData::FieldElement => { - self.write_current_token_and_bump(); - } UnresolvedTypeData::Array(type_expr, typ) => { self.write_left_bracket(); self.format_type(*typ); @@ -36,21 +30,6 @@ impl Formatter<'_> { UnresolvedTypeData::Expression(type_expr) => { self.format_type_expression(type_expr); } - UnresolvedTypeData::String(type_expr) => { - self.write_keyword(Keyword::String); - self.write_token(Token::Less); - self.format_type_expression(type_expr); - self.write_token(Token::Greater); - } - UnresolvedTypeData::FormatString(type_expr, typ) => { - self.write_keyword(Keyword::FormatString); - self.write_token(Token::Less); - self.format_type_expression(type_expr); - self.write_comma(); - self.write_space(); - self.format_type(*typ); - self.write_token(Token::Greater); - } UnresolvedTypeData::Parenthesized(typ) => { self.write_left_paren(); self.format_type(*typ); @@ -157,9 +136,6 @@ impl Formatter<'_> { self.format_type(*return_type); } } - UnresolvedTypeData::Quoted(..) => { - self.write_current_token_and_bump(); - } UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.format_as_trait_path(*as_trait_path); }