[libcxx-commits] [libcxxabi] [llvm] [ItaniumDemangle] Demangle templated members of local-scope types (PR #201876)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 5 09:34:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxxabi
Author: mi11ion (mi11ione)
<details>
<summary>Changes</summary>
The Itanium demangler rejects valid, clang-emitted names where a templated member of a type nested in a <local-name> references a template parameter in its signature, e.g. a generic lambda's operator():
```
_ZZ1fvENK3barclIiEEvT_ → void f()::bar::operator()<int>(int) const
```
`__cxa_demangle` returns -2 and `llvm-cxxfilt` echoes the input unchanged. Yet clang emits this shape (e.g. `_ZZ1fvENK3$_0clIiEEDaT_`), so the demangler fails on symbols LLVM itself produces (internal lambdas in libclang/libLTO)
Cause: `parseLocalName`'s `SaveTemplateParams` scope restores (clears) the template-parameter table on return, so the enclosing encoding parses the trailing `T_` with no params in scope and rejects the name
Fix: when the local entity ends with a template-argument list, keep its scope alive for the enclosing encoding (like the non-local path, which has no such guard). Tests added to `DemangleTestCases.inc`, headers synced via `cp-to-llvm.sh`
Related to #<!-- -->45442
---
Full diff: https://github.com/llvm/llvm-project/pull/201876.diff
3 Files Affected:
- (modified) libcxxabi/src/demangle/ItaniumDemangle.h (+9-1)
- (modified) libcxxabi/test/DemangleTestCases.inc (+7)
- (modified) llvm/include/llvm/Demangle/ItaniumDemangle.h (+9-1)
``````````diff
diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 94d780e229631..05fe7acd143ff 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -2817,6 +2817,7 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser {
AbstractManglingParser *Parser;
decltype(TemplateParams) OldParams;
decltype(OuterTemplateParams) OldOuterParams;
+ bool Restore = true;
public:
SaveTemplateParams(AbstractManglingParser *TheParser) : Parser(TheParser) {
@@ -2826,9 +2827,12 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser {
Parser->OuterTemplateParams.clear();
}
~SaveTemplateParams() {
+ if (!Restore)
+ return;
Parser->TemplateParams = std::move(OldParams);
Parser->OuterTemplateParams = std::move(OldOuterParams);
}
+ void keepCurrentScope() { Restore = false; }
};
// Set of unresolved forward <template-param> references. These can occur in a
@@ -3135,7 +3139,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
}
// The template parameters of the inner name are unrelated to those of the
- // enclosing context.
+ // enclosing context, unless the name ends with a template argument list.
SaveTemplateParams SaveTemplateParamsScope(this);
if (consumeIf('d')) {
@@ -3145,6 +3149,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
Node *N = getDerived().parseName(State);
if (N == nullptr)
return nullptr;
+ if (State && State->EndsWithTemplateArgs)
+ SaveTemplateParamsScope.keepCurrentScope();
return make<LocalName>(Encoding, N);
}
@@ -3152,6 +3158,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
if (Entity == nullptr)
return nullptr;
First = parse_discriminator(First, Last);
+ if (State && State->EndsWithTemplateArgs)
+ SaveTemplateParamsScope.keepCurrentScope();
return make<LocalName>(Encoding, Entity);
}
diff --git a/libcxxabi/test/DemangleTestCases.inc b/libcxxabi/test/DemangleTestCases.inc
index 6b7a99d849a94..7508fec084a1f 100644
--- a/libcxxabi/test/DemangleTestCases.inc
+++ b/libcxxabi/test/DemangleTestCases.inc
@@ -30234,4 +30234,11 @@
{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&) (.alloc_token)"},
{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234) (.alloc_token)"},
{"__alloc_token_123__Z3foov", "foo() (.alloc_token)"},
+
+{"_ZZ1fvENK3barclIiEEvT_", "void f()::bar::operator()<int>(int) const"},
+{"_ZZ1fvEN3$_0clIiEEvT_", "void f()::$_0::operator()<int>(int)"},
+{"_ZZ1fvENK3$_01gIiEEvT_", "void f()::$_0::g<int>(int) const"},
+{"_ZZ1fvENK3$_0clIiEEDaT_", "auto f()::$_0::operator()<int>(int) const"},
+{"_ZZL17isSafeToIgnoreCWDRKN5clang21CowCompilerInvocationEENK3$_0clINSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEEEDaRKT_", "auto isSafeToIgnoreCWD(clang::CowCompilerInvocation const&)::$_0::operator()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) const"},
+{"_Z3fooIZN3BarC1EvE3$_0EvT_", "void foo<Bar::Bar()::$_0>(Bar::Bar()::$_0)"},
// clang-format on
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 04338e1d7cb51..8710980a4a99e 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -2817,6 +2817,7 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser {
AbstractManglingParser *Parser;
decltype(TemplateParams) OldParams;
decltype(OuterTemplateParams) OldOuterParams;
+ bool Restore = true;
public:
SaveTemplateParams(AbstractManglingParser *TheParser) : Parser(TheParser) {
@@ -2826,9 +2827,12 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser {
Parser->OuterTemplateParams.clear();
}
~SaveTemplateParams() {
+ if (!Restore)
+ return;
Parser->TemplateParams = std::move(OldParams);
Parser->OuterTemplateParams = std::move(OldOuterParams);
}
+ void keepCurrentScope() { Restore = false; }
};
// Set of unresolved forward <template-param> references. These can occur in a
@@ -3135,7 +3139,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
}
// The template parameters of the inner name are unrelated to those of the
- // enclosing context.
+ // enclosing context, unless the name ends with a template argument list.
SaveTemplateParams SaveTemplateParamsScope(this);
if (consumeIf('d')) {
@@ -3145,6 +3149,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
Node *N = getDerived().parseName(State);
if (N == nullptr)
return nullptr;
+ if (State && State->EndsWithTemplateArgs)
+ SaveTemplateParamsScope.keepCurrentScope();
return make<LocalName>(Encoding, N);
}
@@ -3152,6 +3158,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
if (Entity == nullptr)
return nullptr;
First = parse_discriminator(First, Last);
+ if (State && State->EndsWithTemplateArgs)
+ SaveTemplateParamsScope.keepCurrentScope();
return make<LocalName>(Encoding, Entity);
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/201876
More information about the libcxx-commits
mailing list