[clang] [lldb] [llvm] [WIP][lldb][Expression] More reliable function call resolution (PR #114529)

Michael Buch via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 1 04:26:38 PDT 2024


https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/114529

>From 7f18d96b7117e83c4ed246ac498ee4c9a72064ff Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Sun, 20 Oct 2024 11:35:15 +0100
Subject: [PATCH] [WIP][lldb][Expression] More reliable function call
 resolution

Implements all the parts of following RFC:
https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816

Main changes:
1. Instead of relying on linkage names to resolve function symbols,
   encode the exact function DIE and module in the `AsmLabelAttr`
2. Teach the LLDB symbol resolve about (1)
3. Introduce new Clang attribute to allow specifying multiple `asm`
   labels for ctors/dtors (one for each variant)
4. Attach the new attribute in (3), where the mangled names use the
   format from (1). To determine which variant a DIE corresponds to we
add a new API to the `ItaniumPartialDemangler` (though could be made
into a DWARF attribute for quicker determination).
---
 clang/include/clang/Basic/Attr.td             |  8 ++
 clang/include/clang/Basic/AttrDocs.td         |  5 ++
 clang/lib/AST/Mangle.cpp                      | 65 ++++++++++++++-
 clang/lib/Sema/SemaDeclAttr.cpp               | 22 +++++
 lldb/source/Expression/IRExecutionUnit.cpp    | 36 +++++++++
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  | 80 +++++++++++++++----
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 15 +++-
 .../TypeSystem/Clang/TypeSystemClang.h        |  3 +-
 lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp | 28 +++++++
 llvm/include/llvm/Demangle/Demangle.h         |  3 +
 llvm/include/llvm/Demangle/ItaniumDemangle.h  |  2 +
 llvm/lib/Demangle/ItaniumDemangle.cpp         | 18 +++--
 12 files changed, 260 insertions(+), 25 deletions(-)
 create mode 100644 lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 70fad60d4edbb5..407eece2a728a2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -784,6 +784,14 @@ def AbiTag : Attr {
   let Documentation = [AbiTagsDocs];
 }
 
+def StructorMangledNames : Attr {
+  let Spellings = [Clang<"structor_names">];
+  let Args = [VariadicStringArgument<"MangledNames">];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let Documentation = [StructorMangledNamesDocs];
+}
+
+
 def AddressSpace : TypeAttr {
   let Spellings = [Clang<"address_space">];
   let Args = [IntArgument<"AddressSpace">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 546e5100b79dd9..2b886aecd193de 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3568,6 +3568,11 @@ manipulating bits of the enumerator when issuing warnings.
   }];
 }
 
