diff --git a/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/compiler/noirc_frontend/src/elaborator/path_resolution.rs index f32cdb827cd..2572283d05c 100644 --- a/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -49,7 +49,7 @@ pub(crate) enum PathResolutionItem { /// A function call on a type that resolves to a trait method, for example `SomeType::from(...)` /// or `SomeType::::from(..).`. The main difference from `TraitFunction` is that this /// holds the self type, in this case `SomeType`. - TypeTraitFunction(Type, TraitId, Option, FuncId), + TypeTraitFunction(Type, TraitId, FuncId), /// A function call on a primitive type, for example `u64::from(...)` or `u64::::from(..)`. PrimitiveFunction(PrimitiveType, Option, FuncId), } @@ -62,7 +62,7 @@ impl PathResolutionItem { | PathResolutionItem::SelfMethod(func_id) | PathResolutionItem::TypeAliasFunction(_, _, func_id) | PathResolutionItem::TraitFunction(_, _, func_id) - | PathResolutionItem::TypeTraitFunction(_, _, _, func_id) + | PathResolutionItem::TypeTraitFunction(_, _, func_id) | PathResolutionItem::PrimitiveFunction(_, _, func_id) => Some(*func_id), PathResolutionItem::Module(..) | PathResolutionItem::Type(..) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 669926cbd5a..dd533cbafb2 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -8,14 +8,14 @@ use crate::{ ERROR_IDENT, Expression, ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, Path, PathSegment, Pattern, TypePath, }, - elaborator::types::SELF_TYPE_NAME, + elaborator::{ + Turbofish, + types::{SELF_TYPE_NAME, TraitPathResolutionMethod}, + }, hir::{ def_collector::dc_crate::CompilationError, resolution::{errors::ResolverError, import::PathResolutionError}, - type_check::{ - Source, TypeCheckError, - generics::{FmtstrPrimitiveType, Generic, StrPrimitiveType}, - }, + type_check::{Source, TypeCheckError}, }, hir_def::{ expr::{HirExpression, HirIdent, HirLiteral, HirMethodReference, ImplKind, TraitMethod}, @@ -23,12 +23,13 @@ use crate::{ }, node_interner::{ DefinitionId, DefinitionInfo, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind, + TypeAliasId, TypeId, }, signed_field::SignedField, }; use super::{ - Elaborator, PrimitiveType, ResolverMeta, + Elaborator, ResolverMeta, path_resolution::{PathResolutionItem, TypedPath, TypedPathSegment}, }; @@ -768,15 +769,7 @@ impl Elaborator<'_> { ) -> (Vec, Option) { match item { PathResolutionItem::Method(struct_id, Some(generics), _func_id) => { - let struct_type = self.interner.get_type(struct_id); - let struct_type = struct_type.borrow(); - let struct_generics = struct_type.instantiate(self.interner); - let generics = self.resolve_struct_turbofish_generics( - &struct_type, - struct_generics, - Some(generics.generics), - generics.location, - ); + let generics = self.resolve_struct_id_turbofish_generics(struct_id, Some(generics)); (generics, None) } PathResolutionItem::SelfMethod(_) => { @@ -790,21 +783,8 @@ impl Elaborator<'_> { PathResolutionItem::TypeAliasFunction(type_alias_id, generics, _func_id) => { let type_alias = self.interner.get_type_alias(type_alias_id); let type_alias = type_alias.borrow(); - let alias_generics = vecmap(&type_alias.generics, |generic| { - self.interner.next_type_variable_with_kind(generic.kind()) - }); - - // First solve the generics on the alias, if any - let generics = if let Some(generics) = generics { - self.resolve_alias_turbofish_generics( - &type_alias, - alias_generics, - Some(generics.generics), - generics.location, - ) - } else { - alias_generics - }; + let generics = + self.resolve_type_alias_id_turbofish_generics(type_alias_id, generics); // Now instantiate the underlying struct or alias with those generics, the struct might // have more generics than those in the alias, like in this example: @@ -828,101 +808,19 @@ impl Elaborator<'_> { ); (generics, None) } - PathResolutionItem::TypeTraitFunction(self_type, trait_id, generics, _func_id) => { - let generics = if let Some(generics) = generics { - let trait_ = self.interner.get_trait(trait_id); - let kinds = vecmap(&trait_.generics, |generic| generic.kind()); - let trait_generics = vecmap(&kinds, |kind| { - self.interner.next_type_variable_with_kind(kind.clone()) - }); - - self.resolve_trait_turbofish_generics( - &trait_.name.to_string(), - kinds, - trait_generics, - Some(generics.generics), - generics.location, - ) - } else { - Vec::new() - }; - (generics, Some(self_type)) + PathResolutionItem::TypeTraitFunction(self_type, _trait_id, _func_id) => { + (Vec::new(), Some(self_type)) } PathResolutionItem::PrimitiveFunction(primitive_type, turbofish, _func_id) => { - let generics = 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() + let typ = self.instantiate_primitive_type_with_turbofish(primitive_type, turbofish); + let generics = match typ { + Type::String(length) => { + vec![*length] } - 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() - } + Type::FmtString(length, element) => { + vec![*length, *element] } + _ => Vec::new(), }; (generics, None) } @@ -938,20 +836,74 @@ impl Elaborator<'_> { } } + pub(super) fn resolve_struct_id_turbofish_generics( + &mut self, + struct_id: TypeId, + mut turbofish: Option, + ) -> Vec { + let struct_type = self.interner.get_type(struct_id); + let struct_type = struct_type.borrow(); + let struct_generics = struct_type.instantiate(self.interner); + if let Some(turbofish) = turbofish.take() { + self.resolve_struct_turbofish_generics( + &struct_type, + struct_generics, + Some(turbofish.generics), + turbofish.location, + ) + } else { + struct_generics + } + } + + pub(super) fn resolve_type_alias_id_turbofish_generics( + &mut self, + type_alias_id: TypeAliasId, + generics: Option, + ) -> Vec { + let type_alias = self.interner.get_type_alias(type_alias_id); + let type_alias = type_alias.borrow(); + let alias_generics = vecmap(&type_alias.generics, |generic| { + self.interner.next_type_variable_with_kind(generic.kind()) + }); + + if let Some(generics) = generics { + self.resolve_alias_turbofish_generics( + &type_alias, + alias_generics, + Some(generics.generics), + generics.location, + ) + } else { + alias_generics + } + } + fn resolve_variable(&mut self, path: TypedPath) -> (HirIdent, Option) { if let Some(trait_path_resolution) = self.resolve_trait_generic_path(&path) { for error in trait_path_resolution.errors { 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, - ); + return match trait_path_resolution.method { + TraitPathResolutionMethod::NotATraitMethod(func_id) => ( + HirIdent { + location: path.location, + id: self.interner.function_definition_id(func_id), + impl_kind: ImplKind::NotATraitMethod, + }, + trait_path_resolution.item, + ), + + TraitPathResolutionMethod::TraitMethod(trait_method) => ( + HirIdent { + location: path.location, + id: self.interner.trait_method_id(trait_method.method_id), + impl_kind: ImplKind::TraitMethod(trait_method), + }, + trait_path_resolution.item, + ), + }; } // If the Path is being used as an Expression, then it is referring to a global from a separate module diff --git a/compiler/noirc_frontend/src/elaborator/primitive_types.rs b/compiler/noirc_frontend/src/elaborator/primitive_types.rs index 88a0695b454..7c49df2de15 100644 --- a/compiler/noirc_frontend/src/elaborator/primitive_types.rs +++ b/compiler/noirc_frontend/src/elaborator/primitive_types.rs @@ -1,4 +1,18 @@ -use crate::{QuotedType, Type, ast::IntegerBitSize, shared::Signedness}; +use noirc_errors::Location; + +use crate::{ + QuotedType, Type, + ast::{GenericTypeArgs, IntegerBitSize}, + elaborator::{Elaborator, PathResolutionMode, Turbofish}, + hir::{ + def_collector::dc_crate::CompilationError, + type_check::{ + TypeCheckError, + generics::{FmtstrPrimitiveType, Generic as _, StrPrimitiveType}, + }, + }, + shared::Signedness, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq, strum_macros::EnumIter)] pub enum PrimitiveType { @@ -164,3 +178,167 @@ impl PrimitiveType { } } } + +impl Elaborator<'_> { + pub(crate) 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() + } + + pub(crate) fn instantiate_primitive_type_with_turbofish( + &mut self, + primitive_type: PrimitiveType, + turbofish: Option, + ) -> 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 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, + }, + )); + } + primitive_type.to_type() + } + PrimitiveType::Str => { + 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)]; + let mut args = if let Some(turbofish) = turbofish { + self.resolve_item_turbofish_generics( + item.item_kind(), + &item.item_name(self.interner), + item_generic_kinds, + generics, + Some(turbofish.generics), + turbofish.location, + ) + } else { + generics + }; + assert_eq!(args.len(), 1); + let length = args.pop().unwrap(); + Type::String(Box::new(length)) + } + PrimitiveType::Fmtstr => { + 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)]; + let mut args = if let Some(turbofish) = turbofish { + self.resolve_item_turbofish_generics( + "primitive type", + "fmtstr", + item_generic_kinds, + generics, + Some(turbofish.generics), + turbofish.location, + ) + } else { + generics + }; + assert_eq!(args.len(), 2); + let element = args.pop().unwrap(); + let length = args.pop().unwrap(); + Type::FmtString(Box::new(length), Box::new(element)) + } + } + } +} diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index f5a595d8853..0e1d7d18b99 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::{FmtstrPrimitiveType, Generic, StrPrimitiveType, TraitGenerics}, + generics::{Generic, TraitGenerics}, }, }, hir_def::{ @@ -43,17 +43,21 @@ use crate::{ use super::{ Elaborator, FunctionContext, PathResolutionTarget, UnsafeBlockStatus, lints, path_resolution::{PathResolutionItem, PathResolutionMode, TypedPath}, - primitive_types::PrimitiveType, }; pub const SELF_TYPE_NAME: &str = "Self"; pub(super) struct TraitPathResolution { - pub(super) method: TraitMethod, + pub(super) method: TraitPathResolutionMethod, pub(super) item: Option, pub(super) errors: Vec, } +pub(super) enum TraitPathResolutionMethod { + NotATraitMethod(FuncId), + TraitMethod(TraitMethod), +} + impl Elaborator<'_> { pub(crate) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { self.resolve_type_inner(typ, &Kind::Normal, PathResolutionMode::MarkAsReferenced) @@ -348,80 +352,6 @@ impl Elaborator<'_> { } } - 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, @@ -803,11 +733,9 @@ impl Elaborator<'_> { let the_trait = self.interner.get_trait(trait_id); let method = the_trait.find_method(method.as_str())?; let constraint = the_trait.as_constraint(path.location); - return Some(TraitPathResolution { - method: TraitMethod { method_id: method, constraint, assumed: true }, - item: None, - errors: Vec::new(), - }); + let trait_method = TraitMethod { method_id: method, constraint, assumed: true }; + let method = TraitPathResolutionMethod::TraitMethod(trait_method); + return Some(TraitPathResolution { method, item: None, errors: Vec::new() }); } } None @@ -824,11 +752,10 @@ impl Elaborator<'_> { let the_trait = self.interner.get_trait(meta.trait_id?); let method = the_trait.find_method(path.last_name())?; let constraint = the_trait.as_constraint(path.location); - Some(TraitPathResolution { - method: TraitMethod { method_id: method, constraint, assumed: false }, - item: Some(path_resolution.item), - errors: path_resolution.errors, - }) + let trait_method = TraitMethod { method_id: method, constraint, assumed: false }; + let method = TraitPathResolutionMethod::TraitMethod(trait_method); + let item = Some(path_resolution.item); + Some(TraitPathResolution { method, item, errors: path_resolution.errors }) } // This resolves a static trait method T::trait_method by iterating over the where clause @@ -853,11 +780,9 @@ impl Elaborator<'_> { let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id); if let Some(method) = the_trait.find_method(path.last_name()) { - return Some(TraitPathResolution { - method: TraitMethod { method_id: method, constraint, assumed: true }, - item: None, - errors: Vec::new(), - }); + let trait_method = TraitMethod { method_id: method, constraint, assumed: true }; + let method = TraitPathResolutionMethod::TraitMethod(trait_method); + return Some(TraitPathResolution { method, item: None, errors: Vec::new() }); } } } @@ -874,15 +799,39 @@ impl Elaborator<'_> { let location = path.location; let last_segment = path.pop(); let before_last_segment = path.last_segment(); + let turbofish = before_last_segment.turbofish(); let path_resolution = self.use_path_as_type(path).ok()?; - let PathResolutionItem::Type(type_id) = path_resolution.item else { - return None; + let typ = match path_resolution.item { + PathResolutionItem::Type(type_id) => { + let generics = self.resolve_struct_id_turbofish_generics(type_id, turbofish); + let datatype = self.get_type(type_id); + Type::DataType(datatype, generics) + } + PathResolutionItem::TypeAlias(type_alias_id) => { + let generics = + self.resolve_type_alias_id_turbofish_generics(type_alias_id, turbofish); + let type_alias = self.interner.get_type_alias(type_alias_id); + let type_alias = type_alias.borrow(); + type_alias.get_type(&generics) + } + PathResolutionItem::PrimitiveType(primitive_type) => { + self.instantiate_primitive_type_with_turbofish(primitive_type, turbofish) + } + PathResolutionItem::Module(..) + | PathResolutionItem::Trait(..) + | PathResolutionItem::Global(..) + | PathResolutionItem::ModuleFunction(..) + | PathResolutionItem::Method(..) + | PathResolutionItem::SelfMethod(..) + | PathResolutionItem::TypeAliasFunction(..) + | PathResolutionItem::TraitFunction(..) + | PathResolutionItem::TypeTraitFunction(..) + | PathResolutionItem::PrimitiveFunction(..) => { + return None; + } }; - let datatype = self.get_type(type_id); - let generics = datatype.borrow().instantiate(self.interner); - let typ = Type::DataType(datatype, generics); let method_name = last_segment.ident.as_str(); // If we can find a method on the type, this is definitely not a trait method @@ -891,6 +840,7 @@ impl Elaborator<'_> { } let trait_methods = self.interner.lookup_trait_methods(&typ, method_name, false); + if trait_methods.is_empty() { return None; } @@ -899,24 +849,36 @@ impl Elaborator<'_> { self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.location); let hir_method_reference = hir_method_reference?; let func_id = hir_method_reference.func_id(self.interner)?; - let HirMethodReference::TraitMethodId(trait_method_id, _, _) = hir_method_reference else { - return None; - }; + match hir_method_reference { + HirMethodReference::FuncId(func_id) => { + // It could happen that we find a single function (one in a trait impl) + let mut errors = path_resolution.errors; + if let Some(error) = error { + errors.push(error); + } - let trait_id = trait_method_id.trait_id; - let trait_ = self.interner.get_trait(trait_id); - let mut constraint = trait_.as_constraint(location); - constraint.typ = typ.clone(); + let method = TraitPathResolutionMethod::NotATraitMethod(func_id); + Some(TraitPathResolution { method, item: None, errors }) + } + HirMethodReference::TraitMethodId(trait_method_id, _, _) => { + let trait_id = trait_method_id.trait_id; + let trait_ = self.interner.get_trait(trait_id); + let mut constraint = trait_.as_constraint(location); + constraint.typ = typ.clone(); - let method = TraitMethod { method_id: trait_method_id, constraint, assumed: false }; - let turbofish = before_last_segment.turbofish(); - let item = PathResolutionItem::TypeTraitFunction(typ, trait_id, turbofish, func_id); - let mut errors = path_resolution.errors; - if let Some(error) = error { - errors.push(error); - } + let trait_method = + TraitMethod { method_id: trait_method_id, constraint, assumed: false }; + let item = PathResolutionItem::TypeTraitFunction(typ, trait_id, func_id); - Some(TraitPathResolution { method, item: Some(item), errors }) + let mut errors = path_resolution.errors; + if let Some(error) = error { + errors.push(error); + } + + let method = TraitPathResolutionMethod::TraitMethod(trait_method); + Some(TraitPathResolution { method, item: Some(item), errors }) + } + } } // Try to resolve the given trait method path. diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 5296f0df56e..d611d9b7aa9 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1237,32 +1237,6 @@ fn calls_trait_method_using_struct_name_when_multiple_impls_exist() { assert_no_errors!(src); } -#[named] -#[test] -fn calls_trait_method_using_struct_name_when_multiple_impls_exist_and_errors_turbofish() { - let src = r#" - trait From2 { - fn from2(input: T) -> Self; - } - struct U60Repr {} - impl From2<[Field; 3]> for U60Repr { - fn from2(_: [Field; 3]) -> Self { - U60Repr {} - } - } - impl From2 for U60Repr { - fn from2(_: Field) -> Self { - U60Repr {} - } - } - fn main() { - let _ = U60Repr::::from2([1, 2, 3]); - ^^^^^^^^^ Expected type Field, found type [Field; 3] - } - "#; - check_errors!(src); -} - #[named] #[test] fn as_trait_path_in_expression() { @@ -1668,3 +1642,30 @@ fn serialize_test_with_a_previous_unrelated_definition() { "#; check_monomorphization_error!(&src); } + +#[named] +#[test] +fn errors_on_incorrect_generics_in_type_trait_call() { + let src = r#" + trait From2 { + fn from2(input: T) -> Self; + } + struct U60Repr {} + impl From2<[Field; 3]> for U60Repr { + fn from2(_: [Field; 3]) -> Self { + U60Repr {} + } + } + impl From2 for U60Repr { + fn from2(_: Field) -> Self { + U60Repr {} + } + } + + fn main() { + let _ = U60Repr::::from2([1, 2, 3]); + ^^^^^^^^^ struct U60Repr expects 0 generics but 1 was given + } + "#; + check_errors!(src); +} diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/Nargo.toml b/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/Nargo.toml new file mode 100644 index 00000000000..aba41f89606 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/src/main.nr b/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/src/main.nr new file mode 100644 index 00000000000..1295bc9398e --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/src/main.nr @@ -0,0 +1,20 @@ + + trait From2 { + fn from2(input: T) -> Self; + } + struct U60Repr {} + impl From2<[Field; 3]> for U60Repr { + fn from2(_: [Field; 3]) -> Self { + U60Repr {} + } + } + impl From2 for U60Repr { + fn from2(_: Field) -> Self { + U60Repr {} + } + } + + fn main() { + let _ = U60Repr::::from2([1, 2, 3]); + } + \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/src_hash.txt b/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/src_hash.txt new file mode 100644 index 00000000000..140ee8d7b58 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/src_hash.txt @@ -0,0 +1 @@ +15773000329539722749 \ No newline at end of file diff --git a/test_programs/compile_success_empty/alias_trait_method_call_multiple_candidates/Nargo.toml b/test_programs/compile_success_empty/alias_trait_method_call_multiple_candidates/Nargo.toml new file mode 100644 index 00000000000..08d93ee14ee --- /dev/null +++ b/test_programs/compile_success_empty/alias_trait_method_call_multiple_candidates/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "alias_trait_method_call_multiple_candidates" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/alias_trait_method_call_multiple_candidates/src/main.nr b/test_programs/compile_success_empty/alias_trait_method_call_multiple_candidates/src/main.nr new file mode 100644 index 00000000000..284c8917519 --- /dev/null +++ b/test_programs/compile_success_empty/alias_trait_method_call_multiple_candidates/src/main.nr @@ -0,0 +1,34 @@ +use dep::std::convert::From; + +struct MyU128 { + lo: Field, + hi: Field, +} + +impl MyU128 { + pub fn from_u64s_le(lo: u64, hi: u64) -> MyU128 { + MyU128 { lo: lo as Field, hi: hi as Field } + } +} + +impl From for MyU128 { + fn from(value: u64) -> MyU128 { + MyU128::from_u64s_le(value, 0) + } +} + +impl From for MyU128 { + fn from(value: u32) -> MyU128 { + MyU128::from(value as u64) + } +} + +type MyU128Alias = MyU128; + +fn main() { + let x: u64 = 0; + let mut small_int = MyU128Alias::from(x); + assert(small_int.lo == x as Field); + let u32_3: u32 = 3; + assert(MyU128::from(u32_3).lo == 3); +} diff --git a/test_programs/compile_success_empty/primitive_trait_method_call_multiple_candidates/Nargo.toml b/test_programs/compile_success_empty/primitive_trait_method_call_multiple_candidates/Nargo.toml new file mode 100644 index 00000000000..56a74848fe4 --- /dev/null +++ b/test_programs/compile_success_empty/primitive_trait_method_call_multiple_candidates/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "primitive_trait_method_call_multiple_candidates" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/primitive_trait_method_call_multiple_candidates/src/main.nr b/test_programs/compile_success_empty/primitive_trait_method_call_multiple_candidates/src/main.nr new file mode 100644 index 00000000000..94024e420d0 --- /dev/null +++ b/test_programs/compile_success_empty/primitive_trait_method_call_multiple_candidates/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + let x: u64 = 0; + let f = Field::from; + let _ = f(x); + + let _ = str::from([1, 2, 3]); +} diff --git a/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/Nargo.toml b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/Nargo.toml new file mode 100644 index 00000000000..4ca218975d2 --- /dev/null +++ b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "type_trait_method_call_multiple_candidates_with_turbofish" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/src/main.nr b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/src/main.nr new file mode 100644 index 00000000000..2a03b9e5cf5 --- /dev/null +++ b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/src/main.nr @@ -0,0 +1,25 @@ +pub struct Struct { + pub x: T, +} + +pub trait From2 { + fn from2(input: T) -> Self; +} + +impl From2 for Struct { + fn from2(_: u64) -> Struct { + Struct { x: 0 } + } +} + +impl From2 for Struct { + fn from2(_: u32) -> Struct { + Struct { x: 0 } + } +} + +fn main() { + let x = 1; + let f = Struct::::from2; + let _ = f(x); +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index e43be12b9aa..0471ba722e0 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -147,7 +147,9 @@ const TESTS_WITHOUT_STDOUT_CHECK: [&str; 0] = []; /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. /// (some are ignored on purpose for the same reason as `IGNORED_NARGO_EXPAND_EXECUTION_TESTS`) -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 21] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 24] = [ + // bug + "alias_trait_method_call_multiple_candidates", // bug "associated_type_bounds", // bug @@ -155,6 +157,8 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 21] = [ // There's no "src/main.nr" here so it's trickier to make this work "overlapping_dep_and_mod", // bug + "primitive_trait_method_call_multiple_candidates", + // bug "reexports", // bug "regression_7038", @@ -186,6 +190,8 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 21] = [ "trait_static_methods", // bug "type_trait_method_call_multiple_candidates", + // bug + "type_trait_method_call_multiple_candidates_with_turbofish", // There's no "src/main.nr" here so it's trickier to make this work "workspace_reexport_bug", // bug diff --git a/tooling/nargo_cli/tests/snapshots/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/execute__tests__stderr.snap b/tooling/nargo_cli/tests/snapshots/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/execute__tests__stderr.snap new file mode 100644 index 00000000000..c6402e8fd3b --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_failure/noirc_frontend_tests_traits_errors_on_incorrect_generics_in_type_trait_call/execute__tests__stderr.snap @@ -0,0 +1,12 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: stderr +--- +error: struct U60Repr expects 0 generics but 1 was given + ┌─ src/main.nr:18:24 + │ +18 │ let _ = U60Repr::::from2([1, 2, 3]); + │ --------- + │ + +Aborting due to 1 previous error diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/alias_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/alias_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..7de940827c8 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/alias_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": null, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : []" + ], + "debug_symbols": "XY5BCsQwCEXv4rqLWfcqw1BsaosgJtikMITefWyYQOlK/3/6tcJCc9km1jXuML4rzMYivE0SA2aO6m49B+hyykbkFty4byU00gyjFpEBDpTShvaE2mpGc/oagHTx6oErC13d+XGBge158UBjnIX+ci0abjR/Uyf942Qx0FKMrqTGPPsH", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/primitive_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/primitive_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..7de940827c8 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/primitive_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": null, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : []" + ], + "debug_symbols": "XY5BCsQwCEXv4rqLWfcqw1BsaosgJtikMITefWyYQOlK/3/6tcJCc9km1jXuML4rzMYivE0SA2aO6m49B+hyykbkFty4byU00gyjFpEBDpTShvaE2mpGc/oagHTx6oErC13d+XGBge158UBjnIX+ci0abjR/Uyf942Qx0FKMrqTGPPsH", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..7de940827c8 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/type_trait_method_call_multiple_candidates_with_turbofish/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": null, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : []" + ], + "debug_symbols": "XY5BCsQwCEXv4rqLWfcqw1BsaosgJtikMITefWyYQOlK/3/6tcJCc9km1jXuML4rzMYivE0SA2aO6m49B+hyykbkFty4byU00gyjFpEBDpTShvaE2mpGc/oagHTx6oErC13d+XGBge158UBjnIX+ci0abjR/Uyf942Qx0FKMrqTGPPsH", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +}