Skip to content

[Clang] Mangling of pack indexing type and expression for itanium #123513

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

cor3ntin
Copy link
Contributor

@cor3ntin cor3ntin commented Jan 19, 2025

Copy link

github-actions bot commented Jan 19, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@cor3ntin cor3ntin changed the title [WIP][Clang] Mangling of pack indexing type and expression for itanium [Clang] Mangling of pack indexing type and expression for itanium Jan 26, 2025
@cor3ntin cor3ntin marked this pull request as ready for review January 26, 2025 21:14
@cor3ntin cor3ntin requested a review from a team as a code owner January 26, 2025 21:14
@llvmbot llvmbot added clang Clang issues not falling into any other category libc++abi libc++abi C++ Runtime Library. Not libc++. clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Jan 26, 2025

@llvm/pr-subscribers-libcxxabi

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

See itanium-cxx-abi/cxx-abi#198
and #112003


Full diff: https://github.com/llvm/llvm-project/pull/123513.diff

8 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+58-31)
  • (added) clang/test/CodeGenCXX/mangle-cxx2c.cpp (+11)
  • (modified) libcxxabi/src/demangle/ItaniumDemangle.h (+43)
  • (modified) libcxxabi/src/demangle/ItaniumNodes.def (+1-1)
  • (modified) libcxxabi/test/test_demangle.pass.cpp (+5)
  • (modified) llvm/include/llvm/Demangle/ItaniumDemangle.h (+43)
  • (modified) llvm/include/llvm/Demangle/ItaniumNodes.def (+1-1)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b63bd366cfe884..e5946fc71c56a6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1003,6 +1003,7 @@ Bug Fixes to C++ Support
   lambda functions or inline friend functions defined inside templates (#GH122493).
 - Clang now rejects declaring an alias template with the same name as its template parameter. (#GH123423)
 - Correctly determine the implicit constexprness of lambdas in dependent contexts. (#GH97958) (#GH114234)
+- Implement Itanium mangling for pack indexing. (#GH112003)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 1dd936cf4fb518..4b2cb630e16f87 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -603,6 +603,7 @@ class CXXNameMangler {
   void mangleInitListElements(const InitListExpr *InitList);
   void mangleRequirement(SourceLocation RequiresExprLoc,
                          const concepts::Requirement *Req);
+  void mangleReferenceToPack(const NamedDecl *ND);
   void mangleExpression(const Expr *E, unsigned Arity = UnknownArity,
                         bool AsTemplateArg = false);
   void mangleCXXCtorType(CXXCtorType T, const CXXRecordDecl *InheritedFrom);
@@ -4348,10 +4349,10 @@ void CXXNameMangler::mangleType(const PackExpansionType *T) {
 }
 
 void CXXNameMangler::mangleType(const PackIndexingType *T) {
-  if (!T->hasSelectedType())
-    mangleType(T->getPattern());
-  else
-    mangleType(T->getSelectedType());
+  // <type>  ::= Dy <type> <expression>  # pack indexing type (C++23)
+  Out << "Dy";
+  mangleType(T->getPattern());
+  mangleExpression(T->getIndexExpr());
 }
 
 void CXXNameMangler::mangleType(const ObjCInterfaceType *T) {
@@ -4785,35 +4786,63 @@ void CXXNameMangler::mangleRequirement(SourceLocation RequiresExprLoc,
   }
 }
 
+void CXXNameMangler::mangleReferenceToPack(const NamedDecl *Pack) {
+  if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Pack))
+    mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
+  else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Pack))
+    mangleTemplateParameter(NTTP->getDepth(), NTTP->getIndex());
+  else if (const auto *TempTP = dyn_cast<TemplateTemplateParmDecl>(Pack))
+    mangleTemplateParameter(TempTP->getDepth(), TempTP->getIndex());
+  else
+    mangleFunctionParam(cast<ParmVarDecl>(Pack));
+}
+
 void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
                                       bool AsTemplateArg) {
   // <expression> ::= <unary operator-name> <expression>
   //              ::= <binary operator-name> <expression> <expression>
-  //              ::= <trinary operator-name> <expression> <expression> <expression>
-  //              ::= cv <type> expression           # conversion with one argument
-  //              ::= cv <type> _ <expression>* E # conversion with a different number of arguments
-  //              ::= dc <type> <expression>         # dynamic_cast<type> (expression)
-  //              ::= sc <type> <expression>         # static_cast<type> (expression)
-  //              ::= cc <type> <expression>         # const_cast<type> (expression)
-  //              ::= rc <type> <expression>         # reinterpret_cast<type> (expression)
+  //              ::= <trinary operator-name> <expression> <expression>
+  //              <expression>
+  //              ::= cv <type> expression           # conversion with one
+  //              argument
+  //              ::= cv <type> _ <expression>* E # conversion with a different
+  //              number of arguments
+  //              ::= dc <type> <expression>         # dynamic_cast<type>
+  //              (expression)
+  //              ::= sc <type> <expression>         # static_cast<type>
+  //              (expression)
+  //              ::= cc <type> <expression>         # const_cast<type>
+  //              (expression)
+  //              ::= rc <type> <expression>         # reinterpret_cast<type>
+  //              (expression)
   //              ::= st <type>                      # sizeof (a type)
   //              ::= at <type>                      # alignof (a type)
   //              ::= <template-param>
   //              ::= <function-param>
-  //              ::= fpT                            # 'this' expression (part of <function-param>)
-  //              ::= sr <type> <unqualified-name>                   # dependent name
-  //              ::= sr <type> <unqualified-name> <template-args>   # dependent template-id
-  //              ::= ds <expression> <expression>                   # expr.*expr
-  //              ::= sZ <template-param>                            # size of a parameter pack
+  //              ::= fpT                            # 'this' expression (part
+  //              of <function-param>)
+  //              ::= sr <type> <unqualified-name>                   # dependent
+  //              name
+  //              ::= sr <type> <unqualified-name> <template-args>   # dependent
+  //              template-id
+  //              ::= sy <expression> <expression>                   # pack
+  //              indexing expression
+  //              ::= ds <expression> <expression>                   #
+  //              expr.*expr
+  //              ::= sZ <template-param>                            # size of a
+  //              parameter pack
   //              ::= sZ <function-param>    # size of a function parameter pack
-  //              ::= u <source-name> <template-arg>* E # vendor extended expression
+  //              ::= u <source-name> <template-arg>* E # vendor extended
+  //              expression
   //              ::= <expr-primary>
   // <expr-primary> ::= L <type> <value number> E    # integer literal
   //                ::= L <type> <value float> E     # floating literal
   //                ::= L <type> <string type> E     # string literal
   //                ::= L <nullptr type> E           # nullptr literal "LDnE"
-  //                ::= L <pointer type> 0 E         # null pointer template argument
-  //                ::= L <type> <real-part float> _ <imag-part float> E    # complex floating point literal (C99); not used by clang
+  //                ::= L <pointer type> 0 E         # null pointer template
+  //                argument
+  //                ::= L <type> <real-part float> _ <imag-part float> E    #
+  //                complex floating point literal (C99); not used by clang
   //                ::= L <mangled-name> E           # external name
   QualType ImplicitlyConvertedToType;
 
@@ -4886,7 +4915,6 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::OMPIteratorExprClass:
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXParenListInitExprClass:
-  case Expr::PackIndexingExprClass:
     llvm_unreachable("unexpected statement kind");
 
   case Expr::ConstantExprClass:
@@ -5788,17 +5816,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
     }
 
     Out << "sZ";
