From eda0878f9107588c888ded54d4f3102e77fa7bf1 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 15:40:58 -0300 Subject: [PATCH 01/14] fix: let `Self::N` inside a trait impl when N is an associated constant to work --- .../noirc_frontend/src/elaborator/patterns.rs | 54 ++++- compiler/noirc_frontend/src/hir_def/types.rs | 194 +++++++++--------- compiler/noirc_frontend/src/tests/traits.rs | 25 +++ .../Nargo.toml | 7 + .../src/main.nr | 19 ++ .../src_hash.txt | 1 + 6 files changed, 192 insertions(+), 108 deletions(-) create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src_hash.txt diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 181f787ce08..486e1b8a361 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -14,12 +14,13 @@ use crate::{ type_check::{Source, TypeCheckError}, }, hir_def::{ - expr::{HirExpression, HirIdent, HirMethodReference, ImplKind, TraitMethod}, + expr::{HirExpression, HirIdent, HirLiteral, HirMethodReference, ImplKind, TraitMethod}, stmt::HirPattern, }, node_interner::{ DefinitionId, DefinitionInfo, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind, }, + signed_field::SignedField, }; use super::{Elaborator, ResolverMeta, path_resolution::PathResolutionItem}; @@ -536,19 +537,50 @@ impl Elaborator<'_> { } 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 + // Check if this is `Self::method_name` or `Self::AssociatedConstant` when we are inside a trait impl and `Self` + // resolves to a primitive type. + // + // In the first 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. + // + // In the second case we solve the associated constant by looking up its value, later + // turning it into a literal. 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); + if let Some(trait_impl_id) = &self.current_trait_impl { + if variable.segments.len() == 2 + && variable.segments[0].ident.is_self_type_name() + && !matches!(self.self_type, Some(Type::DataType(..))) + { + let name = variable.segments[1].ident.as_str(); + let associated_types = + self.interner.get_associated_types_for_impl(*trait_impl_id); + let associated_type = + associated_types.iter().find(|typ| typ.name.as_str() == name); + if let Some(associated_type) = associated_type { + if let Type::Constant(field, Kind::Numeric(numeric_type)) = + &associated_type.typ + { + let numeric_type: Type = *numeric_type.clone(); + let hir_expr = HirExpression::Literal(HirLiteral::Integer( + SignedField::positive(*field), + )); + let id = self.interner.push_expr(hir_expr); + self.interner.push_expr_location(id, variable.location); + self.interner.push_expr_type(id, numeric_type.clone()); + return (id, numeric_type); + } + } + + 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, + ); + } } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ed0fa4a05de..ac81324c361 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -31,7 +31,7 @@ use super::{ mod arithmetic; -#[derive(Eq, Clone, Ord, PartialOrd)] +#[derive(Eq, Clone, Ord, PartialOrd, Debug)] pub enum Type { /// A primitive Field type FieldElement, @@ -3031,102 +3031,102 @@ impl From<&Type> for PrintableType { } } -impl std::fmt::Debug for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Type::FieldElement => { - write!(f, "Field") - } - Type::Array(len, typ) => { - write!(f, "[{typ:?}; {len:?}]") - } - Type::Slice(typ) => { - write!(f, "[{typ:?}]") - } - Type::Integer(sign, num_bits) => match sign { - Signedness::Signed => write!(f, "i{num_bits}"), - Signedness::Unsigned => write!(f, "u{num_bits}"), - }, - Type::TypeVariable(var) => { - let binding = &var.1; - if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { - match type_var_kind { - Kind::Any | Kind::Normal => write!(f, "{:?}", var), - Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), - Kind::Integer => write!(f, "Int{:?}", binding), - Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), - } - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::DataType(s, args) => { - let args = vecmap(args, |arg| format!("{:?}", arg)); - if args.is_empty() { - write!(f, "{}", s.borrow()) - } else { - write!(f, "{}<{}>", s.borrow(), args.join(", ")) - } - } - Type::Alias(alias, args) => { - let args = vecmap(args, |arg| format!("{:?}", arg)); - if args.is_empty() { - write!(f, "{}", alias.borrow()) - } else { - write!(f, "{}<{}>", alias.borrow(), args.join(", ")) - } - } - Type::TraitAsType(_id, name, generics) => write!(f, "impl {}{:?}", name, generics), - Type::Tuple(elements) => { - let elements = vecmap(elements, |arg| format!("{:?}", arg)); - write!(f, "({})", elements.join(", ")) - } - Type::Bool => write!(f, "bool"), - Type::String(len) => write!(f, "str<{len:?}>"), - Type::FmtString(len, elements) => { - write!(f, "fmtstr<{len:?}, {elements:?}>") - } - Type::Unit => write!(f, "()"), - Type::Error => write!(f, "error"), - Type::CheckedCast { to, .. } => write!(f, "{:?}", to), - Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match type_var.kind() { - Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { - write!(f, "{}{:?}", name, type_var) - } - Kind::Numeric(typ) => { - write!(f, "({} : {}){:?}", name, typ, type_var) - } - }, - Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), - Type::Forall(typevars, typ) => { - let typevars = vecmap(typevars, |var| format!("{:?}", var)); - write!(f, "forall {}. {:?}", typevars.join(" "), typ) - } - Type::Function(args, ret, env, unconstrained) => { - if *unconstrained { - write!(f, "unconstrained ")?; - } - - let closure_env_text = match **env { - Type::Unit => "".to_string(), - _ => format!(" with env {env:?}"), - }; - - let args = vecmap(args.iter(), |arg| format!("{:?}", arg)); - - write!(f, "fn({}) -> {ret:?}{closure_env_text}", args.join(", ")) - } - Type::Reference(element, false) => { - write!(f, "&{element:?}") - } - Type::Reference(element, true) => { - write!(f, "&mut {element:?}") - } - Type::Quoted(quoted) => write!(f, "{}", quoted), - Type::InfixExpr(lhs, op, rhs, _) => write!(f, "({lhs:?} {op} {rhs:?})"), - } - } -} +// impl std::fmt::Debug for Type { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// match self { +// Type::FieldElement => { +// write!(f, "Field") +// } +// Type::Array(len, typ) => { +// write!(f, "[{typ:?}; {len:?}]") +// } +// Type::Slice(typ) => { +// write!(f, "[{typ:?}]") +// } +// Type::Integer(sign, num_bits) => match sign { +// Signedness::Signed => write!(f, "i{num_bits}"), +// Signedness::Unsigned => write!(f, "u{num_bits}"), +// }, +// Type::TypeVariable(var) => { +// let binding = &var.1; +// if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { +// match type_var_kind { +// Kind::Any | Kind::Normal => write!(f, "{:?}", var), +// Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), +// Kind::Integer => write!(f, "Int{:?}", binding), +// Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), +// } +// } else { +// write!(f, "{}", binding.borrow()) +// } +// } +// Type::DataType(s, args) => { +// let args = vecmap(args, |arg| format!("{:?}", arg)); +// if args.is_empty() { +// write!(f, "{}", s.borrow()) +// } else { +// write!(f, "{}<{}>", s.borrow(), args.join(", ")) +// } +// } +// Type::Alias(alias, args) => { +// let args = vecmap(args, |arg| format!("{:?}", arg)); +// if args.is_empty() { +// write!(f, "{}", alias.borrow()) +// } else { +// write!(f, "{}<{}>", alias.borrow(), args.join(", ")) +// } +// } +// Type::TraitAsType(_id, name, generics) => write!(f, "impl {}{:?}", name, generics), +// Type::Tuple(elements) => { +// let elements = vecmap(elements, |arg| format!("{:?}", arg)); +// write!(f, "({})", elements.join(", ")) +// } +// Type::Bool => write!(f, "bool"), +// Type::String(len) => write!(f, "str<{len:?}>"), +// Type::FmtString(len, elements) => { +// write!(f, "fmtstr<{len:?}, {elements:?}>") +// } +// Type::Unit => write!(f, "()"), +// Type::Error => write!(f, "error"), +// Type::CheckedCast { to, .. } => write!(f, "{:?}", to), +// Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match type_var.kind() { +// Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { +// write!(f, "{}{:?}", name, type_var) +// } +// Kind::Numeric(typ) => { +// write!(f, "({} : {}){:?}", name, typ, type_var) +// } +// }, +// Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), +// Type::Forall(typevars, typ) => { +// let typevars = vecmap(typevars, |var| format!("{:?}", var)); +// write!(f, "forall {}. {:?}", typevars.join(" "), typ) +// } +// Type::Function(args, ret, env, unconstrained) => { +// if *unconstrained { +// write!(f, "unconstrained ")?; +// } + +// let closure_env_text = match **env { +// Type::Unit => "".to_string(), +// _ => format!(" with env {env:?}"), +// }; + +// let args = vecmap(args.iter(), |arg| format!("{:?}", arg)); + +// write!(f, "fn({}) -> {ret:?}{closure_env_text}", args.join(", ")) +// } +// Type::Reference(element, false) => { +// write!(f, "&{element:?}") +// } +// Type::Reference(element, true) => { +// write!(f, "&mut {element:?}") +// } +// Type::Quoted(quoted) => write!(f, "{}", quoted), +// Type::InfixExpr(lhs, op, rhs, _) => write!(f, "({lhs:?} {op} {rhs:?})"), +// } +// } +// } impl std::fmt::Debug for TypeVariableId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 5af5df8bcd3..87e4c3ab3a3 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1580,3 +1580,28 @@ fn errors_if_unconstrained_trait_definition_has_constrained_impl() { "#; check_errors!(src); } + +#[named] +#[test] +fn accesses_associated_type_inside_trait_impl_using_self() { + let src = r#" + pub trait Trait { + let N: u32; + + fn foo() -> u32; + } + + impl Trait for i32 { + let N: u32 = 10; + + fn foo() -> u32 { + Self::N + } + } + + fn main() { + let _ = i32::foo(); + } + "#; + assert_no_errors!(src); +} diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/Nargo.toml new file mode 100644 index 00000000000..49e9a89a659 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src/main.nr new file mode 100644 index 00000000000..96c260031f4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src/main.nr @@ -0,0 +1,19 @@ + + pub trait Trait { + let N: u32; + + fn foo() -> u32; + } + + impl Trait for i32 { + let N: u32 = 10; + + fn foo() -> u32 { + Self::N + } + } + + fn main() { + let _ = i32::foo(); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src_hash.txt new file mode 100644 index 00000000000..199c844d5c4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/src_hash.txt @@ -0,0 +1 @@ +16784421911760213987 \ No newline at end of file From 61db33f2ca07fb3c55e738a13a3f0e92c5958541 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 15:48:14 -0300 Subject: [PATCH 02/14] fix: handle associated constants in nargo expand --- .../nargo_cli/src/cli/expand_cmd/printer.rs | 28 +++++++++++++++---- .../execute__tests__expanded.snap | 21 ++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap diff --git a/tooling/nargo_cli/src/cli/expand_cmd/printer.rs b/tooling/nargo_cli/src/cli/expand_cmd/printer.rs index f7724d5af8e..9e63af16732 100644 --- a/tooling/nargo_cli/src/cli/expand_cmd/printer.rs +++ b/tooling/nargo_cli/src/cli/expand_cmd/printer.rs @@ -353,8 +353,17 @@ impl<'interner, 'def_map, 'string> ItemPrinter<'interner, 'def_map, 'string> { } self.write_indent(); - self.push_str("type "); - self.push_str(&associated_type.name); + + if let Kind::Numeric(numeric_type) = associated_type.kind() { + self.push_str("let "); + self.push_str(&associated_type.name); + self.push_str(": "); + self.show_type(&numeric_type); + } else { + self.push_str("type "); + self.push_str(&associated_type.name); + } + self.push_str(";"); printed_type_or_function = true; } @@ -414,9 +423,18 @@ impl<'interner, 'def_map, 'string> ItemPrinter<'interner, 'def_map, 'string> { } self.write_indent(); - self.push_str("type "); - self.push_str(&named_type.name.to_string()); - self.push_str(" = "); + + if let Type::Constant(_, Kind::Numeric(numeric_type)) = &named_type.typ { + self.push_str("let "); + self.push_str(&named_type.name.to_string()); + self.push_str(": "); + self.show_type(numeric_type); + self.push_str(" = "); + } else { + self.push_str("type "); + self.push_str(&named_type.name.to_string()); + self.push_str(" = "); + } self.show_type(&named_type.typ); self.push_str(";"); diff --git a/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap new file mode 100644 index 00000000000..583e349f799 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap @@ -0,0 +1,21 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Trait { + let N: u32; + + fn foo() -> u32; +} + +impl Trait for i32 { + let N: u32 = 10; + + fn foo() -> u32 { + 10 + } +} + +fn main() { + let _: u32 = Trait::foo(); +} From f408d743be876d176489e9074c549eb07dab9ff9 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 15:51:29 -0300 Subject: [PATCH 03/14] Ignore a nargo expand test --- tooling/nargo_cli/build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 7cb055cfa06..a71fb00af66 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -176,7 +176,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 14] = [ /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 17] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 18] = [ "noirc_frontend_tests_arithmetic_generics_checked_casts_do_not_prevent_canonicalization", "noirc_frontend_tests_check_trait_as_type_as_fn_parameter", "noirc_frontend_tests_check_trait_as_type_as_two_fn_parameters", @@ -192,6 +192,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 17] = [ "noirc_frontend_tests_traits_trait_alias_two_members", "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric", "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type", + "test_noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self", "noirc_frontend_tests_u32_globals_as_sizes_in_types", "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called", ]; From 1a73756060f6cf9b94bd9db827cfab0a654d5803 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 15:53:09 -0300 Subject: [PATCH 04/14] Unignore one nargo expand test --- tooling/nargo_cli/build.rs | 3 +-- .../execute__tests__expanded.snap | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function/execute__tests__expanded.snap diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index a71fb00af66..88e72f4d726 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -176,7 +176,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 14] = [ /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 18] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 17] = [ "noirc_frontend_tests_arithmetic_generics_checked_casts_do_not_prevent_canonicalization", "noirc_frontend_tests_check_trait_as_type_as_fn_parameter", "noirc_frontend_tests_check_trait_as_type_as_two_fn_parameters", @@ -185,7 +185,6 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 18] = [ "noirc_frontend_tests_traits_calls_trait_function_if_it_is_in_scope", "noirc_frontend_tests_traits_calls_trait_function_if_it_is_only_candidate_in_scope", "noirc_frontend_tests_traits_calls_trait_function_if_it_is_only_candidate_in_scope_in_nested_module_using_super", - "noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function", "noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function_inside_struct_impl", "noirc_frontend_tests_traits_trait_alias_polymorphic_inheritance", "noirc_frontend_tests_traits_trait_alias_single_member", diff --git a/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function/execute__tests__expanded.snap new file mode 100644 index 00000000000..40c1bee9dd4 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function/execute__tests__expanded.snap @@ -0,0 +1,22 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +trait Trait { + let N: u32; +} + +pub struct Foo {} + +impl Trait for Foo { + let N: u32 = 1; +} + +fn main() { + foo::(); +} + +fn foo() +where + T: Trait, +{} From b9de51690a9a682f3b6c04e7fe9c348e67dfbbfd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 15:54:15 -0300 Subject: [PATCH 05/14] Unignore one more test --- tooling/nargo_cli/build.rs | 3 +- .../execute__tests__expanded.snap | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function_inside_struct_impl/execute__tests__expanded.snap diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 88e72f4d726..d5239b4b955 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -176,7 +176,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 14] = [ /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 17] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 16] = [ "noirc_frontend_tests_arithmetic_generics_checked_casts_do_not_prevent_canonicalization", "noirc_frontend_tests_check_trait_as_type_as_fn_parameter", "noirc_frontend_tests_check_trait_as_type_as_two_fn_parameters", @@ -185,7 +185,6 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 17] = [ "noirc_frontend_tests_traits_calls_trait_function_if_it_is_in_scope", "noirc_frontend_tests_traits_calls_trait_function_if_it_is_only_candidate_in_scope", "noirc_frontend_tests_traits_calls_trait_function_if_it_is_only_candidate_in_scope_in_nested_module_using_super", - "noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function_inside_struct_impl", "noirc_frontend_tests_traits_trait_alias_polymorphic_inheritance", "noirc_frontend_tests_traits_trait_alias_single_member", "noirc_frontend_tests_traits_trait_alias_two_members", diff --git a/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function_inside_struct_impl/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function_inside_struct_impl/execute__tests__expanded.snap new file mode 100644 index 00000000000..f0517cbc079 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_passes_trait_with_associated_number_to_generic_function_inside_struct_impl/execute__tests__expanded.snap @@ -0,0 +1,29 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +trait Trait { + let N: u32; +} + +pub struct Foo {} + +impl Trait for Foo { + let N: u32 = 1; +} + +pub struct Bar {} + +impl Bar { + fn bar(self) + where + U: Trait, + { + let _: Self = self; + } +} + +fn main() { + let bar: Bar = Bar:: {}; + bar.bar::(); +} From 54d9c60cc77feeac539bce8c0490b39a115f65fa Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 15:55:07 -0300 Subject: [PATCH 06/14] And one more test --- tooling/nargo_cli/build.rs | 3 +-- .../execute__tests__expanded.snap | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/execute__tests__expanded.snap diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index d5239b4b955..7d742cbb672 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -176,7 +176,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 14] = [ /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 16] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 15] = [ "noirc_frontend_tests_arithmetic_generics_checked_casts_do_not_prevent_canonicalization", "noirc_frontend_tests_check_trait_as_type_as_fn_parameter", "noirc_frontend_tests_check_trait_as_type_as_two_fn_parameters", @@ -188,7 +188,6 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 16] = [ "noirc_frontend_tests_traits_trait_alias_polymorphic_inheritance", "noirc_frontend_tests_traits_trait_alias_single_member", "noirc_frontend_tests_traits_trait_alias_two_members", - "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric", "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type", "test_noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self", "noirc_frontend_tests_u32_globals_as_sizes_in_types", diff --git a/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/execute__tests__expanded.snap new file mode 100644 index 00000000000..a7389ae5039 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_numeric/execute__tests__expanded.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +trait Bar { + let N: Field; +} + +impl Bar for Field { + let N: Self = 42; +} + +trait Foo { + fn foo(b: B) + where + B: Bar; +} + +impl Foo for Field { + fn foo(_: B) + where + B: Bar, + {} +} + +fn main() {} From 58136cc93546a4933c770af7872717bdb173aa50 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 16:09:43 -0300 Subject: [PATCH 07/14] Refactor --- .../noirc_frontend/src/elaborator/patterns.rs | 102 ++++++++++-------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 486e1b8a361..31fe686ceaf 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -536,52 +536,11 @@ impl Elaborator<'_> { }) } - pub(super) fn elaborate_variable(&mut self, mut variable: Path) -> (ExprId, Type) { - // Check if this is `Self::method_name` or `Self::AssociatedConstant` when we are inside a trait impl and `Self` - // resolves to a primitive type. - // - // In the first 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. - // - // In the second case we solve the associated constant by looking up its value, later - // turning it into a literal. - if let Some(self_type) = &self.self_type { - if let Some(trait_impl_id) = &self.current_trait_impl { - if variable.segments.len() == 2 - && variable.segments[0].ident.is_self_type_name() - && !matches!(self.self_type, Some(Type::DataType(..))) - { - let name = variable.segments[1].ident.as_str(); - let associated_types = - self.interner.get_associated_types_for_impl(*trait_impl_id); - let associated_type = - associated_types.iter().find(|typ| typ.name.as_str() == name); - if let Some(associated_type) = associated_type { - if let Type::Constant(field, Kind::Numeric(numeric_type)) = - &associated_type.typ - { - let numeric_type: Type = *numeric_type.clone(); - let hir_expr = HirExpression::Literal(HirLiteral::Integer( - SignedField::positive(*field), - )); - let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, variable.location); - self.interner.push_expr_type(id, numeric_type.clone()); - return (id, numeric_type); - } - } - - 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, - ); - } - } + pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) { + if let Some((expr_id, typ)) = + self.elaborate_variable_as_self_method_or_associated_constant(&variable) + { + return (expr_id, typ); } let unresolved_turbofish = variable.segments.last().unwrap().generics.clone(); @@ -645,6 +604,57 @@ impl Elaborator<'_> { } } + /// Checks whether `variable` is `Self::method_name` or `Self::AssociatedConstant` when we are inside a trait impl and `Self` + /// resolves to a primitive type. + /// + /// In the first 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. + /// + /// In the second case we solve the associated constant by looking up its value, later + /// turning it into a literal. + fn elaborate_variable_as_self_method_or_associated_constant( + &mut self, + variable: &Path, + ) -> Option<(ExprId, Type)> { + let Some(self_type) = &self.self_type else { + return None; + }; + + let Some(trait_impl_id) = &self.current_trait_impl else { + return None; + }; + + if !(variable.segments.len() == 2 && variable.segments[0].ident.is_self_type_name()) { + return None; + } + + // Check the `Self::AssociatedConstant` case + let name = variable.segments[1].ident.as_str(); + let associated_types = self.interner.get_associated_types_for_impl(*trait_impl_id); + let associated_type = associated_types.iter().find(|typ| typ.name.as_str() == name); + if let Some(associated_type) = associated_type { + if let Type::Constant(field, Kind::Numeric(numeric_type)) = &associated_type.typ { + let numeric_type: Type = *numeric_type.clone(); + let hir_expr = + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(*field))); + let id = self.interner.push_expr(hir_expr); + self.interner.push_expr_location(id, variable.location); + self.interner.push_expr_type(id, numeric_type.clone()); + return Some((id, numeric_type)); + } + } + + // Check the `Self::method_name` case when `Self` is a primitive type + if !matches!(self.self_type, Some(Type::DataType(..))) { + return None; + } + + let ident = variable.segments[0].ident.clone(); + let typ_location = variable.segments[0].location; + Some(self.elaborate_type_path_impl(self_type.clone(), ident, None, typ_location)) + } + /// Solve any generics that are part of the path before the function, for example: /// /// ```noir From 3b08e2cc01fbf4140fa377af161792c6f138cd97 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 16:20:04 -0300 Subject: [PATCH 08/14] Make it work for the trait case --- .../noirc_frontend/src/elaborator/patterns.rs | 48 ++++++++++++++----- compiler/noirc_frontend/src/tests/traits.rs | 23 +++++++++ .../Nargo.toml | 7 +++ .../src/main.nr | 17 +++++++ .../src_hash.txt | 1 + tooling/nargo_cli/build.rs | 3 +- 6 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src_hash.txt diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 31fe686ceaf..357192b3ebb 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -617,6 +617,26 @@ impl Elaborator<'_> { &mut self, variable: &Path, ) -> Option<(ExprId, Type)> { + if !(variable.segments.len() == 2 && variable.segments[0].ident.is_self_type_name()) { + return None; + } + + let name = variable.segments[1].ident.as_str(); + + // Check the `Self::AssociatedConstant` case when inside a trait + if let Some(trait_id) = &self.current_trait { + let trait_ = self.interner.get_trait(*trait_id); + if let Some(associated_type) = trait_.get_associated_type(name) { + if let Kind::Numeric(numeric_type) = associated_type.kind() { + // We can produce any value here because this trait method is never going to + // produce code (only trait impl methods do) + let numeric_type: Type = *numeric_type.clone(); + let value = SignedField::zero(); + return Some(self.constant_integer(numeric_type, value, variable.location)); + } + } + } + let Some(self_type) = &self.self_type else { return None; }; @@ -625,23 +645,14 @@ impl Elaborator<'_> { return None; }; - if !(variable.segments.len() == 2 && variable.segments[0].ident.is_self_type_name()) { - return None; - } - - // Check the `Self::AssociatedConstant` case - let name = variable.segments[1].ident.as_str(); + // Check the `Self::AssociatedConstant` case when inside a trait impl let associated_types = self.interner.get_associated_types_for_impl(*trait_impl_id); let associated_type = associated_types.iter().find(|typ| typ.name.as_str() == name); if let Some(associated_type) = associated_type { if let Type::Constant(field, Kind::Numeric(numeric_type)) = &associated_type.typ { let numeric_type: Type = *numeric_type.clone(); - let hir_expr = - HirExpression::Literal(HirLiteral::Integer(SignedField::positive(*field))); - let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, variable.location); - self.interner.push_expr_type(id, numeric_type.clone()); - return Some((id, numeric_type)); + let value = SignedField::positive(*field); + return Some(self.constant_integer(numeric_type, value, variable.location)); } } @@ -655,6 +666,19 @@ impl Elaborator<'_> { Some(self.elaborate_type_path_impl(self_type.clone(), ident, None, typ_location)) } + fn constant_integer( + &mut self, + numeric_type: Type, + value: SignedField, + location: Location, + ) -> (ExprId, Type) { + let hir_expr = HirExpression::Literal(HirLiteral::Integer(value)); + let id = self.interner.push_expr(hir_expr); + self.interner.push_expr_location(id, location); + self.interner.push_expr_type(id, numeric_type.clone()); + (id, numeric_type) + } + /// Solve any generics that are part of the path before the function, for example: /// /// ```noir diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 87e4c3ab3a3..c17b2e62328 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1605,3 +1605,26 @@ fn accesses_associated_type_inside_trait_impl_using_self() { "#; assert_no_errors!(src); } + +#[named] +#[test] +fn accesses_associated_type_inside_trait_using_self() { + let src = r#" + pub trait Trait { + let N: u32; + + fn foo() -> u32 { + Self::N + } + } + + impl Trait for i32 { + let N: u32 = 10; + } + + fn main() { + let _ = i32::foo(); + } + "#; + assert_no_errors!(src); +} diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/Nargo.toml new file mode 100644 index 00000000000..2249089f52d --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src/main.nr new file mode 100644 index 00000000000..88a396eed07 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src/main.nr @@ -0,0 +1,17 @@ + + pub trait Trait { + let N: u32; + + fn foo() -> u32 { + Self::N + } + } + + impl Trait for i32 { + let N: u32 = 10; + } + + fn main() { + let _ = i32::foo(); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src_hash.txt new file mode 100644 index 00000000000..700a2054a63 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self/src_hash.txt @@ -0,0 +1 @@ +13003701006269760980 \ No newline at end of file diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 7d742cbb672..33bebf1d7c5 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -176,7 +176,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_EMPTY_TESTS: [&str; 14] = [ /// These tests are ignored because of existing bugs in `nargo expand`. /// As the bugs are fixed these tests should be removed from this list. -const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 15] = [ +const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 16] = [ "noirc_frontend_tests_arithmetic_generics_checked_casts_do_not_prevent_canonicalization", "noirc_frontend_tests_check_trait_as_type_as_fn_parameter", "noirc_frontend_tests_check_trait_as_type_as_two_fn_parameters", @@ -190,6 +190,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 15] = [ "noirc_frontend_tests_traits_trait_alias_two_members", "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type", "test_noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self", + "noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self", "noirc_frontend_tests_u32_globals_as_sizes_in_types", "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called", ]; From 5add327e0370a37c6ffde4731568fde43ca58b28 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 16:51:03 -0300 Subject: [PATCH 09/14] Undo Debug change --- compiler/noirc_frontend/src/hir_def/types.rs | 194 +++++++++---------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ac81324c361..ed0fa4a05de 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -31,7 +31,7 @@ use super::{ mod arithmetic; -#[derive(Eq, Clone, Ord, PartialOrd, Debug)] +#[derive(Eq, Clone, Ord, PartialOrd)] pub enum Type { /// A primitive Field type FieldElement, @@ -3031,102 +3031,102 @@ impl From<&Type> for PrintableType { } } -// impl std::fmt::Debug for Type { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// match self { -// Type::FieldElement => { -// write!(f, "Field") -// } -// Type::Array(len, typ) => { -// write!(f, "[{typ:?}; {len:?}]") -// } -// Type::Slice(typ) => { -// write!(f, "[{typ:?}]") -// } -// Type::Integer(sign, num_bits) => match sign { -// Signedness::Signed => write!(f, "i{num_bits}"), -// Signedness::Unsigned => write!(f, "u{num_bits}"), -// }, -// Type::TypeVariable(var) => { -// let binding = &var.1; -// if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { -// match type_var_kind { -// Kind::Any | Kind::Normal => write!(f, "{:?}", var), -// Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), -// Kind::Integer => write!(f, "Int{:?}", binding), -// Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), -// } -// } else { -// write!(f, "{}", binding.borrow()) -// } -// } -// Type::DataType(s, args) => { -// let args = vecmap(args, |arg| format!("{:?}", arg)); -// if args.is_empty() { -// write!(f, "{}", s.borrow()) -// } else { -// write!(f, "{}<{}>", s.borrow(), args.join(", ")) -// } -// } -// Type::Alias(alias, args) => { -// let args = vecmap(args, |arg| format!("{:?}", arg)); -// if args.is_empty() { -// write!(f, "{}", alias.borrow()) -// } else { -// write!(f, "{}<{}>", alias.borrow(), args.join(", ")) -// } -// } -// Type::TraitAsType(_id, name, generics) => write!(f, "impl {}{:?}", name, generics), -// Type::Tuple(elements) => { -// let elements = vecmap(elements, |arg| format!("{:?}", arg)); -// write!(f, "({})", elements.join(", ")) -// } -// Type::Bool => write!(f, "bool"), -// Type::String(len) => write!(f, "str<{len:?}>"), -// Type::FmtString(len, elements) => { -// write!(f, "fmtstr<{len:?}, {elements:?}>") -// } -// Type::Unit => write!(f, "()"), -// Type::Error => write!(f, "error"), -// Type::CheckedCast { to, .. } => write!(f, "{:?}", to), -// Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match type_var.kind() { -// Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { -// write!(f, "{}{:?}", name, type_var) -// } -// Kind::Numeric(typ) => { -// write!(f, "({} : {}){:?}", name, typ, type_var) -// } -// }, -// Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), -// Type::Forall(typevars, typ) => { -// let typevars = vecmap(typevars, |var| format!("{:?}", var)); -// write!(f, "forall {}. {:?}", typevars.join(" "), typ) -// } -// Type::Function(args, ret, env, unconstrained) => { -// if *unconstrained { -// write!(f, "unconstrained ")?; -// } - -// let closure_env_text = match **env { -// Type::Unit => "".to_string(), -// _ => format!(" with env {env:?}"), -// }; - -// let args = vecmap(args.iter(), |arg| format!("{:?}", arg)); - -// write!(f, "fn({}) -> {ret:?}{closure_env_text}", args.join(", ")) -// } -// Type::Reference(element, false) => { -// write!(f, "&{element:?}") -// } -// Type::Reference(element, true) => { -// write!(f, "&mut {element:?}") -// } -// Type::Quoted(quoted) => write!(f, "{}", quoted), -// Type::InfixExpr(lhs, op, rhs, _) => write!(f, "({lhs:?} {op} {rhs:?})"), -// } -// } -// } +impl std::fmt::Debug for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Type::FieldElement => { + write!(f, "Field") + } + Type::Array(len, typ) => { + write!(f, "[{typ:?}; {len:?}]") + } + Type::Slice(typ) => { + write!(f, "[{typ:?}]") + } + Type::Integer(sign, num_bits) => match sign { + Signedness::Signed => write!(f, "i{num_bits}"), + Signedness::Unsigned => write!(f, "u{num_bits}"), + }, + Type::TypeVariable(var) => { + let binding = &var.1; + if let TypeBinding::Unbound(_, type_var_kind) = &*binding.borrow() { + match type_var_kind { + Kind::Any | Kind::Normal => write!(f, "{:?}", var), + Kind::IntegerOrField => write!(f, "IntOrField{:?}", binding), + Kind::Integer => write!(f, "Int{:?}", binding), + Kind::Numeric(typ) => write!(f, "Numeric({:?}: {:?})", binding, typ), + } + } else { + write!(f, "{}", binding.borrow()) + } + } + Type::DataType(s, args) => { + let args = vecmap(args, |arg| format!("{:?}", arg)); + if args.is_empty() { + write!(f, "{}", s.borrow()) + } else { + write!(f, "{}<{}>", s.borrow(), args.join(", ")) + } + } + Type::Alias(alias, args) => { + let args = vecmap(args, |arg| format!("{:?}", arg)); + if args.is_empty() { + write!(f, "{}", alias.borrow()) + } else { + write!(f, "{}<{}>", alias.borrow(), args.join(", ")) + } + } + Type::TraitAsType(_id, name, generics) => write!(f, "impl {}{:?}", name, generics), + Type::Tuple(elements) => { + let elements = vecmap(elements, |arg| format!("{:?}", arg)); + write!(f, "({})", elements.join(", ")) + } + Type::Bool => write!(f, "bool"), + Type::String(len) => write!(f, "str<{len:?}>"), + Type::FmtString(len, elements) => { + write!(f, "fmtstr<{len:?}, {elements:?}>") + } + Type::Unit => write!(f, "()"), + Type::Error => write!(f, "error"), + Type::CheckedCast { to, .. } => write!(f, "{:?}", to), + Type::NamedGeneric(NamedGeneric { type_var, name, .. }) => match type_var.kind() { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { + write!(f, "{}{:?}", name, type_var) + } + Kind::Numeric(typ) => { + write!(f, "({} : {}){:?}", name, typ, type_var) + } + }, + Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), + Type::Forall(typevars, typ) => { + let typevars = vecmap(typevars, |var| format!("{:?}", var)); + write!(f, "forall {}. {:?}", typevars.join(" "), typ) + } + Type::Function(args, ret, env, unconstrained) => { + if *unconstrained { + write!(f, "unconstrained ")?; + } + + let closure_env_text = match **env { + Type::Unit => "".to_string(), + _ => format!(" with env {env:?}"), + }; + + let args = vecmap(args.iter(), |arg| format!("{:?}", arg)); + + write!(f, "fn({}) -> {ret:?}{closure_env_text}", args.join(", ")) + } + Type::Reference(element, false) => { + write!(f, "&{element:?}") + } + Type::Reference(element, true) => { + write!(f, "&mut {element:?}") + } + Type::Quoted(quoted) => write!(f, "{}", quoted), + Type::InfixExpr(lhs, op, rhs, _) => write!(f, "({lhs:?} {op} {rhs:?})"), + } + } +} impl std::fmt::Debug for TypeVariableId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { From f2f0cd793c382e38552ff381dbe51ed67eb8d812 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 16:54:56 -0300 Subject: [PATCH 10/14] Fix condition --- compiler/noirc_frontend/src/elaborator/patterns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 357192b3ebb..8d5b4386186 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -657,7 +657,7 @@ impl Elaborator<'_> { } // Check the `Self::method_name` case when `Self` is a primitive type - if !matches!(self.self_type, Some(Type::DataType(..))) { + if matches!(self.self_type, Some(Type::DataType(..))) { return None; } From 16f0fe34dde33bb84e2d52cd07a1bb0e73f6590d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 7 May 2025 17:23:43 -0300 Subject: [PATCH 11/14] Fix wrong index --- compiler/noirc_frontend/src/elaborator/patterns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 8d5b4386186..38032be1586 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -661,7 +661,7 @@ impl Elaborator<'_> { return None; } - let ident = variable.segments[0].ident.clone(); + let ident = variable.segments[1].ident.clone(); let typ_location = variable.segments[0].location; Some(self.elaborate_type_path_impl(self_type.clone(), ident, None, typ_location)) } From 3993158b524ff1baf5fb6b584d31c9613dd8b970 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 8 May 2025 07:14:58 -0300 Subject: [PATCH 12/14] Correct way to ignore test --- tooling/nargo_cli/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 33bebf1d7c5..83c2d0ada04 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -189,7 +189,7 @@ const IGNORED_NARGO_EXPAND_COMPILE_SUCCESS_NO_BUG_TESTS: [&str; 16] = [ "noirc_frontend_tests_traits_trait_alias_single_member", "noirc_frontend_tests_traits_trait_alias_two_members", "noirc_frontend_tests_traits_trait_impl_with_where_clause_with_trait_with_associated_type", - "test_noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self", + "noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self", "noirc_frontend_tests_traits_accesses_associated_type_inside_trait_using_self", "noirc_frontend_tests_u32_globals_as_sizes_in_types", "noirc_frontend_tests_unused_items_considers_struct_as_constructed_if_trait_method_is_called", From eb6fe840d55d1423a2b6fd8f32adb1cb1023b54a Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 8 May 2025 07:15:29 -0300 Subject: [PATCH 13/14] Remove extra snapshot --- .../execute__tests__expanded.snap | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap diff --git a/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap deleted file mode 100644 index 583e349f799..00000000000 --- a/tooling/nargo_cli/tests/snapshots/expand/compile_success_no_bug/noirc_frontend_tests_traits_accesses_associated_type_inside_trait_impl_using_self/execute__tests__expanded.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: tooling/nargo_cli/tests/execute.rs -expression: expanded_code ---- -pub trait Trait { - let N: u32; - - fn foo() -> u32; -} - -impl Trait for i32 { - let N: u32 = 10; - - fn foo() -> u32 { - 10 - } -} - -fn main() { - let _: u32 = Trait::foo(); -} From 903a1498dafa600321d50423f60f86f6b7b10b0b Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 13 May 2025 13:42:18 -0300 Subject: [PATCH 14/14] Apply suggestion from code review --- compiler/noirc_frontend/src/elaborator/patterns.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 944fe296859..e9e7234fb2d 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -16,6 +16,7 @@ use crate::{ hir_def::{ expr::{HirExpression, HirIdent, HirLiteral, HirMethodReference, ImplKind, TraitMethod}, stmt::HirPattern, + traits::NamedType, }, node_interner::{ DefinitionId, DefinitionInfo, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind, @@ -668,12 +669,12 @@ impl Elaborator<'_> { // Check the `Self::AssociatedConstant` case when inside a trait impl let associated_types = self.interner.get_associated_types_for_impl(*trait_impl_id); let associated_type = associated_types.iter().find(|typ| typ.name.as_str() == name); - if let Some(associated_type) = associated_type { - if let Type::Constant(field, Kind::Numeric(numeric_type)) = &associated_type.typ { - let numeric_type: Type = *numeric_type.clone(); - let value = SignedField::positive(*field); - return Some(self.constant_integer(numeric_type, value, variable.location)); - } + if let Some(NamedType { typ: Type::Constant(field, Kind::Numeric(numeric_type)), .. }) = + associated_type + { + let numeric_type: Type = *numeric_type.clone(); + let value = SignedField::positive(*field); + return Some(self.constant_integer(numeric_type, value, variable.location)); } // Check the `Self::method_name` case when `Self` is a primitive type