Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 15 additions & 1 deletion compiler/rustc_symbol_mangling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,21 @@ fn compute_symbol_name<'tcx>(
mangled_name
}
}
SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate, false),
SymbolManglingVersion::V0 => {
let mangled = v0::mangle(tcx, instance, instantiating_crate, false);
// v0 mangling can produce symbols that are too deeply nested for
// `rustc_demangle` to handle. This happens with pathologically
// nested generic types: the v0 format uses backreferences to encode
// repeated path components, and following those backreferences
// during demangling recurses, exceeding `rustc_demangle`'s internal
// recursion limit. Fall back to hashed mangling in that case so we
// always emit a demanglable symbol.
if rustc_demangle::try_demangle(&mangled).is_ok() {
mangled
} else {
hashed::mangle(tcx, instance, instantiating_crate, || mangled)
Copy link
Copy Markdown
Contributor

@oli-obk oli-obk Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View changes since the review

This means we do thr fairly expensive demangling on every mangle and fall back to hashing even for bugs

Also, is there any previous discussion with maintainers about this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I investigated an alternative: tracking nesting depth inside V0SymbolMangler during print_type. Unfortunately this doesn't work because the depth experienced by rustc_demangle is not the same as the call-stack depth during mangling backref following in the demangler adds recursion that isn't tracked in the mangler. A symbol that only reaches depth ~300 in print_type can still exceed the 500-level limit in the demangler due to backrefs. So try_demangle is the only reliable way to detect this.

Regarding hiding bugs: the debug_assert! at the end of compute_symbol_name (line 331) still validates the final symbol (including hashed fallback) in debug builds, so actual v0 mangling bugs producing invalid symbols would still be caught there.

I'm not aware of prior maintainer discussion on this approach this is the initial fix.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is the final symbol validation relevant when you take any error here and turn it into a hashed symbol?

Copy link
Copy Markdown
Contributor Author

@GokhanKabar GokhanKabar Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that point doesn't make sense

}
}
SymbolManglingVersion::Hashed => {
hashed::mangle(tcx, instance, instantiating_crate, || {
v0::mangle(tcx, instance, instantiating_crate, false)
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/symbol-names/deeply-nested-generic-aliases.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//@ build-pass
//@ compile-flags: -C symbol-mangling-version=v0

struct D;

type O0<T> = Option<T>;
type O1<T> = O0<T>;
type O2<T> = O1<O1<T>>;
type O3<T> = O2<O2<T>>;
type O4<T> = O3<O3<T>>;
type O5<T> = O4<O4<T>>;
type O6<T> = O5<O5<T>>;
type O7<T> = O6<O6<T>>;
type O8<T> = O7<O7<T>>;
type Q510<T> = O8<O7<O6<O5<O4<O3<O2<T>>>>>>>;

fn f<T>() {}
fn describe<T>() {
f::<Q510<T>>()
}

fn main() {
describe::<D>();
}
Loading