-    const NamedDecl *Pack = SPE->getPack();
-    if (const TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Pack))
-      mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
-    else if (const NonTypeTemplateParmDecl *NTTP
-                = dyn_cast<NonTypeTemplateParmDecl>(Pack))
-      mangleTemplateParameter(NTTP->getDepth(), NTTP->getIndex());
-    else if (const TemplateTemplateParmDecl *TempTP
-                                    = dyn_cast<TemplateTemplateParmDecl>(Pack))
-      mangleTemplateParameter(TempTP->getDepth(), TempTP->getIndex());
-    else
-      mangleFunctionParam(cast<ParmVarDecl>(Pack));
+    mangleReferenceToPack(SPE->getPack());
     break;
   }
 
@@ -5828,6 +5846,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
     break;
   }
 
+  case Expr::PackIndexingExprClass: {
+    auto *PE = cast<PackIndexingExpr>(E);
+    NotPrimaryExpr();
+    Out << "sy";
+    mangleReferenceToPack(PE->getPackDecl());
+    mangleExpression(PE->getIndexExpr());
+    break;
+  }
+
   case Expr::CXXThisExprClass:
     NotPrimaryExpr();
     Out << "fpT";
diff --git a/clang/test/CodeGenCXX/mangle-cxx2c.cpp b/clang/test/CodeGenCXX/mangle-cxx2c.cpp
new file mode 100644
index 00000000000000..3e4689948a7a7e
--- /dev/null
+++ b/clang/test/CodeGenCXX/mangle-cxx2c.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu -std=c++2a | FileCheck %s
+
+namespace spaceship {
+  struct X {};
+  struct Y {};
+  int operator<=>(X, Y);
+
+  // CHECK-LABEL: define {{.*}} @_ZN9spaceship1fIiEEvDTcmltcvNS_1YE_EcvNS_1XE_EcvT__EE
+  template<typename T> void f(decltype(Y() < X(), T()) x) {}
+  template void f<int>(int);
+}
diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index 3df41b5f4d7d07..4cda6514bfac35 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -1521,6 +1521,27 @@ class ParameterPackExpansion final : public Node {
   }
 };
 
