Skip to content

[clang] Mangle deduction-guide specialization reflections - #316

Open
Cfretz244 wants to merge 4 commits into
bloomberg:p2996from
Cfretz244:reflect-deduction-guide-spec-mangling
Open

[clang] Mangle deduction-guide specialization reflections#316
Cfretz244 wants to merge 4 commits into
bloomberg:p2996from
Cfretz244:reflect-deduction-guide-spec-mangling

Conversation

@Cfretz244

Copy link
Copy Markdown

Fixes #312. Stacks on #301 (the reflection-NTTP mangling series; only the last commit is new).

A guide SPECIALIZATION reflection (Declaration kind, e.g. obtained via substitute on the guide template) routed through mangle()mangleFunctionEncodingmangleUnqualifiedName, whose CXXDeductionGuideName case is unreachable. Encode it like Template-kind guide reflections — "dg" + the deduced template + an ODR-hash discriminator — with the specialization's own function type folded in (AddFunctionDecl no-ops in specialization context, so the type is what keeps Box<int>'s guide distinct from Box<double>'s).

Includes a regression test asserting both manglability and that distinct guide specializations do NOT fold into one symbol at link time.

Validation (Apple Silicon, Release+assertions): repro crashes with the stack base's mangler and compiles clean with this change (verified both directions by reverting just this file); clang/test/Reflection at parity; regression test passes.

Cfretz244 and others added 4 commits June 9, 2026 22:30
…ions in NTTP mangling

A reflection of a template (ReflectionKind::Template) mangled as the
template's NAME only. Class/variable/alias templates cannot be overloaded,
so a name suffices for them -- but function templates overload, and two
same-named siblings (e.g. absl raw_hash_map's operator[] and its
SFINAE-false lifetimebound pack twin) produced byte-identical manglings.

Any template dispatcher taking such a reflection as an NTTP -- e.g.
  template <typename T, std::meta::info tmpl> void level2();
instantiated per member from a `template for` over members_of(^^T) --
therefore got ONE mangled name for its two specializations. The AST-level
specializations are correct and distinct; CodeGen then silently folds the
linkonce_odr definitions by mangled name, and a single body serves both
call sites. No diagnostic at any point.

Fix: for FunctionTemplateDecl reflections, append a '$'-bracketed ODR hash
of the template parameter list + the templated declaration pattern. The
ODR hash is cross-TU-stable by design (modules use it to compare decls
across TUs), so legitimate linkonce_odr merging is preserved. A structural
mangling of the pattern's function type was tried first and abandoned:
real-world dependent pattern types (lifetimebound SFINAE, noexcept(...)
referencing parameters) embed ParmVarDecl-referencing expressions that
mangleFunctionParam cannot encode outside a function-declaration context.

The regression test asserts at RUNTIME that the two dispatcher
instantiations are distinct functions with their own bodies;
static_asserts cannot catch the fold because the AST is always correct.

Fixes llvm#286.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ting unreachable

members_of over a namespace enumerates deduction guides like any other
member; lifting that list into define_static_array makes each element
reflection a template argument of the backing array specialization, so guide
reflections MUST mangle. mangleReflection's Template case encoded a
reflected template via mangleTemplateName -> mangleUnqualifiedName, which has
no case for CXXDeductionGuideName -- llvm_unreachable("Can't mangle a
deduction guide name!", ItaniumMangle.cpp:1774). The field shape is
TartanLlama/expected's namespace-scope `unexpected(E) -> unexpected<E>`: any
reflection consumer walking the tl namespace ICE'd at codegen.

Fix, local to mangleReflection (mangleUnqualifiedName's unreachable is a
sound invariant for normal symbol mangling and stays): encode "dg" + the
DEDUCED template's name + the same '$'-bracketed ODR-hash discriminator
already used for overloaded function templates. Every guide for one template
shares a single DeclarationName -- and Sema's IMPLICIT guides
(per-constructor + copy) enumerate alongside explicit ones, with the
per-constructor guide structurally identical to a same-signature explicit
guide -- so the hash also folds in isImplicit() and the deduction-candidate
kind. Deterministic and cross-TU-stable, preserving legitimate linkonce_odr
merging; distinct from a reflection of the deduced class template itself.

Test: deduction-guide-reflection-mangling.pass.cpp -- two explicit + two
implicit guides lifted via define_static_array, pinned as &probe<m> NTTPs,
asserted pairwise distinct at runtime (a fold is invisible to static_assert).

Stacks on the same-named-function-template NTTP discriminator change (it
extends the same mangleReflection hash block).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ons of a specialization in NTTP mangling

The NTTP discriminator for overloaded function-template reflections hashes
the template parameter list plus the declaration pattern via
ODRHash::AddFunctionDecl -- but AddFunctionDecl silently NO-OPS for any
declaration in "specialization context" (its decl-context walk returns on
finding a ClassTemplateSpecializationDecl). A member template of an
instantiated class template -- the common members_of shape -- therefore
contributed only its template HEAD to the hash, and same-named siblings with
IDENTICAL heads still mangled identically: tl::expected<T,E>'s four value()
member templates (const&/&/const&&/&&, one shared
`template <class U = T, enable_if_t<!is_void<U>::value>* = nullptr>` head)
got one mangled name, CodeGen silently folded the linkonce_odr dispatch
specializations, and value() never bound in a reflection-driven binding
generator -- caught by a differential test suite, not a diagnostic. The
original change's own field shape escaped only because absl's operator[]
siblings differ in their heads.

Fix: additionally hash what AddFunctionDecl skips there -- the pattern's
function type via ODRHash::AddQualType (return type, parameter types,
cv-quals; the ODR type hash handles the dependent pattern types a structural
MANGLING cannot, the original constraint) plus the ref-qualifier, which even
VisitFunctionProtoType omits (two siblings can differ in nothing else).

Test: fn-template-nttp-mangling-spec-context.pass.cpp -- the four-sibling
value() shape and a ref-qualifier-only pair, asserted pairwise distinct at
runtime.

Stacks on the deduction-guide mangling change (same hash block).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A guide SPECIALIZATION reflection (Declaration kind, e.g. obtained via
substitute on the guide template) routed through mangle() ->
mangleFunctionEncoding -> mangleUnqualifiedName, whose
CXXDeductionGuideName case is unreachable. Encode it like Template-kind
guide reflections: "dg" + the deduced template + an ODR-hash
discriminator, with the specialization's own function type folded in
(AddFunctionDecl no-ops in specialization context, so the type is what
keeps Box<int>'s guide distinct from Box<double>'s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant