Skip to content

Commit 4d5079c

Browse files
authored
[libcxxabi][ItaniumDemangle] Demangle explicitly named object parameters (#72881)
The mangling for an explicitly named object was introduced in https://reviews.llvm.org/D140828 See following discussion for why a new mangling had to be introduced: itanium-cxx-abi/cxx-abi#148 Since clang started emitting names with the new mangling, this patch implements support for demangling such names. The approach this patch takes is to add a new `ExplicitObjectParameter` node that will print the first parameter of a function declaration with a `this ` prefix, to reflect what was spelled out in source. Example: ``` void MyClass::func(this MyClass const& self); // _ZNH7MyClass4funcERKS_ ``` With this patch, the above demangles to: ``` _ZNH7MyClass4funcERKS_ -> MyClass::func(this MyClass const&) ``` Note that `func` is not marked as `const &`, since the function-qualifiers are now encoded as part of the explicit `this`. C++ doesn't allow specifying the function-qualifiers in the presence of an explicit object parameter, so this demangling is consistent with the source spelling.
1 parent ec76d39 commit 4d5079c

File tree

5 files changed

+118
-16
lines changed

5 files changed

+118
-16
lines changed

libcxxabi/src/demangle/ItaniumDemangle.h

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,32 @@ class DynamicExceptionSpec : public Node {
889889
}
890890
};
891891