+class PackIndexing final : public Node {
+  const Node *Pattern;
+  const Node *Index;
+
+public:
+  PackIndexing(const Node *Pattern_, const Node *Index_)
+      : Node(KPackIndexing), Pattern(Pattern_), Index(Index_) {}
+
+  template <typename Fn> void match(Fn F) const { F(Pattern, Index); }
+
+  void printLeft(OutputBuffer &OB) const override {
+    OB.printOpen('(');
+    ParameterPackExpansion PPE(Pattern);
+    PPE.printLeft(OB);
+    OB.printClose(')');
+    OB.printOpen('[');
+    Index->printLeft(OB);
+    OB.printClose(']');
+  }
+};
+
 class TemplateArgs final : public Node {
   NodeArray Params;
   Node *Requires;
@@ -4510,6 +4531,18 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() {
       Result = make<ParameterPackExpansion>(Child);
       break;
     }
+    //           ::= Dy <type> <expression> # pack indexing (C++26)
+    case 'y': {
+      First += 2;
+      Node *Pattern = getDerived().parseType();
+      if (!Pattern)
+        return nullptr;
+      Node *Index = getDerived().parseExpr();
+      if (!Index)
+        return nullptr;
+      Result = make<PackIndexing>(Pattern, Index);
+      break;
+    }
     // Exception specifier on a function type.
     case 'o':
     case 'O':
@@ -5354,6 +5387,16 @@ Node *AbstractManglingParser<Derived, Alloc>::parseExpr() {
       return nullptr;
     return make<ParameterPackExpansion>(Child);
   }
+  if (consumeIf("sy")) {
+    Node *Pattern = look() == 'T' ? getDerived().parseTemplateParam()
+                                  : getDerived().parseFunctionParam();
+    if (Pattern == nullptr)
+      return nullptr;
+    Node *Index = getDerived().parseExpr();
+    if (Index == nullptr)
+      return nullptr;
+    return make<PackIndexing>(Pattern, Index);
+  }
   if (consumeIf("sZ")) {
     if (look() == 'T') {
       Node *R = getDerived().parseTemplateParam();
diff --git a/libcxxabi/src/demangle/ItaniumNodes.def b/libcxxabi/src/demangle/ItaniumNodes.def
index 18f5d52b47e911..2c183ec5d23f86 100644
--- a/libcxxabi/src/demangle/ItaniumNodes.def
+++ b/libcxxabi/src/demangle/ItaniumNodes.def
@@ -100,5 +100,5 @@ NODE(ExprRequirement)
 NODE(TypeRequirement)
 NODE(NestedRequirement)
 NODE(ExplicitObjectParameter)
-
+NODE(PackIndexing)
 #undef NODE
diff --git a/libcxxabi/test/test_demangle.pass.cpp b/libcxxabi/test/test_demangle.pass.cpp
index e9c74f70a094b5..e0a84de5e8cef9 100644
--- a/libcxxabi/test/test_demangle.pass.cpp
+++ b/libcxxabi/test/test_demangle.pass.cpp
@@ -30220,6 +30220,11 @@ const char* cases[][2] = {
     {"_ZZNH3Foo3fooES_iENK4Foo24foo2Ev", "Foo::foo(this Foo, int)::Foo2::foo2() const" },
     {"_ZNH3FooclERKS_", "Foo::operator()(this Foo const&)"},
 
+
+    // C++26 pack indexing
+    {"_Z3fooILi0ETpTnDaJLi1ELi2EEEDTsyT0_T_Ev", "decltype((1, 2...)[0]) foo<0, 1, 2>()"},
+    {"_Z1gILi0EJciEEDyT0_T_v", "(char, int)[0] g<0, char, int>()"},
+
     // fixed-point types as defined in the N1169 draft of ISO/IEC DTR 18037
     {"_Z1fDAs", "f(short _Accum)"},
     {"_Z1fDAt", "f(unsigned short _Accum)"},
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index b0363c1a7a7863..f567bfea37dbc8 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -1521,6 +1521,27 @@ class ParameterPackExpansion final : public Node {
   }
 };
 
+class PackIndexing final : public Node {
+  const Node *Pattern;
+  const Node *Index;
+
+public:
+  PackIndexing(const Node *Pattern_, const Node *Index_)
+      : Node(KPackIndexing), Pattern(Pattern_), Index(Index_) {}
+
+  template <typename Fn> void match(Fn F) const { F(Pattern, Index); }
+
+  void printLeft(OutputBuffer &OB) const override {
+    OB.printOpen('(');
+    ParameterPackExpansion PPE(Pattern);
+    PPE.printLeft(OB);
+    OB.printClose(')');
+    OB.printOpen('[');
+    Index->printLeft(OB);
+    OB.printClose(']');
+  }
+};
+
 class TemplateArgs final : public Node {
   NodeArray Params;
   Node *Requires;
@@ -4510,6 +4531,18 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() {
       Result = make<ParameterPackExpansion>(Child);
       break;
     }
+    //           ::= Dy <type> <expression> # pack indexing (C++26)
+    case 'y': {
+      First += 2;
+      Node *Pattern = getDerived().parseType();
+      if (!Pattern)
+        return nullptr;
+      Node *Index = getDerived().parseExpr();
+      if (!Index)
+        return nullptr;
+      Result = make<PackIndexing>(Pattern, Index);
+      break;
+    }
     // Exception specifier on a function type.
     case 'o':
     case 'O':
@@ -5354,6 +5387,16 @@ Node *AbstractManglingParser<Derived, Alloc>::parseExpr() {
       return nullptr;
     return make<ParameterPackExpansion>(Child);
   }
+  if (consumeIf("sy")) {
+    Node *Pattern = look() == 'T' ? getDerived().parseTemplateParam()
+                                  : getDerived().parseFunctionParam();
+    if (Pattern == nullptr)
+      return nullptr;
+    Node *Index = getDerived().parseExpr();
+    if (Index == nullptr)
+      return nullptr;
+    return make<PackIndexing>(Pattern, Index);
+  }
   if (consumeIf("sZ")) {
     if (look() == 'T') {
       Node *R = getDerived().parseTemplateParam();
diff --git a/llvm/include/llvm/Demangle/ItaniumNodes.def b/llvm/include/llvm/Demangle/ItaniumNodes.def
index 330552663ee658..3a28b1f1759247 100644
--- a/llvm/include/llvm/Demangle/ItaniumNodes.def
+++ b/llvm/include/llvm/Demangle/ItaniumNodes.def
@@ -100,5 +100,5 @@ NODE(ExprRequirement)
 NODE(TypeRequirement)
 NODE(NestedRequirement)
 NODE(ExplicitObjectParameter)
-
+NODE(PackIndexing)
 #undef NODE

Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm unfamiliar with the libcxx/LLVM demangler part, but it does look like we're duplicating something. Can anyone explain why we have to do that?

@@ -1003,6 +1003,7 @@ Bug Fixes to C++ Support
lambda functions or inline friend functions defined inside templates (#GH122493).
- Clang now rejects declaring an alias template with the same name as its template parameter. (#GH123423)
- Correctly determine the implicit constexprness of lambdas in dependent contexts. (#GH97958) (#GH114234)
- Implement Itanium mangling for pack indexing. (#GH112003)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should live in C++2c support section, as this isn't strictly a bug.

@@ -4787,6 +4788,7 @@ void CXXNameMangler::mangleRequirement(SourceLocation RequiresExprLoc,

void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
bool AsTemplateArg) {
// clang-format off
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why turning it off?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format initially completely destroy the comment below, which has been neatly aligned

Comment on lines 3 to 11
namespace spaceship {
struct X {};
struct Y {};
int operator<=>(X, Y);

// CHECK-LABEL: define {{.*}} @_ZN9spaceship1fIiEEvDTcmltcvNS_1YE_EcvNS_1XE_EcvT__EE
template<typename T> void f(decltype(Y() < X(), T()) x) {}
template void f<int>(int);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't related to what this patch proposes or am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oups

@cor3ntin
Copy link
Contributor Author

Hmm, I'm unfamiliar with the libcxx/LLVM demangler part, but it does look like we're duplicating something. Can anyone explain why we have to do that?

https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Demangle/README.txt

It's... not great ( guess how many times i modified the wrong copy?!)

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CFE changes seem fine to me. The demangler ones look correct, but I've not debugged in there for quite a while.

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense but let's make sure we cover all the added branches in the test suite.

@@ -5828,6 +5822,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
break;
}

case Expr::PackIndexingExprClass: {
auto *PE = cast<PackIndexingExpr>(E);
NotPrimaryExpr();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am looking at the test cases and I don't see the X, can we make sure we cover that branch in NotPrimaryExpr e.g. if (AsTemplateArg && IsPrimaryExpr). We should make sure we cover that in the demangling as well.

@@ -30220,6 +30220,11 @@ const char* cases[][2] = {
{"_ZZNH3Foo3fooES_iENK4Foo24foo2Ev", "Foo::foo(this Foo, int)::Foo2::foo2() const" },
{"_ZNH3FooclERKS_", "Foo::operator()(this Foo const&)"},


// C++26 pack indexing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add invalid cases as well see invalid_cases and make sure we cover each case that can return a nullptr in AbstractManglingParser (I think)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category libc++abi libc++abi C++ Runtime Library. Not libc++.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants