[libcxx-commits] [libcxxabi] [lldb] [llvm] [lldb][DemangledNameInfo] Fix tracking of template arugments (PR #166578)
Michael Buch via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Nov 5 08:12:33 PST 2025
https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/166578
Depends on:
* https://github.com/llvm/llvm-project/pull/166577
The `isGtInsideTemplateArgs` wasn't the correct API to use for answering
whether we are currently printing template arugments. One example is:
```
void func<(foo::Enum)1>()
```
Here `OutputBuffer::GtIsGt > 0` despite us being inside template arguments (because we incremented it when seeing '(').
This patch now uses the correct API and adds the above as a test-case.
>From 35e239cc0f7349a82af5efd7b5937ec0a189e23d Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 5 Nov 2025 16:07:12 +0000
Subject: [PATCH 1/2] [libcxxabi][ItaniumDemangle] Separate GtIsGt counter into
more states
Currently `OutputBuffer::GtIsGt` is used to tell us if we're inside template arguments and have printed a '(' without a closing ')'. If so, we don't need to quote '<' when printing it as part of a binary expression inside a template argument. Otherwise we need to. E.g.,
```
foo<a<(b < c)>> // Quotes around binary expression needed.
```
LLDB's `TrackingOutputBuffer` has heuristics that rely on checking whether we are inside template arguments, regardless of the current parentheses depth. We've been using `isGtInsideTemplateArgs` for this, but that isn't correct. Resulting in us incorrectly tracking the basename of function like:
```
void func<(foo::Enum)1>()
```
Here `GtIsGt > 0` despite us being inside template arguments (because we incremented it when seeing '(').
This patch adds a `isInsideTemplateArgs` API which LLDB will use to more accurately track parts of the demangled name.
To make sure this API doesn't go untested in the actual libcxxabi test-suite, I changed the existing `GtIsGt` logic to use it. Also renamed the various variables/APIs involved to make it (in my opinion) more straightforward to understand what's going on. But happy to rename it back if people disagree.
---
libcxxabi/src/demangle/ItaniumDemangle.h | 12 +++++----
libcxxabi/src/demangle/Utility.h | 26 +++++++++++++++-----
llvm/include/llvm/Demangle/ItaniumDemangle.h | 12 +++++----
llvm/include/llvm/Demangle/Utility.h | 26 +++++++++++++++-----
4 files changed, 54 insertions(+), 22 deletions(-)
diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 6f27da7b9cadf..b999438ff2ca8 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -1366,7 +1366,7 @@ class TemplateTemplateParamDecl final : public Node {
template <typename Fn> void match(Fn F) const { F(Name, Params, Requires); }
void printLeft(OutputBuffer &OB) const override {
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "template<";
Params.printWithComma(OB);
OB += "> typename ";
@@ -1550,7 +1550,7 @@ class TemplateArgs final : public Node {
NodeArray getParams() { return Params; }
void printLeft(OutputBuffer &OB) const override {
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "<";
Params.printWithComma(OB);
OB += ">";
@@ -1824,7 +1824,7 @@ class ClosureTypeName : public Node {
void printDeclarator(OutputBuffer &OB) const {
if (!TemplateParams.empty()) {
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "<";
TemplateParams.printWithComma(OB);
OB += ">";
@@ -1885,7 +1885,9 @@ class BinaryExpr : public Node {
}
void printLeft(OutputBuffer &OB) const override {
- bool ParenAll = OB.isGtInsideTemplateArgs() &&
+ // If we're printing a '<' inside of a template argument, and we haven't
+ // yet parenthesized the expression, do so now.
+ bool ParenAll = !OB.isInParensInTemplateArgs() &&
(InfixOperator == ">" || InfixOperator == ">>");
if (ParenAll)
OB.printOpen();
@@ -2061,7 +2063,7 @@ class CastExpr : public Node {
void printLeft(OutputBuffer &OB) const override {
OB += CastKind;
{
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "<";
OB.printLeft(*To);
OB += ">";
diff --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h
index 76243f5d3280c..df5b54dca492d 100644
--- a/libcxxabi/src/demangle/Utility.h
+++ b/libcxxabi/src/demangle/Utility.h
@@ -104,18 +104,32 @@ class OutputBuffer {
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
- /// When zero, we're printing template args and '>' needs to be parenthesized.
- /// Use a counter so we can simply increment inside parentheses.
- unsigned GtIsGt = 1;
+ struct {
+ /// The depth of '(' and ')' inside the currently printed template
+ /// arguments.
+ unsigned ParenDepth = 0;
- bool isGtInsideTemplateArgs() const { return GtIsGt == 0; }
+ /// True if we're currently printing a template argument.
+ bool InsideTemplate = false;
+ } TemplateTracker;
+
+ /// Returns true if we're currently between a '(' and ')' when printing
+ /// template args.
+ bool isInParensInTemplateArgs() const {
+ return TemplateTracker.ParenDepth > 0;
+ }
+
+ /// Returns true if we're printing template args.
+ bool isInsideTemplateArgs() const { return TemplateTracker.InsideTemplate; }
void printOpen(char Open = '(') {
- GtIsGt++;
+ if (isInsideTemplateArgs())
+ TemplateTracker.ParenDepth++;
*this += Open;
}
void printClose(char Close = ')') {
- GtIsGt--;
+ if (isInsideTemplateArgs())
+ TemplateTracker.ParenDepth--;
*this += Close;
}
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 62d427c3966bb..67de123fdbad5 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -1366,7 +1366,7 @@ class TemplateTemplateParamDecl final : public Node {
template <typename Fn> void match(Fn F) const { F(Name, Params, Requires); }
void printLeft(OutputBuffer &OB) const override {
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "template<";
Params.printWithComma(OB);
OB += "> typename ";
@@ -1550,7 +1550,7 @@ class TemplateArgs final : public Node {
NodeArray getParams() { return Params; }
void printLeft(OutputBuffer &OB) const override {
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "<";
Params.printWithComma(OB);
OB += ">";
@@ -1824,7 +1824,7 @@ class ClosureTypeName : public Node {
void printDeclarator(OutputBuffer &OB) const {
if (!TemplateParams.empty()) {
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "<";
TemplateParams.printWithComma(OB);
OB += ">";
@@ -1885,7 +1885,9 @@ class BinaryExpr : public Node {
}
void printLeft(OutputBuffer &OB) const override {
- bool ParenAll = OB.isGtInsideTemplateArgs() &&
+ // If we're printing a '<' inside of a template argument, and we haven't
+ // yet parenthesized the expression, do so now.
+ bool ParenAll = !OB.isInParensInTemplateArgs() &&
(InfixOperator == ">" || InfixOperator == ">>");
if (ParenAll)
OB.printOpen();
@@ -2061,7 +2063,7 @@ class CastExpr : public Node {
void printLeft(OutputBuffer &OB) const override {
OB += CastKind;
{
- ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
+ ScopedOverride<bool> LT(OB.TemplateTracker.InsideTemplate, true);
OB += "<";
OB.printLeft(*To);
OB += ">";
diff --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h
index 6e6203d716e7a..afdc1a397ca6f 100644
--- a/llvm/include/llvm/Demangle/Utility.h
+++ b/llvm/include/llvm/Demangle/Utility.h
@@ -104,18 +104,32 @@ class OutputBuffer {
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
- /// When zero, we're printing template args and '>' needs to be parenthesized.
- /// Use a counter so we can simply increment inside parentheses.
- unsigned GtIsGt = 1;
+ struct {
+ /// The depth of '(' and ')' inside the currently printed template
+ /// arguments.
+ unsigned ParenDepth = 0;
- bool isGtInsideTemplateArgs() const { return GtIsGt == 0; }
+ /// True if we're currently printing a template argument.
+ bool InsideTemplate = false;
+ } TemplateTracker;
+
+ /// Returns true if we're currently between a '(' and ')' when printing
+ /// template args.
+ bool isInParensInTemplateArgs() const {
+ return TemplateTracker.ParenDepth > 0;
+ }
+
+ /// Returns true if we're printing template args.
+ bool isInsideTemplateArgs() const { return TemplateTracker.InsideTemplate; }
void printOpen(char Open = '(') {
- GtIsGt++;
+ if (isInsideTemplateArgs())
+ TemplateTracker.ParenDepth++;
*this += Open;
}
void printClose(char Close = ')') {
- GtIsGt--;
+ if (isInsideTemplateArgs())
+ TemplateTracker.ParenDepth--;
*this += Close;
}
>From fe949989de7b5db75b6555c90ee41ba0085c64cd Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 5 Nov 2025 16:09:08 +0000
Subject: [PATCH 2/2] [lldb][DemangledNameInfo] Fix tracking of template
arugments
Depends on:
* https://github.com/llvm/llvm-project/pull/166577
The `isGtInsideTemplateArgs` wasn't the correct API to use for answering
whether we are currently printing template arugments. One example is:
```
void func<(foo::Enum)1>()
```
Here `OutputBuffer::GtIsGt > 0` despite us being inside template arguments (because we incremented it when seeing '(').
This patch now uses the correct API and adds the above as a test-case.
---
lldb/source/Core/DemangledNameInfo.cpp | 4 ++--
lldb/unittests/Core/MangledTest.cpp | 10 ++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp
index 76f8987c5149c..16fbfda299b21 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -16,7 +16,7 @@ bool TrackingOutputBuffer::shouldTrack() const {
if (!isPrintingTopLevelFunctionType())
return false;
- if (isGtInsideTemplateArgs())
+ if (isInsideTemplateArgs())
return false;
if (NameInfo.ArgumentsRange.first > 0)
@@ -29,7 +29,7 @@ bool TrackingOutputBuffer::canFinalize() const {
if (!isPrintingTopLevelFunctionType())
return false;
- if (isGtInsideTemplateArgs())
+ if (isInsideTemplateArgs())
return false;
if (NameInfo.ArgumentsRange.first == 0)
diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp
index cbc0c5d951b99..706e67801e01a 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -636,6 +636,16 @@ DemanglingPartsTestCase g_demangling_parts_test_cases[] = {
/*.basename=*/"operator()",
/*.scope=*/"dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_0::",
/*.qualifiers=*/" const",
+ },
+ {"_Z4funcILN3foo4EnumE1EEvv",
+ {
+ /*.BasenameRange=*/{5, 9}, /*.TemplateArgumentsRange=*/{9, 23}, /*.ScopeRange=*/{5, 5},
+ /*.ArgumentsRange=*/{23, 25}, /*.QualifiersRange=*/{25, 25}, /*.NameQualifiersRange=*/{0, 0},
+ /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+ },
+ /*.basename=*/"func",
+ /*.scope=*/"",
+ /*.qualifiers=*/"",
}
// clang-format on
};
More information about the libcxx-commits
mailing list