Skip to content

[clang][reflection] Handle entity-proxy reflections in member metafunctions and NTTP mangling - #291

Open
Cfretz244 wants to merge 1 commit into
bloomberg:p2996from
Cfretz244:reflect-entity-proxy-hardening
Open

[clang][reflection] Handle entity-proxy reflections in member metafunctions and NTTP mangling#291
Cfretz244 wants to merge 1 commit into
bloomberg:p2996from
Cfretz244:reflect-entity-proxy-hardening

Conversation

@Cfretz244

Copy link
Copy Markdown

Fixes #290.

Problem

With -fentity-proxy-reflection, members_of enumerates using-shadow declarations as ReflectionKind::EntityProxy, so proxy reflections flow directly into ordinary consteval member queries and, as std::meta::info NTTPs, into the Itanium mangler. Several consumers still treated that kind as llvm_unreachable("proxies should already have been unwrapped"), turning valid code into ICEs (see #290 for self-contained reproducers and crash signatures).

Fix

  • clang/lib/AST/ExprConstantMeta.cpp — six metafunctions crashed when asked of a proxy: is_constructor, is_destructor, is_special_member_function, is_static_member, is_enumerable_type, has_complete_definition. Each now answers false, consistent with how the surrounding kind predicates already classify proxies (a shadow declaration is never itself one of these; the underlying entity's properties are available through underlying_entity_of). The remaining unreachable proxy arms were probed NOT reachable from user code (identifier_of/has_identifier/source_location_of pre-unwrap via MaybeUnproxy; substitute's dispatcher unwraps template arguments before TArgFromReflection; reflect_invoke through a proxy fails gracefully as not-a-constant-expression) and are left as-is.
  • clang/lib/AST/ItaniumMangle.cppmangleReflection's EntityProxy case encoded the proxy by mangling the shadow declaration's NAME (mangleNameWithAbiTags), with two defects: an operator-named shadow (e.g. using OB::operator*;, the shape absl::StatusOr<T> hits via using StatusOr::OperatorBase::operator*;) crashed outright (mangleUnqualifiedName casts the decl to FunctionDecl to disambiguate the operator name — cast<> assertion), and one using-declarator over an overload set introduced several same-named shadows whose proxies all mangled identically ("definition with same mangled name" at best, a silent linkonce_odr fold at worst). The mangler now encodes the proxy's TARGET declaration kind-aware (ctors/dtors via GlobalDecl, functions/variables/fields via mangle(), enum constants as literals, types via mangleCanonicalTypeName, else the source name), keeping the a tag so a proxy-of-X reflection stays a distinct template argument from a declaration-of-X reflection.

Test

  • libcxx/test/std/experimental/reflection/entity-proxy-member-queries.pass.cpp (new): a consteval sweep of all six metafunctions over proxies of every member kind — member function, static member function, static data member, field, type alias, nested class (each call ICEd before the fix) — plus runtime distinctness assertions for proxy NTTPs: an operator shadow and a four-overload value() shadow set from a class template specialization, asserting every proxy instantiation is distinct from its siblings and from its underlying declaration's instantiation (static_assert cannot catch a mangling fold).

Validation

  • Base: 837da39eb88c (current p2996 tip; applies cleanly).
  • Without the fix: each of the six metafunction calls independently hits the UNREACHABLE (ExprConstantMeta.cpp:5240/5391/5435/4563/4830/4789); the operator-shadow NTTP asserts in mangleUnqualifiedName; the overload-set NTTP errors with definition with same mangled name '_Z5probeIMaN1SIiE5valueE$EEiv'; the new test ICEs.
  • With the fix: reproducers and the new test compile and run clean.
  • clang/test/Reflection: 15/16 both at base and with the fix — identical results; the one failure (splice-exprs.cpp) is pre-existing at base and unrelated.
  • Downstream soak (reflection-driven nanobind binding generator): 50-test binder suite with all modules recompiled, plus real-library corpus runs including absl::StatusOr (the field trigger: its using StatusOr::OperatorBase::value/operator*/operator->; re-exports) and nlohmann/json — all green.

🤖 Generated with Claude Code

…ctions and NTTP mangling

With -fentity-proxy-reflection, members_of enumerates using-shadow
declarations as ReflectionKind::EntityProxy, so proxy reflections flow
directly into ordinary consteval member queries and, as info NTTPs, into
the Itanium mangler. Several consumers still treated that kind as
llvm_unreachable("proxies should already have been unwrapped"), turning
valid code into ICEs:

- Six metafunctions in ExprConstantMeta.cpp crashed when asked of a
  proxy: is_constructor, is_destructor, is_special_member_function,
  is_static_member, is_enumerable_type, has_complete_definition. Each
  now answers false, consistent with how the surrounding kind
  predicates already classify proxies (a shadow declaration is never
  itself one of these; the underlying entity's properties are available
  through underlying_entity_of). The remaining unreachable proxy arms
  were probed NOT reachable from user code: identifier_of /
  has_identifier / source_location_of pre-unwrap via MaybeUnproxy,
  substitute's dispatcher unwraps template arguments before
  TArgFromReflection, and reflect_invoke through a proxy fails
  gracefully as not-a-constant-expression.

- mangleReflection's EntityProxy case encoded the proxy by mangling the
  shadow declaration's NAME (mangleNameWithAbiTags), with two defects.
  An operator-named shadow (e.g. `using OB::operator*;`, the shape
  absl::StatusOr<T> hits via `using StatusOr::OperatorBase::operator*;`)
  crashed outright: mangleUnqualifiedName casts the decl to FunctionDecl
  to disambiguate the operator name, and a UsingShadowDecl is not one
  (cast<> assertion). And one using-declarator over an overload set
  introduces several same-named shadows whose proxies all mangled
  identically -- "definition with same mangled name" at best, a silent
  linkonce_odr fold at worst. The mangler now encodes the proxy's TARGET
  declaration kind-aware (constructors/destructors via GlobalDecl,
  functions/variables/fields via mangle(), enum constants as literals,
  types via mangleCanonicalTypeName, else the source name), keeping the
  'a' tag so a proxy-of-X reflection stays a distinct template argument
  from a declaration-of-X reflection.

Test: entity-proxy-member-queries.pass.cpp covers all six metafunctions
on proxies of a member function, static member function, static data
member, field, type alias, and nested class (each call ICEd before),
plus the mangler shapes: an operator shadow and a four-overload value()
shadow set from a class template specialization as info NTTPs, asserting
every proxy instantiation is distinct from its siblings and from its
underlying declaration's instantiation.

Fixes llvm#290.

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