diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index f354b74c800..04740dd4074 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -5,8 +5,8 @@ use rustc_hash::FxHashSet as HashSet; use crate::{ DataType, Kind, Shared, Type, TypeAlias, TypeBindings, ast::{ - ERROR_IDENT, Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, - UnresolvedType, + ERROR_IDENT, Expression, ExpressionKind, GenericTypeArgs, Ident, ItemVisibility, Path, + Pattern, TypePath, UnresolvedType, }, hir::{ def_collector::dc_crate::CompilationError, @@ -535,7 +535,23 @@ impl Elaborator<'_> { }) } - pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) { + pub(super) fn elaborate_variable(&mut self, mut variable: Path) -> (ExprId, Type) { + // Check if this is `Self::method_name` when we are inside a trait impl and `Self` + // resolves to a primitive type. In that case we elaborate this as if it were a TypePath + // (for example, if `Self` is `u32` then we consider this the same as `u32::method_name`). + // A regular path lookup won't work here for the same reason `TypePath` exists. + if let Some(self_type) = &self.self_type { + if self.current_trait_impl.is_some() + && variable.segments.len() == 2 + && variable.segments[0].ident.is_self_type_name() + && !matches!(self.self_type, Some(Type::DataType(..))) + { + let ident = variable.segments.remove(1).ident; + let typ_location = variable.segments[0].location; + return self.elaborate_type_path_impl(self_type.clone(), ident, None, typ_location); + } + } + let unresolved_turbofish = variable.segments.last().unwrap().generics.clone(); let location = variable.location; @@ -886,21 +902,32 @@ impl Elaborator<'_> { } pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) { - let location = path.item.location(); - let object_location = path.typ.location; + let typ_location = path.typ.location; + let turbofish = path.turbofish; let typ = self.resolve_type(path.typ); + self.elaborate_type_path_impl(typ, path.item, turbofish, typ_location) + } + + fn elaborate_type_path_impl( + &mut self, + typ: Type, + ident: Ident, + turbofish: Option, + typ_location: Location, + ) -> (ExprId, Type) { + let ident_location = ident.location(); let check_self_param = false; - self.interner.push_type_ref_location(&typ, object_location); + self.interner.push_type_ref_location(&typ, typ_location); let Some(method) = self.lookup_method( &typ, - path.item.as_str(), - location, - object_location, + ident.as_str(), + ident_location, + typ_location, check_self_param, ) else { - let error = Expression::new(ExpressionKind::Error, location); + let error = Expression::new(ExpressionKind::Error, ident_location); return self.elaborate_expression(error); }; @@ -909,7 +936,7 @@ impl Elaborator<'_> { .expect("Expected trait function to be a DefinitionKind::Function"); let generics = - path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, location).0); + turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, ident_location).0); let id = self.interner.function_definition_id(func_id); @@ -917,15 +944,15 @@ impl Elaborator<'_> { HirMethodReference::FuncId(_) => ImplKind::NotATraitMethod, HirMethodReference::TraitMethodId(method_id, generics, _) => { let mut constraint = - self.interner.get_trait(method_id.trait_id).as_constraint(location); + self.interner.get_trait(method_id.trait_id).as_constraint(ident_location); constraint.trait_bound.trait_generics = generics; ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed: false }) } }; - let ident = HirIdent { location, id, impl_kind }; + let ident = HirIdent { location: ident_location, id, impl_kind }; let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), generics.clone())); - self.interner.push_expr_location(id, location); + self.interner.push_expr_location(id, ident_location); let typ = self.type_check_variable(ident, id, generics); self.interner.push_expr_type(id, typ.clone()); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 6d2c3fd2101..e7c008b1bba 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -621,6 +621,12 @@ impl Elaborator<'_> { // Returns the trait method, trait constraint, and whether the impl is assumed to exist by a where clause or not // E.g. `t.method()` with `where T: Foo` in scope will return `(Foo::method, T, vec![Bar])` fn resolve_trait_static_method_by_self(&mut self, path: &Path) -> Option { + // If we are inside a trait impl, `Self` is known to be a concrete type so we don't have + // to solve the path via trait method lookup. + if self.current_trait_impl.is_some() { + return None; + } + let trait_id = if let Some(current_trait) = self.current_trait { current_trait } else { diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 7dbd4e012af..247d3050bf1 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1399,3 +1399,99 @@ fn passes_trait_with_associated_number_to_generic_function_inside_struct_impl() "; assert_no_errors!(src); } + +#[named] +#[test] +fn returns_self_in_trait_method_1() { + let src = " + pub trait MagicNumber { + fn from_magic_value() -> Self; + fn from_value() -> Self; + } + + pub struct Foo {} + + impl MagicNumber for Foo { + fn from_magic_value() -> Foo { + Self::from_value() + } + fn from_value() -> Self { + Self {} + } + } + + pub struct Bar {} + + impl MagicNumber for Bar { + fn from_magic_value() -> Bar { + Self::from_value() + } + fn from_value() -> Self { + Self {} + } + } + + fn main() {} + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn returns_self_in_trait_method_2() { + let src = " + pub trait MagicNumber { + fn from_magic_value() -> Self { + Self::from_value() + } + fn from_value() -> Self; + } + + pub struct Foo {} + + impl MagicNumber for Foo { + fn from_value() -> Self { + Self {} + } + } + + pub struct Bar {} + + impl MagicNumber for Bar { + fn from_value() -> Self { + Self {} + } + } + + fn main() {} + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn returns_self_in_trait_method_3() { + let src = " + pub trait MagicNumber { + fn from_magic_value() -> Self { + Self::from_value() + } + fn from_value() -> Self; + } + + impl MagicNumber for i32 { + fn from_value() -> Self { + 0 + } + } + + impl MagicNumber for i64 { + fn from_value() -> Self { + 0 + } + } + + fn main() {} + "; + assert_no_errors!(src); +} diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/Nargo.toml new file mode 100644 index 00000000000..0ad19fddbaf --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_returns_self_in_trait_method_1" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/src/main.nr new file mode 100644 index 00000000000..326574e68d7 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/src/main.nr @@ -0,0 +1,30 @@ + + pub trait MagicNumber { + fn from_magic_value() -> Self; + fn from_value() -> Self; + } + + pub struct Foo {} + + impl MagicNumber for Foo { + fn from_magic_value() -> Foo { + Self::from_value() + } + fn from_value() -> Self { + Self {} + } + } + + pub struct Bar {} + + impl MagicNumber for Bar { + fn from_magic_value() -> Bar { + Self::from_value() + } + fn from_value() -> Self { + Self {} + } + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/src_hash.txt new file mode 100644 index 00000000000..6bf949f4228 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_1/src_hash.txt @@ -0,0 +1 @@ +17232156059324941241 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/Nargo.toml new file mode 100644 index 00000000000..94e078b1f36 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_returns_self_in_trait_method_2" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/src/main.nr new file mode 100644 index 00000000000..d95fee88270 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/src/main.nr @@ -0,0 +1,26 @@ + + pub trait MagicNumber { + fn from_magic_value() -> Self { + Self::from_value() + } + fn from_value() -> Self; + } + + pub struct Foo {} + + impl MagicNumber for Foo { + fn from_value() -> Self { + Self {} + } + } + + pub struct Bar {} + + impl MagicNumber for Bar { + fn from_value() -> Self { + Self {} + } + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/src_hash.txt new file mode 100644 index 00000000000..ec3892fd5fe --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_2/src_hash.txt @@ -0,0 +1 @@ +6196417284918689669 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/Nargo.toml new file mode 100644 index 00000000000..a6dbde6eace --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_returns_self_in_trait_method_3" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/src/main.nr new file mode 100644 index 00000000000..6d737718e74 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/src/main.nr @@ -0,0 +1,22 @@ + + pub trait MagicNumber { + fn from_magic_value() -> Self { + Self::from_value() + } + fn from_value() -> Self; + } + + impl MagicNumber for i32 { + fn from_value() -> Self { + 0 + } + } + + impl MagicNumber for i64 { + fn from_value() -> Self { + 0 + } + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/src_hash.txt new file mode 100644 index 00000000000..9e032acae0d --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_returns_self_in_trait_method_3/src_hash.txt @@ -0,0 +1 @@ +960084401282402797 \ No newline at end of file