892+
/// Represents the explicitly named object parameter.
893+
/// E.g.,
894+
/// \code{.cpp}
895+
/// struct Foo {
896+
/// void bar(this Foo && self);
897+
/// };
898+
/// \endcode
899+
class ExplicitObjectParameter final : public Node {
900+
Node *Base;
901+
902+
public:
903+
ExplicitObjectParameter(Node *Base_)
904+
: Node(KExplicitObjectParameter), Base(Base_) {
905+
DEMANGLE_ASSERT(
906+
Base != nullptr,
907+
"Creating an ExplicitObjectParameter without a valid Base Node.");
908+
}
909+
910+
template <typename Fn> void match(Fn F) const { F(Base); }
911+
912+
void printLeft(OutputBuffer &OB) const override {
913+
OB += "this ";
914+
Base->print(OB);
915+
}
916+
};
917+
892918
class FunctionEncoding final : public Node {
893919
const Node *Ret;
894920
const Node *Name;
@@ -2780,6 +2806,7 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser {
27802806
Qualifiers CVQualifiers = QualNone;
27812807
FunctionRefQual ReferenceQualifier = FrefQualNone;
27822808
size_t ForwardTemplateRefsBegin;
2809+
bool HasExplicitObjectParameter = false;
27832810

27842811
NameState(AbstractManglingParser *Enclosing)
27852812
: ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {}
@@ -3438,15 +3465,25 @@ AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) {
34383465
if (!consumeIf('N'))
34393466
return nullptr;
34403467

3441-
Qualifiers CVTmp = parseCVQualifiers();
3442-
if (State) State->CVQualifiers = CVTmp;
3468+
// 'H' specifies that the encoding that follows
3469+
// has an explicit object parameter.
3470+
if (!consumeIf('H')) {
3471+
Qualifiers CVTmp = parseCVQualifiers();
3472+
if (State)
3473+
State->CVQualifiers = CVTmp;
34433474

3444-
if (consumeIf('O')) {
3445-
if (State) State->ReferenceQualifier = FrefQualRValue;
3446-
} else if (consumeIf('R')) {
3447-
if (State) State->ReferenceQualifier = FrefQualLValue;
3448-
} else {
3449-
if (State) State->ReferenceQualifier = FrefQualNone;
3475+
if (consumeIf('O')) {
3476+
if (State)
3477+
State->ReferenceQualifier = FrefQualRValue;
3478+
} else if (consumeIf('R')) {
3479+
if (State)
3480+
State->ReferenceQualifier = FrefQualLValue;
3481+
} else {
3482+
if (State)
3483+
State->ReferenceQualifier = FrefQualNone;
3484+
}
3485+
} else if (State) {
3486+
State->HasExplicitObjectParameter = true;
34503487
}
34513488

34523489
Node *SoFar = nullptr;
@@ -5422,6 +5459,14 @@ Node *AbstractManglingParser<Derived, Alloc>::parseEncoding() {
54225459
Node *Ty = getDerived().parseType();
54235460
if (Ty == nullptr)
54245461
return nullptr;
5462+
5463+
const bool IsFirstParam = ParamsBegin == Names.size();
5464+
if (NameInfo.HasExplicitObjectParameter && IsFirstParam)
5465+
Ty = make<ExplicitObjectParameter>(Ty);
5466+
5467+
if (Ty == nullptr)
5468+
return nullptr;
5469+
54255470
Names.push_back(Ty);
54265471
} while (!IsEndOfEncoding() && look() != 'Q');
54275472
Params = popTrailingNodeArray(ParamsBegin);

libcxxabi/src/demangle/ItaniumNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,6 @@ NODE(RequiresExpr)
9999
NODE(ExprRequirement)
100100
NODE(TypeRequirement)
101101
NODE(NestedRequirement)
102+
NODE(ExplicitObjectParameter)
102103

103104
#undef NODE

libcxxabi/test/test_demangle.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30175,6 +30175,16 @@ const char* cases[][2] =
3017530175
{"_Z2f5IKiEvu14__remove_constIT_E", "void f5<int const>(__remove_const(int const))"},
3017630176
{"_Z2f5IPiEvu16__remove_pointerIT_E", "void f5<int*>(__remove_pointer(int*))"},
3017730177
{"_Z2f5IiEvu14__remove_cvrefIT_E", "void f5<int>(__remove_cvref(int))"},
30178+
30179+
// C++23 explicit object parameter
30180+
{"_ZNH3Foo3fooES_i", "Foo::foo(this Foo, int)"},
30181+
{"_ZNH3Foo3fooERKS_i", "Foo::foo(this Foo const&, int)"},
30182+
{"_ZNH1X3fooIRS_EEvOT_i", "void X::foo<X&>(this X&, int)"},
30183+
{"_ZZNH2ns3Foo3fooES0_iENH4Foo24foo2EOKS1_", "ns::Foo::foo(this ns::Foo, int)::Foo2::foo2(this Foo2 const&&)" },
30184+
{"_ZNH2ns3FooB7Foo_tag3fooB7foo_tagERKS0_i", "ns::Foo[abi:Foo_tag]::foo[abi:foo_tag](this ns::Foo[abi:Foo_tag] const&, int)" },
30185+
{"_ZZN3Foo3fooEiENH4Foo24foo2EOKS0_", "Foo::foo(int)::Foo2::foo2(this Foo2 const&&)"},
30186+
{"_ZZNH3Foo3fooES_iENK4Foo24foo2Ev", "Foo::foo(this Foo, int)::Foo2::foo2() const" },
30187+
{"_ZNH3FooclERKS_", "Foo::operator()(this Foo const&)"},
3017830188
};
3017930189
// clang-format on
3018030190

llvm/include/llvm/Demangle/ItaniumDemangle.h

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,32 @@ class DynamicExceptionSpec : public Node {
888888
}
889889
};
890890

891+
/// Represents the explicitly named object parameter.
892+
/// E.g.,
893+
/// \code{.cpp}
894+
/// struct Foo {
895+
/// void bar(this Foo && self);
896+
/// };
897+
/// \endcode
898+
class ExplicitObjectParameter final : public Node {
899+
Node *Base;
900+
901+
public:
902+
ExplicitObjectParameter(Node *Base_)
903+
: Node(KExplicitObjectParameter), Base(Base_) {
904+
DEMANGLE_ASSERT(
905+
Base != nullptr,
906+
"Creating an ExplicitObjectParameter without a valid Base Node.");
907+
}
908+
909+
template <typename Fn> void match(Fn F) const { F(Base); }
910+
911+
void printLeft(OutputBuffer &OB) const override {
912+
OB += "this ";
913+
Base->print(OB);
914+
}
915+
};
916+
891917
class FunctionEncoding final : public Node {
892918
const Node *Ret;
893919
const Node *Name;
@@ -2779,6 +2805,7 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser {
27792805
Qualifiers CVQualifiers = QualNone;
27802806
FunctionRefQual ReferenceQualifier = FrefQualNone;
27812807
size_t ForwardTemplateRefsBegin;
2808+
bool HasExplicitObjectParameter = false;
27822809

27832810
NameState(AbstractManglingParser *Enclosing)
27842811
: ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {}
@@ -3437,15 +3464,25 @@ AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) {
34373464
if (!consumeIf('N'))
34383465
return nullptr;
34393466

3440-
Qualifiers CVTmp = parseCVQualifiers();
3441-
if (State) State->CVQualifiers = CVTmp;
3467+
// 'H' specifies that the encoding that follows
3468+
// has an explicit object parameter.
3469+
if (!consumeIf('H')) {
3470+
Qualifiers CVTmp = parseCVQualifiers();
3471+
if (State)
3472+
State->CVQualifiers = CVTmp;
34423473

3443-
if (consumeIf('O')) {
3444-
if (State) State->ReferenceQualifier = FrefQualRValue;
3445-
} else if (consumeIf('R')) {
3446-
if (State) State->ReferenceQualifier = FrefQualLValue;
3447-
} else {
3448-
if (State) State->ReferenceQualifier = FrefQualNone;
3474+
if (consumeIf('O')) {
3475+
if (State)
3476+
State->ReferenceQualifier = FrefQualRValue;
3477+
} else if (consumeIf('R')) {
3478+
if (State)
3479+
State->ReferenceQualifier = FrefQualLValue;
3480+
} else {
3481+
if (State)
3482+
State->ReferenceQualifier = FrefQualNone;
3483+
}
3484+
} else if (State) {
3485+
State->HasExplicitObjectParameter = true;
34493486
}
34503487

34513488
Node *SoFar = nullptr;
@@ -5421,6 +5458,14 @@ Node *AbstractManglingParser<Derived, Alloc>::parseEncoding() {
54215458
Node *Ty = getDerived().parseType();
54225459
if (Ty == nullptr)
54235460
return nullptr;
5461+
5462+
const bool IsFirstParam = ParamsBegin == Names.size();
5463+
if (NameInfo.HasExplicitObjectParameter && IsFirstParam)
5464+
Ty = make<ExplicitObjectParameter>(Ty);
5465+
5466+
if (Ty == nullptr)
5467+
return nullptr;
5468+
54245469
Names.push_back(Ty);
54255470
} while (!IsEndOfEncoding() && look() != 'Q');
54265471
Params = popTrailingNodeArray(ParamsBegin);

llvm/include/llvm/Demangle/ItaniumNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,6 @@ NODE(RequiresExpr)
9999
NODE(ExprRequirement)
100100
NODE(TypeRequirement)
101101
NODE(NestedRequirement)
102+
NODE(ExplicitObjectParameter)
102103

103104
#undef NODE

0 commit comments

Comments
 (0)