+def StructorMangledNamesDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{ TODO }];
+}
+
 def AsmLabelDocs : Documentation {
   let Category = DocCatDecl;
   let Content = [{
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 4875e8537b3c11..c784fca053062c 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -9,19 +9,20 @@
 // Implements generic name mangling support for blocks and Objective-C.
 //
 //===----------------------------------------------------------------------===//
-#include "clang/AST/Attr.h"
+#include "clang/AST/Mangle.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ExprCXX.h"
-#include "clang/AST/Mangle.h"
 #include "clang/AST/VTableBuilder.h"
 #include "clang/Basic/ABI.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -126,7 +127,7 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
 
   // Any decl can be declared with __asm("foo") on it, and this takes precedence
   // over all other naming in the .o file.
-  if (D->hasAttr<AsmLabelAttr>())
+  if (D->hasAttr<AsmLabelAttr>() || D->hasAttr<StructorMangledNamesAttr>())
     return true;
 
   // Declarations that don't have identifier names always need to be mangled.
@@ -140,6 +141,64 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
   const ASTContext &ASTContext = getASTContext();
   const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
 
+  if (const StructorMangledNamesAttr *SMA =
+          D->getAttr<StructorMangledNamesAttr>()) {
+    CXXConstructorDecl const *Ctor = dyn_cast<CXXConstructorDecl>(D);
+    CXXDestructorDecl const *Dtor = dyn_cast<CXXDestructorDecl>(D);
+    assert(Ctor || Dtor);
+    enum CtorDtor {
+      None = -1,
+      Deleting = 0,
+      Base,
+      Complete,
+      Allocating
+    } CtorDtorVariant = None;
+
+    if (Dtor) {
+      switch (GD.getDtorType()) {
+      case Dtor_Complete:
+        CtorDtorVariant = Complete;
+        break;
+      case Dtor_Base:
+        CtorDtorVariant = Base;
+        break;
+      case Dtor_Deleting:
+      case Dtor_Comdat:
+        llvm_unreachable("");
+      }
+    } else if (Ctor) {
+      switch (GD.getCtorType()) {
+      case Ctor_Complete:
+        CtorDtorVariant = Complete;
+        break;
+      case Ctor_Base:
+        CtorDtorVariant = Base;
+        break;
+      case Ctor_DefaultClosure:
+      case Ctor_CopyingClosure:
+      case Ctor_Comdat:
+        llvm_unreachable("");
+      }
+    }
+
+    llvm::DenseMap<CtorDtor, llvm::StringRef> names;
+    for (auto name : SMA->mangledNames()) {
+      auto [structor_variant, mangled_name] = name.split(':');
+      auto variant = llvm::StringSwitch<CtorDtor>(structor_variant)
+                         .Case("0", CtorDtor::Deleting)
+                         .Case("1", CtorDtor::Base)
+                         .Case("2", CtorDtor::Complete)
+                         .Case("3", CtorDtor::Complete)
+                         .Default(CtorDtor::None);
+      names[variant] = mangled_name;
+    }
+
+    assert(CtorDtorVariant != CtorDtor::None);
+
+    Out << names[CtorDtorVariant];
+    return;
+  }
+
   // Any decl can be declared with __asm("foo") on it, and this takes precedence
   // over all other naming in the .o file.
   if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 14cc51cf89665a..fdf66be8ac3987 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5539,6 +5539,25 @@ static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL));
 }
 
+static void handleStructorNamesAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  SmallVector<StringRef, 4> Tags;
+  for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
+    StringRef Tag;
+    if (!S.checkStringLiteralArgumentAttr(AL, I, Tag))
+      return;
+    Tags.push_back(Tag);
+  }
+
+  if (!AL.checkAtLeastNumArgs(S, 1))
+    return;
+
+  // Store tags without duplicates.
+  Tags.erase(std::unique(Tags.begin(), Tags.end()), Tags.end());
+
+  D->addAttr(::new (S.Context) StructorMangledNamesAttr(
+      S.Context, AL, Tags.data(), Tags.size()));
+}
+
 static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   SmallVector<StringRef, 4> Tags;
   for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
@@ -6983,6 +7002,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
     S.HLSL().handleParamModifierAttr(D, AL);
     break;
 
+  case ParsedAttr::AT_StructorMangledNames:
+    handleStructorNamesAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_AbiTag:
     handleAbiTagAttr(S, D, AL);
     break;
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 15ca2ddbbae046..b11085d921c419 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -16,6 +16,8 @@
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/raw_ostream.h"
 
+#include "Plugins/SymbolFile/DWARF/DWARFBaseDIE.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Disassembler.h"
 #include "lldb/Core/Module.h"
@@ -37,6 +39,8 @@
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 
+#include <cstddef>
+#include <cstdint>
 #include <optional>
 
 using namespace lldb_private;
