diff --git a/compiler/noirc_frontend/src/elaborator/enums.rs b/compiler/noirc_frontend/src/elaborator/enums.rs index b299463ddf1..aec8d8b9e36 100644 --- a/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/compiler/noirc_frontend/src/elaborator/enums.rs @@ -713,8 +713,9 @@ impl Elaborator<'_> { | PathResolutionItem::PrimitiveType(_) | PathResolutionItem::Trait(_) | PathResolutionItem::ModuleFunction(_) - | PathResolutionItem::TypeAliasFunction(_, _, _) - | PathResolutionItem::TraitFunction(_, _, _) + | PathResolutionItem::TypeAliasFunction(..) + | PathResolutionItem::TraitFunction(..) + | PathResolutionItem::TypeTraitFunction(..) | PathResolutionItem::PrimitiveFunction(..) => { // This variable refers to an existing item if let Some(name) = name { diff --git a/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/compiler/noirc_frontend/src/elaborator/path_resolution.rs index fe3543c5d36..f32cdb827cd 100644 --- a/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -35,12 +35,22 @@ pub(crate) enum PathResolutionItem { Trait(TraitId), // These are values + /// A reference to a global value. Global(GlobalId), + /// A function call on a module, for example `some::module::function()`. ModuleFunction(FuncId), Method(TypeId, Option, FuncId), + /// A function call on `Self`, for example `Self::function()`. Turbofish is not allowed here. SelfMethod(FuncId), + /// A function call on a type alias, for example `TypeAlias::function()`. TypeAliasFunction(TypeAliasId, Option, FuncId), + /// A function call on a trait, for example `Trait::function()` or `Trait::::function()`. TraitFunction(TraitId, Option, FuncId), + /// 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), + /// A function call on a primitive type, for example `u64::from(...)` or `u64::::from(..)`. PrimitiveFunction(PrimitiveType, Option, FuncId), } @@ -52,6 +62,7 @@ impl PathResolutionItem { | PathResolutionItem::SelfMethod(func_id) | PathResolutionItem::TypeAliasFunction(_, _, func_id) | PathResolutionItem::TraitFunction(_, _, func_id) + | PathResolutionItem::TypeTraitFunction(_, _, _, func_id) | PathResolutionItem::PrimitiveFunction(_, _, func_id) => Some(*func_id), PathResolutionItem::Module(..) | PathResolutionItem::Type(..) @@ -75,6 +86,7 @@ impl PathResolutionItem { | PathResolutionItem::SelfMethod(..) | PathResolutionItem::TypeAliasFunction(..) | PathResolutionItem::TraitFunction(..) + | PathResolutionItem::TypeTraitFunction(..) | PathResolutionItem::PrimitiveFunction(..) => "function", } } @@ -388,6 +400,7 @@ impl Elaborator<'_> { | PathResolutionItem::SelfMethod(..) | PathResolutionItem::TypeAliasFunction(..) | PathResolutionItem::TraitFunction(..) + | PathResolutionItem::TypeTraitFunction(..) | PathResolutionItem::PrimitiveFunction(..) => (), } resolution diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index aed246f8d17..669926cbd5a 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -8,6 +8,7 @@ use crate::{ ERROR_IDENT, Expression, ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, Path, PathSegment, Pattern, TypePath, }, + elaborator::types::SELF_TYPE_NAME, hir::{ def_collector::dc_crate::CompilationError, resolution::{errors::ResolverError, import::PathResolutionError}, @@ -579,7 +580,11 @@ impl Elaborator<'_> { let (expr, item) = self.resolve_variable(variable); let definition_id = expr.id; - let type_generics = item.map(|item| self.resolve_item_turbofish(item)).unwrap_or_default(); + let (type_generics, self_generic) = if let Some(item) = item { + self.resolve_item_turbofish_and_self_type(item) + } else { + (Vec::new(), None) + }; let definition = self.interner.try_definition(definition_id); let is_comptime_local = !self.in_comptime_context() @@ -596,9 +601,21 @@ impl Elaborator<'_> { None }; - // If this is a function call on a type that has generics, we need to bind those generic types. - if !type_generics.is_empty() { - if let Some(DefinitionKind::Function(func_id)) = &definition_kind { + if let Some(DefinitionKind::Function(func_id)) = &definition_kind { + // If there's a self type, bind it to the self type generic + if let Some(self_generic) = self_generic { + let func_generics = &self.interner.function_meta(func_id).all_generics; + let self_resolved_generic = + func_generics.iter().find(|generic| generic.name.as_str() == SELF_TYPE_NAME); + if let Some(self_resolved_generic) = self_resolved_generic { + let type_var = &self_resolved_generic.type_var; + bindings + .insert(type_var.id(), (type_var.clone(), type_var.kind(), self_generic)); + } + } + + // If this is a function call on a type that has generics, we need to bind those generic types. + if !type_generics.is_empty() { // `all_generics` will always have the enclosing type generics first, so we need to bind those let func_generics = &self.interner.function_meta(func_id).all_generics; for (type_generic, func_generic) in type_generics.into_iter().zip(func_generics) { @@ -745,25 +762,30 @@ impl Elaborator<'_> { /// foo::Bar::::baz /// ``` /// Solve `` above - fn resolve_item_turbofish(&mut self, item: PathResolutionItem) -> Vec { + fn resolve_item_turbofish_and_self_type( + &mut self, + item: PathResolutionItem, + ) -> (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); - self.resolve_struct_turbofish_generics( + let generics = self.resolve_struct_turbofish_generics( &struct_type, struct_generics, Some(generics.generics), generics.location, - ) + ); + (generics, None) } PathResolutionItem::SelfMethod(_) => { - if let Some(Type::DataType(_, generics)) = &self.self_type { + let generics = if let Some(Type::DataType(_, generics)) = &self.self_type { generics.clone() } else { Vec::new() - } + }; + (generics, None) } PathResolutionItem::TypeAliasFunction(type_alias_id, generics, _func_id) => { let type_alias = self.interner.get_type_alias(type_alias_id); @@ -788,7 +810,8 @@ impl Elaborator<'_> { // have more generics than those in the alias, like in this example: // // type Alias = Struct; - get_type_alias_generics(&type_alias, &generics) + let generics = get_type_alias_generics(&type_alias, &generics); + (generics, None) } PathResolutionItem::TraitFunction(trait_id, Some(generics), _func_id) => { let trait_ = self.interner.get_trait(trait_id); @@ -796,16 +819,37 @@ impl Elaborator<'_> { let trait_generics = vecmap(&kinds, |kind| self.interner.next_type_variable_with_kind(kind.clone())); - self.resolve_trait_turbofish_generics( + let generics = self.resolve_trait_turbofish_generics( &trait_.name.to_string(), kinds, trait_generics, Some(generics.generics), generics.location, - ) + ); + (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::PrimitiveFunction(primitive_type, turbofish, _func_id) => { - match primitive_type { + let generics = match primitive_type { PrimitiveType::Bool | PrimitiveType::CtString | PrimitiveType::Expr @@ -879,7 +923,8 @@ impl Elaborator<'_> { Vec::new() } } - } + }; + (generics, None) } PathResolutionItem::Method(_, None, _) | PathResolutionItem::TraitFunction(_, None, _) @@ -889,7 +934,7 @@ impl Elaborator<'_> { | PathResolutionItem::PrimitiveType(..) | PathResolutionItem::Trait(..) | PathResolutionItem::Global(..) - | PathResolutionItem::ModuleFunction(..) => Vec::new(), + | PathResolutionItem::ModuleFunction(..) => (Vec::new(), None), } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 8eb7dae73fe..f5a595d8853 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -906,15 +906,16 @@ impl Elaborator<'_> { 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; + constraint.typ = typ.clone(); let method = TraitMethod { method_id: trait_method_id, constraint, assumed: false }; let turbofish = before_last_segment.turbofish(); - let item = PathResolutionItem::TraitFunction(trait_id, turbofish, func_id); + let item = PathResolutionItem::TypeTraitFunction(typ, trait_id, turbofish, func_id); let mut errors = path_resolution.errors; if let Some(error) = error { errors.push(error); } + Some(TraitPathResolution { method, item: Some(item), errors }) } diff --git a/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates/Nargo.toml b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates/Nargo.toml new file mode 100644 index 00000000000..6355e1c06f4 --- /dev/null +++ b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "type_trait_method_call_multiple_candidates" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates/src/main.nr b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates/src/main.nr new file mode 100644 index 00000000000..3a477f4599f --- /dev/null +++ b/test_programs/compile_success_empty/type_trait_method_call_multiple_candidates/src/main.nr @@ -0,0 +1,32 @@ +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) + } +} + +fn main() { + let x: u64 = 0; + let mut small_int = MyU128::from(x); + assert(small_int.lo == x as Field); + let u32_3: u32 = 3; + assert(MyU128::from(u32_3).lo == 3); +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 1ea00d44049..e43be12b9aa 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -147,7 +147,7 @@ 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; 20] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 21] = [ // bug "associated_type_bounds", // bug @@ -184,6 +184,8 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 20] = [ "trait_override_implementation", // bug "trait_static_methods", + // bug + "type_trait_method_call_multiple_candidates", // 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_success_empty/type_trait_method_call_multiple_candidates/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/type_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/type_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": [] +}