Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
df347c8
fix: print `Self::item` for assumed trait items in `nargo expand`
asterite Jul 31, 2026
e020eb8
fix: don't print `impl Trait` parameter constraints in where clauses
asterite Jul 31, 2026
1760328
fix: print `impl Trait` parameters with their trait generic arguments
asterite Jul 31, 2026
6b3b938
fix: don't embed "(resolved type)" in associated type names
asterite Jul 31, 2026
0de2f0b
fix: print a global's initializer when its value can't be printed as …
asterite Jul 31, 2026
cb4f1d8
chore: drop stale entries from the `nargo expand` ignore lists
asterite Jul 31, 2026
40c9aa8
chore: un-ignore the `regression_9116` `nargo expand` execution test
asterite Jul 31, 2026
edf9a59
fix: qualify associated constant references in `nargo expand`
asterite Jul 31, 2026
14d9fb8
fix: print numeric type aliases with their numeric type annotation
asterite Jul 31, 2026
f09627e
test: numeric type alias used as a value expands to the wrong ident
asterite Jul 31, 2026
a27a54d
fix: print instantiated numeric generics as their constant value
asterite Jul 31, 2026
c1d76c6
fix: print associated constant declarations that involve the self type
asterite Jul 31, 2026
0dc6c5e
fix: don't print non-UTF-8 string values lossily in `nargo expand`
asterite Jul 31, 2026
aba8237
fix: print an associated constant's value when its trait isn't visible
asterite Jul 31, 2026
7ab33ba
chore: drop the stale bug note on `IGNORED_NARGO_EXPAND_EXECUTION_TESTS`
asterite Jul 31, 2026
fe28b5f
fix: suffix instantiated numeric generics with their numeric type
asterite Aug 3, 2026
17b6d51
fix: harden `nargo expand`'s unrepresentable-value detection
asterite Aug 3, 2026
9539412
Format
asterite Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/trait_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ impl Elaborator<'_> {
// This way associated types can be referred to even if their actual value (for associated constants)
// is not known yet. This is to allow associated constants to refer to associated constants
// in other trait impls.
let object = trait_impl.object_type.to_string();
let object = self.unresolved_type_name(&trait_impl.object_type);
let trait_name = trait_id.map(|id| self.interner.get_trait(id).name.to_string());
let associated_types_behind_type_vars = vecmap(&associated_types, |(name, _typ, kind)| {
let new_generic_id = self.interner.next_type_variable_id();
Expand Down
7 changes: 4 additions & 3 deletions compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ use itertools::Itertools;
use noirc_errors::Location;

use crate::{
Kind, NamedGeneric, ResolvedGeneric, Type, TypeBindings, TypeVariable,
IMPL_TRAIT_PARAMETER_NAME_PREFIX, Kind, NamedGeneric, ResolvedGeneric, Type, TypeBindings,
TypeVariable,
ast::{
FunctionDefinition, FunctionKind, GenericTypeArgs, Ident, NoirFunction, Path, TraitBound,
TraitItem, UnresolvedGeneric, UnresolvedTraitConstraint, UnresolvedType,
Expand Down Expand Up @@ -444,7 +445,7 @@ impl Elaborator<'_> {

let the_trait = self.get_trait(trait_id);
let trait_name = the_trait.name.to_string();
let object_name = object.to_string();
let object_name = self.unresolved_type_name(object);
let associated_type_bounds = the_trait.associated_type_bounds.clone();

for associated_type in &the_trait.associated_types.clone() {
Expand Down Expand Up @@ -520,7 +521,7 @@ impl Elaborator<'_> {
let new_generic = TypeVariable::unbound(new_generic_id, Kind::Normal);
generics.push(new_generic.clone());

let name = format!("impl {trait_path}");
let name = format!("{IMPL_TRAIT_PARAMETER_NAME_PREFIX}{trait_path}");
let generic_type = new_generic.into_named_generic(&Rc::new(name), None);
let trait_bound = TraitBound { trait_path, trait_generics };

Expand Down
20 changes: 20 additions & 0 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@
)
}

/// Stringifies a type as written, for embedding in an associated type's name
/// (`"<{object} as {trait}>::{name}"`). A macro-spliced type arrives already resolved and
/// its `Display` is the "(resolved type)" placeholder, so look through the resolution to
/// the actual type. These names resurface in `nargo expand` output, where the placeholder
/// would not parse.
///
/// Note that `Display` for a resolved [Type] is not source-faithful: data types print as
/// their bare name (no module path, so a same-named type in scope at the printing site can
/// shadow it) and unbound type variables print as `_` or their kind's default. These names
/// are display-only — nothing semantic keys off them — but the printed projection may not
/// re-resolve in every context.
pub(super) fn unresolved_type_name(&self, typ: &UnresolvedType) -> String {
match &typ.typ {
UnresolvedTypeData::Resolved(quoted_type_id) => {
self.interner.get_quoted_type(*quoted_type_id).to_string()
}
_ => typ.to_string(),
}
}

/// Resolves an [`UnresolvedType`] to a [Type] with [`Kind::Normal`] and marks it, and any generic types it contains, as _used_.
#[tracing::instrument(level = "trace", skip_all)]
pub(crate) fn use_type(
Expand Down Expand Up @@ -1766,7 +1786,7 @@
}
}

/// Resolves a turbofished `TypeName::<..>::method` path. Resolves to the single inherent method

Check warning on line 1789 in compiler/noirc_frontend/src/elaborator/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (turbofished)
/// matching the turbofish type if there is one, reports an error if the name is a method on the
/// type but none matches the turbofish, and otherwise defers to trait-method resolution.
#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -1831,7 +1851,7 @@
)
}