@@ -781,6 +785,38 @@ IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
   function_options.include_inlines = false;
 
   for (const ConstString &name : names) {
+    auto ref = name.GetStringRef();
+    if (ref.consume_front("$__lldb_func_")) {
+      uintptr_t module_ptr;
+      if (ref.consumeInteger(0, module_ptr))
+        return LLDB_INVALID_ADDRESS;
+
+      auto *mod = (lldb_private::Module *)module_ptr;
+      auto *sym = mod->GetSymbolFile();
+      assert(mod && sym);
+
+      if (!ref.consume_front(":"))
+        return LLDB_INVALID_ADDRESS;
+
+      lldb::user_id_t die_id;
+      if (ref.consumeInteger(10, die_id))
+        return LLDB_INVALID_ADDRESS;
+
+      auto *dwarf = llvm::dyn_cast<plugin::dwarf::SymbolFileDWARF>(sym);
+      if (!dwarf)
+        return LLDB_INVALID_ADDRESS;
+
+      auto die = dwarf->GetDIE(die_id);
+      Module::LookupInfo lookup_info(
+          ConstString(die.GetMangledName()),
+          lldb::FunctionNameType::eFunctionNameTypeAny,
+          lldb::LanguageType::eLanguageTypeC_plus_plus);
+      SymbolContextList sc_list;
+      dwarf->FindFunctions(lookup_info, {}, false, sc_list);
+      if (auto load_addr = resolver.Resolve(sc_list))
+        return *load_addr;
+    }
+
     if (sc.module_sp) {
       SymbolContextList sc_list;
       sc.module_sp->FindFunctions(name, CompilerDeclContext(),
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 70540fe7fada68..ea86ef352800ec 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -45,6 +45,7 @@
 #include "clang/AST/Type.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/FormatVariadic.h"
 
 #include <map>
 #include <memory>
@@ -1040,6 +1041,62 @@ bool DWARFASTParserClang::ParseObjCMethod(
   return true;
 }
 
+static bool IsStructorDIE(DWARFDIE const &die, DWARFDIE const &parent_die) {
+  llvm::StringRef name = die.GetName();
+  llvm::StringRef parent_name = parent_die.GetName();
+
+  name.consume_front("~");
+  parent_name = parent_name.substr(0, parent_name.find('<'));
+
+  return name == parent_name;
+}
+
+/// Given a DIE with an external definition (and thus no linkage name)
+/// find the definitions by lookup into the DWARF name index.
+/// We check the DW_AT_specification for each DIE in the index with
+/// the same name as the specified 'die' until we find one that references
+/// 'die'. Then return that linkage name. If no such DIE is found in the index,
+/// returns nullptr.
+static std::vector<std::string> FindStructorNames(DWARFDIE die) {
+  auto *dwarf = die.GetDWARF();
+  assert(dwarf);
+
+  ConstString func_name(die.GetName());
+  assert(func_name);
+
+  SymbolContextList sc_list;
+  Module::LookupInfo lookup_info(func_name,
+                                 FunctionNameType::eFunctionNameTypeMethod |
+                                     FunctionNameType::eFunctionNameTypeFull,
+                                 LanguageType::eLanguageTypeUnknown);
+  dwarf->FindFunctions(lookup_info, {}, true, sc_list);
+
+  std::vector<std::string> structor_names;
+  for (auto const &sc : sc_list.SymbolContexts()) {
+    if (auto *func = sc.function) {
+      auto func_die = dwarf->GetDIE(func->GetID());
+      if (!func_die.IsValid())
+        continue;
+
+      auto spec_die =
+          func_die.GetAttributeValueAsReferenceDIE(DW_AT_specification);
+      if (spec_die.IsValid() && spec_die == die) {
+        llvm::ItaniumPartialDemangler D;
+        const bool success = !D.partialDemangle(func_die.GetMangledName());
+        assert(success);
+        const auto maybe_structor_kind = D.getCtorDtorVariant();
+        assert(maybe_structor_kind);
+
+        structor_names.push_back(
+            llvm::formatv("{0}:$__lldb_func_{1}:{2}", *maybe_structor_kind,
+                          func_die.GetModule().get(), func_die.GetID()));
+      }
+    }
+  }
+
+  return structor_names;
+}
+
 std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod(
     const DWARFDIE &die, CompilerType clang_type,
     const ParsedDWARFTypeAttributes &attrs, const DWARFDIE &decl_ctx_die,
@@ -1140,11 +1197,15 @@ std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod(
   const auto accessibility =
       attrs.accessibility == eAccessNone ? eAccessPublic : attrs.accessibility;
 
+  std::vector<std::string> structor_names;
+  if (IsStructorDIE(die, decl_ctx_die))
+    structor_names = FindStructorNames(die);
+
   clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType(
       class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(),
       attrs.mangled_name, clang_type, accessibility, attrs.is_virtual,
       is_static, attrs.is_inline, attrs.is_explicit, is_attr_used,
-      attrs.is_artificial);
+      attrs.is_artificial, structor_names);
 
   if (cxx_method_decl) {
     LinkDeclContextToDIE(cxx_method_decl, die);
@@ -1330,19 +1391,10 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
         lldbassert(function_decl);
 
         if (function_decl) {
-          // Attach an asm(<mangled_name>) label to the FunctionDecl.
-          // This ensures that clang::CodeGen emits function calls
-          // using symbols that are mangled according to the DW_AT_linkage_name.
-          // If we didn't do this, the external symbols wouldn't exactly
-          // match the mangled name LLDB knows about and the IRExecutionUnit
-          // would have to fall back to searching object files for
-          // approximately matching function names. The motivating
-          // example is generating calls to ABI-tagged template functions.
-          // This is done separately for member functions in
-          // AddMethodToCXXRecordType.
-          if (attrs.mangled_name)
-            function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
-                m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false));
+          auto ident = llvm::formatv("$__lldb_func_{0}:{1}",
+                                     die.GetModule().get(), die.GetID());
+          function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
+              m_ast.getASTContext(), ident.str(), /*literal=*/false));
 
           LinkDeclContextToDIE(function_decl, die);
 
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index b0f49ebf2d2cbb..549b810490a590 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -10,6 +10,7 @@
 
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/FormatAdapters.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -7735,7 +7736,8 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
     lldb::opaque_compiler_type_t type, llvm::StringRef name,
     const char *mangled_name, const CompilerType &method_clang_type,
     lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline,
-    bool is_explicit, bool is_attr_used, bool is_artificial) {
+    bool is_explicit, bool is_attr_used, bool is_artificial,
+    std::vector<std::string> const &structor_names) {
   if (!type || !method_clang_type.IsValid() || name.empty())
     return nullptr;
 
@@ -7788,6 +7790,11 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
     cxx_dtor_decl->setImplicit(is_artificial);
     cxx_dtor_decl->setInlineSpecified(is_inline);
     cxx_dtor_decl->setConstexprKind(ConstexprSpecKind::Unspecified);
+    if (!structor_names.empty()) {
+      auto names = llvm::to_vector_of<llvm::StringRef>(structor_names);
+      cxx_dtor_decl->addAttr(clang::StructorMangledNamesAttr::CreateImplicit(
+          getASTContext(), names.data(), names.size()));
+    }
     cxx_method_decl = cxx_dtor_decl;
   } else if (decl_name == cxx_record_decl->getDeclName()) {
     cxx_ctor_decl = clang::CXXConstructorDecl::CreateDeserialized(
@@ -7802,6 +7809,12 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
     cxx_ctor_decl->setConstexprKind(ConstexprSpecKind::Unspecified);
     cxx_ctor_decl->setNumCtorInitializers(0);
     cxx_ctor_decl->setExplicitSpecifier(explicit_spec);
+    if (!structor_names.empty()) {
+      auto names = llvm::to_vector_of<llvm::StringRef>(structor_names);
+      cxx_ctor_decl->addAttr(clang::StructorMangledNamesAttr::CreateImplicit(
+          getASTContext(), names.data(), names.size()));
+    }
+
     cxx_method_decl = cxx_ctor_decl;
   } else {
     clang::StorageClass SC = is_static ? clang::SC_Static : clang::SC_None;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index e39aedec7e3902..f5ab4dde8ed58c 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -980,7 +980,8 @@ class TypeSystemClang : public TypeSystem {
       lldb::opaque_compiler_type_t type, llvm::StringRef name,
       const char *mangled_name, const CompilerType &method_type,
       lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline,
-      bool is_explicit, bool is_attr_used, bool is_artificial);
+      bool is_explicit, bool is_attr_used, bool is_artificial,
+      std::vector<std::string> const &structor_names = {});
 
   void AddMethodOverridesForCXXRecordType(lldb::opaque_compiler_type_t type);
 
diff --git a/lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp b/lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp
new file mode 100644
index 00000000000000..49de90ee64dea6
--- /dev/null
+++ b/lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp
@@ -0,0 +1,28 @@
+// Tests that we can call abi-tagged constructors/destructors.
+
+// RUN: %build %s -o %t
+// RUN: %lldb %t -o run \
+// RUN:          -o "expression sinkTagged(getTagged())" \
+// RUN:          -o "expression Tagged()" \
+// RUN:          -o exit | FileCheck %s
+
+// CHECK: expression sinkTagged(getTagged())
+// CHECK: expression Tagged()
+
+struct Tagged {
+  [[gnu::abi_tag("Test", "CtorTag")]] [[gnu::abi_tag("v1")]] Tagged() = default;
+  [[gnu::abi_tag("Test", "DtorTag")]] [[gnu::abi_tag("v1")]] ~Tagged() {}
+
+  int mem = 15;
+};
+
+Tagged getTagged() { return Tagged(); }
+void sinkTagged(Tagged t) {}
+
+int main() {
+  Tagged t;
+
+  // TODO: is there a more reliable way of triggering destructor call?
+  sinkTagged(getTagged());
+  __builtin_debugtrap();
+}
diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h
index fe129603c0785d..103b60d6c343ce 100644
--- a/llvm/include/llvm/Demangle/Demangle.h
+++ b/llvm/include/llvm/Demangle/Demangle.h
@@ -10,6 +10,7 @@
 #define LLVM_DEMANGLE_DEMANGLE_H
 
 #include <cstddef>
+#include <optional>
 #include <string>
 #include <string_view>
 
@@ -108,6 +109,8 @@ struct ItaniumPartialDemangler {
   /// the function is a non-static member function.
   bool hasFunctionQualifiers() const;
 
+  std::optional<int> getCtorDtorVariant() const;
+
   /// If this symbol describes a constructor or destructor.
   bool isCtorOrDtor() const;
 
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 0af0224bc83fa8..39389132430ab0 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -1728,6 +1728,8 @@ class CtorDtorName final : public Node {
 
   template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }
 
+  int getVariant() const { return Variant; }
+
   void printLeft(OutputBuffer &OB) const override {
     if (IsDtor)
       OB += "~";
diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp
index 5c21b06a1d0955..786985a141b022 100644
--- a/llvm/lib/Demangle/ItaniumDemangle.cpp
+++ b/llvm/lib/Demangle/ItaniumDemangle.cpp
@@ -20,6 +20,7 @@
 #include <cstring>
 #include <exception>
 #include <functional>
+#include <optional>
 #include <utility>
 
 using namespace llvm;
@@ -548,15 +549,16 @@ bool ItaniumPartialDemangler::hasFunctionQualifiers() const {
   return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;
 }
 
-bool ItaniumPartialDemangler::isCtorOrDtor() const {
+std::optional<int> ItaniumPartialDemangler::getCtorDtorVariant() const {
   const Node *N = static_cast<const Node *>(RootNode);
   while (N) {
     switch (N->getKind()) {
     default:
-      return false;
-    case Node::KCtorDtorName:
-      return true;
-
+      return std::nullopt;
+    case Node::KCtorDtorName: {
+      auto const *StructorNode = static_cast<const CtorDtorName *>(N);
+      return StructorNode->getVariant();
+    }
     case Node::KAbiTagAttr:
       N = static_cast<const AbiTagAttr *>(N)->Base;
       break;
@@ -577,7 +579,11 @@ bool ItaniumPartialDemangler::isCtorOrDtor() const {
       break;
     }
   }
-  return false;
+  return std::nullopt;
+}
+
+bool ItaniumPartialDemangler::isCtorOrDtor() const {
+  return getCtorDtorVariant().has_value();
 }
 
 bool ItaniumPartialDemangler::isFunction() const {



More information about the cfe-commits mailing list