[libcxxabi][ItaniumDemangle] Separate GtIsGt counter into more states (#166578)

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.

Also adjusted LLDB to use the newly introduced API (and added a
unit-test that would previously fail).
This commit is contained in:
Michael Buch 2025-11-07 07:52:03 +00:00 committed by GitHub
parent fc5e0c071b
commit 54c9ddddd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 66 additions and 24 deletions

View File

@ -1366,7 +1366,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
}
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 @@ public:
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 += ">";

View File

@ -104,18 +104,32 @@ public:
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;
}

View File

@ -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)

View File

@ -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
};

View File

@ -1366,7 +1366,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
}
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 @@ public:
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 += ">";

View File

@ -104,18 +104,32 @@ public:
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;
}