/// Resolves a non-turbofished `TypeName::method` (or `Self::method`) path to an inherent method.

Check warning on line 1854 in compiler/noirc_frontend/src/elaborator/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (turbofished)
/// `Self::method` anchors on the impl's own concrete self type; `TypeName::method` is reported as
/// ambiguous (Rust's E0034) when more than one non-overlapping inherent impl provides it.
#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -2042,7 +2062,7 @@
}
}

/// Builds the resolution for an inherent `TypeName::method` (or turbofished `TypeName::<..>::method`)

Check warning on line 2065 in compiler/noirc_frontend/src/elaborator/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (turbofished)
/// call, reporting a `Private` error if `func_id` is not visible from the current module.
fn resolve_direct_method(
&mut self,
Expand Down
80 changes: 75 additions & 5 deletions compiler/noirc_frontend/src/hir/printer/items/hir_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::borrow::Cow;
use itertools::Itertools;

use crate::{
NamedGeneric, Type, TypeBindings,
Kind, NamedGeneric, Type, TypeBinding, TypeBindings,
ast::{ItemVisibility, UnaryOp},
hir::def_map::ModuleDefId,
hir_def::{
Expand Down Expand Up @@ -857,7 +857,15 @@ impl ItemPrinter<'_, '_> {
} else {
match &constraint.typ {
Type::TypeVariable(type_var) if type_var.borrow().is_unbound() => {
// Don't show this as `AsTraitPath`
// The trait's own `Self` type variable can only stay unbound inside
// that trait's body, where the item is reachable as `Self::item`.
if self.trait_self_typevar == Some(type_var.id()) {
self.push_str("Self::");
let name = self.interner.definition_name(trait_item.definition);
self.push_str(name);
return;
}
// Otherwise don't show this as `AsTraitPath`
}
_ => {
self.push('<');
Expand Down Expand Up @@ -950,9 +958,71 @@ impl ItemPrinter<'_, '_> {
use_import,
);
}
DefinitionKind::Local(..)
| DefinitionKind::NumericGeneric(..)
| DefinitionKind::AssociatedConstant(..) => {
DefinitionKind::AssociatedConstant(trait_impl_id, ref name) => {
// The bare name only resolves inside the trait impl that defines the constant,
// so qualify it: `Self::N` within that impl, `<Type as Trait>::N` elsewhere.
let trait_impl = self.interner.get_trait_implementation(trait_impl_id);
let trait_impl = trait_impl.borrow();
if self.self_type.as_ref() == Some(&trait_impl.typ) {
self.push_str("Self::");
} else {
// The qualified form `<Type as Trait>::N` names the trait; when the trait
// isn't visible from this module (e.g. the reference was spliced in by a
// comptime `Expr::resolve` in another module's scope), print the constant's
// compile-time value instead.
let module_def_id = ModuleDefId::TraitId(trait_impl.trait_id);
let trait_ = self.interner.get_trait(trait_impl.trait_id);
let trait_is_visible = self
.module_def_id_is_visible_or_reexported(module_def_id, trait_.visibility);
if !trait_is_visible
&& let Some(named_type) = self
.interner
.get_associated_types_for_impl(trait_impl_id)
.iter()
.find(|named_type| named_type.name.as_str() == name)
&& let Type::Constant(constant) = named_type.typ.follow_bindings()
{
self.push_str(&constant.to_string());
if let Kind::Numeric(numeric_type) = named_type.typ.kind() {
self.push('_');
self.show_type(&numeric_type);
}
return;
}
self.push('<');
self.show_type(&trait_impl.typ);
self.push_str(" as ");
let trait_ = self.interner.get_trait(trait_impl.trait_id);
self.show_reference_to_module_def_id(
ModuleDefId::TraitId(trait_impl.trait_id),
trait_.visibility,
true,
);
let trait_generics = self.interner.get_trait_generics_for_impl(trait_impl_id);
let use_colons = false;
self.show_generic_types(&trait_generics.ordered, use_colons);
self.push_str(">::");
}
self.push_str(name);
}
DefinitionKind::NumericGeneric(ref type_var, ref numeric_type) => {
// When a numeric type alias's parameter is used as a value (`AliasN::<1>`),
// the definition's type variable is bound to the resolved value and the bare
// name doesn't resolve at the use site (or worse, resolves to something else
// with the same name). Print the value instead, suffixed with its numeric
// type so it can't be inferred as a different one.
if let TypeBinding::Bound(binding) = &*type_var.borrow()
&& let Type::Constant(constant) = binding.follow_bindings()
{
self.push_str(&constant.to_string());
self.push('_');
self.show_type(numeric_type);
return;
}
let name = self.interner.definition_name(ident.id);
self.push_str(name);
}
DefinitionKind::Local(..) => {
let name = self.interner.definition_name(ident.id);

// The compiler uses '$' for some internal identifiers.
Expand Down
Loading
Loading