[Lldb-commits] [lldb] 57a7907 - [lldb][Expression] Add structor variant to LLDB's function call labels (#149827)

via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 9 02:02:05 PDT 2025


Author: Michael Buch
Date: 2025-09-09T09:02:00Z
New Revision: 57a790717937af52db8c97d3666dc76487bd4226

URL: https://github.com/llvm/llvm-project/commit/57a790717937af52db8c97d3666dc76487bd4226
DIFF: https://github.com/llvm/llvm-project/commit/57a790717937af52db8c97d3666dc76487bd4226.diff

LOG: [lldb][Expression] Add structor variant to LLDB's function call labels (#149827)

Depends on
* https://github.com/llvm/llvm-project/pull/148877
* https://github.com/llvm/llvm-project/pull/155483
* https://github.com/llvm/llvm-project/pull/155485
* https://github.com/llvm/llvm-project/pull/154137
* https://github.com/llvm/llvm-project/pull/154142

This patch is an implementation of [this
discussion](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7)
about handling ABI-tagged structors during expression evaluation.

**Motivation**

LLDB encodes the mangled name of a `DW_TAG_subprogram` into `AsmLabel`s
on function and method Clang AST nodes. This means that when calls to
these functions get lowered into IR (when running JITted expressions),
the address resolver can locate the appropriate symbol by mangled name
(and it is guaranteed to find the symbol because we got the mangled name
from debug-info, instead of letting Clang mangle it based on AST
structure). However, we don't do this for
`CXXConstructorDecl`s/`CXXDestructorDecl`s because these structor
declarations in DWARF don't have a linkage name. This is because there
can be multiple variants of a structor, each with a distinct mangling in
the Itanium ABI. Each structor variant has its own definition
`DW_TAG_subprogram`. So LLDB doesn't know which mangled name to put into
the `AsmLabel`.

Currently this means using ABI-tagged structors in LLDB expressions
won't work (see [this
RFC](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816)
for concrete examples).

**Proposed Solution**

The `FunctionCallLabel` encoding that we put into `AsmLabel`s already
supports stuffing more info about a DIE into it. So this patch extends
the `FunctionCallLabel` to contain an optional discriminator (a sequence
of bytes) which the `SymbolFileDWARF` plugin interprets as the
constructor/destructor variant of that DIE. So when searching for the
definition DIE, LLDB will include the structor variant in its heuristic
for determining a match.

There's a few subtleties here:
1. At the point at which LLDB first constructs the label, it has no way
of knowing (just by looking at the debug-info declaration), which
structor variant the expression evaluator is supposed to call. That's
something that gets decided when compiling the expression. So we let the
Clang mangler inject the correct structor variant into the `AsmLabel`
during JITing. I adjusted the `AsmLabelAttr` mangling for this in
https://github.com/llvm/llvm-project/pull/155485. An option would've
been to create a new Clang attribute which behaved like an `AsmLabel`
but with these special semantics for LLDB. My main concern there is that
we'd have to adjust all the `AsmLabelAttr` checks around Clang to also
now account for this new attribute.
2. The compiler is free to omit the `C1` variant of a constructor if the
`C2` variant is sufficient. In that case it may alias `C1` to `C2`,
leaving us with only the `C2` `DW_TAG_subprogram` in the object file.
Linux is one of the platforms where this occurs. For those cases I added
a heuristic in `SymbolFileDWARF` where we pick `C2` if we asked for `C1`
but it doesn't exist. This may not always be correct (e.g., if the
compiler decided to drop `C1` for other reasons).
3. In https://github.com/llvm/llvm-project/pull/154142 Clang will emit
`C4`/`D4` variants of ctors/dtors on declarations. When resolving the
`FunctionCallLabel` we will now substitute the actual variant that Clang
told us we need to call into the mangled name. We do this using LLDB's
`ManglingSubstitutor`. That way we find the definition DIE exactly the
same way we do for regular function calls.
4. In cases where declarations and definitions live in separate modules,
the DIE ID encoded in the function call label may not be enough to find
the definition DIE in the encoded module ID. For those cases we fall
back to how LLDB used to work: look up in all images of the target. To
make sure we don't use the unified mangled name for the fallback lookup,
we change the lookup name to whatever mangled name the FunctionCallLabel
resolved to.

rdar://104968288

Added: 
    lldb/test/API/lang/cpp/abi_tag_structors/Makefile
    lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
    lldb/test/API/lang/cpp/abi_tag_structors/main.cpp

Modified: 
    lldb/include/lldb/Expression/Expression.h
    lldb/include/lldb/Symbol/SymbolFile.h
    lldb/source/Expression/Expression.cpp
    lldb/source/Expression/IRExecutionUnit.cpp
    lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
    lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
    lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
    lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
    lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
    lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
    lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
    lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
    lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
    lldb/unittests/Expression/ExpressionTest.cpp
    lldb/unittests/Symbol/TestTypeSystemClang.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h
index 20067f469895b..847226167d584 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -103,11 +103,15 @@ class Expression {
 ///
 /// The format being:
 ///
-///   <prefix>:<module uid>:<symbol uid>:<name>
+///   <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
 ///
 /// The label string needs to stay valid for the entire lifetime
 /// of this object.
 struct FunctionCallLabel {
+  /// Arbitrary string which language plugins can interpret for their
+  /// own needs.
+  llvm::StringRef discriminator;
+
   /// Unique identifier of the lldb_private::Module
   /// which contains the symbol identified by \c symbol_id.
   lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
   ///
   /// The representation roundtrips through \c fromString:
   /// \code{.cpp}
-  /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
+  /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
   /// FunctionCallLabel label = *fromString(label);
   ///
   /// assert (label.toString() == encoded);

diff  --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index bbc615d9fdc38..a84f081b340eb 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -332,12 +332,12 @@ class SymbolFile : public PluginInterface {
   /// Resolves the function corresponding to the specified LLDB function
   /// call \c label.
   ///
-  /// \param[in] label The FunctionCallLabel to be resolved.
+  /// \param[in,out] label The FunctionCallLabel to be resolved.
   ///
   /// \returns An llvm::Error if the specified \c label couldn't be resolved.
   ///          Returns the resolved function (as a SymbolContext) otherwise.
   virtual llvm::Expected<SymbolContext>
-  ResolveFunctionCallLabel(const FunctionCallLabel &label) {
+  ResolveFunctionCallLabel(FunctionCallLabel &label) {
     return llvm::createStringError("Not implemented");
   }
 

diff  --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp
index 796851ff15ca3..16ecb1d7deef8 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope)
 
 llvm::Expected<FunctionCallLabel>
 lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
-  llvm::SmallVector<llvm::StringRef, 4> components;
-  label.split(components, ":", /*MaxSplit=*/3);
+  llvm::SmallVector<llvm::StringRef, 5> components;
+  label.split(components, ":", /*MaxSplit=*/4);
 
-  if (components.size() != 4)
+  if (components.size() != 5)
     return llvm::createStringError("malformed function call label.");
 
   if (components[0] != FunctionCallLabelPrefix)
@@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
         "expected function call label prefix '{0}' but found '{1}' instead.",
         FunctionCallLabelPrefix, components[0]));
 
-  llvm::StringRef module_label = components[1];
-  llvm::StringRef die_label = components[2];
+  llvm::StringRef discriminator = components[1];
+  llvm::StringRef module_label = components[2];
+  llvm::StringRef die_label = components[3];
+  llvm::StringRef lookup_name = components[4];
 
   lldb::user_id_t module_id = 0;
   if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
     return llvm::createStringError(
         llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
 
-  return FunctionCallLabel{/*.module_id=*/module_id,
+  return FunctionCallLabel{/*.discriminator=*/discriminator,
+                           /*.module_id=*/module_id,
                            /*.symbol_id=*/die_id,
-                           /*.lookup_name=*/components[3]};
+                           /*.lookup_name=*/lookup_name};
 }
 
 std::string lldb_private::FunctionCallLabel::toString() const {
-  return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
-                       module_id, symbol_id, lookup_name)
+  return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
+                       discriminator, module_id, symbol_id, lookup_name)
       .str();
 }
 
 void llvm::format_provider<FunctionCallLabel>::format(
     const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
-  OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
-                      "lookup_name: {2} }",
-                      label.module_id, label.symbol_id, label.lookup_name);
+  OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
+                      "{1:x}, symbol_id: {2:x}, "
+                      "lookup_name: {3} }",
+                      label.discriminator, label.module_id, label.symbol_id,
+                      label.lookup_name);
 }

diff  --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 998f03b098562..25d4a87b89ef2 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -723,7 +723,7 @@ class LoadAddressResolver {
 /// Returns address of the function referred to by the special function call
 /// label \c label.
 static llvm::Expected<lldb::addr_t>
-ResolveFunctionCallLabel(const FunctionCallLabel &label,
+ResolveFunctionCallLabel(FunctionCallLabel &label,
                          const lldb_private::SymbolContext &sc,
                          bool &symbol_was_missing_weak) {
   symbol_was_missing_weak = false;

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 0f1f41baced86..5ffb4423969ca 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -252,7 +252,7 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram,
 }
 
 static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
-  char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+  const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
   if (!name)
     return {};
 
@@ -286,7 +286,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
   if (die_id == LLDB_INVALID_UID)
     return {};
 
-  return FunctionCallLabel{/*module_id=*/module_id,
+  // Note, discriminator is added by Clang during mangling.
+  return FunctionCallLabel{/*discriminator=*/{},
+                           /*module_id=*/module_id,
                            /*symbol_id=*/die_id,
                            /*.lookup_name=*/name}
       .toString();

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index b15e0c15fedb8..baadf01428ae8 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -7,11 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "SymbolFileDWARF.h"
+#include "clang/Basic/ABI.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/FormatAdapters.h"
 #include "llvm/Support/Threading.h"
@@ -23,6 +26,7 @@
 #include "lldb/Core/Progress.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Core/Value.h"
+#include "lldb/Expression/Expression.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/RegularExpression.h"
@@ -79,6 +83,7 @@
 
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
+#include "llvm/Demangle/Demangle.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"
 
@@ -2484,34 +2489,148 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
   return false;
 }
 
-DWARFDIE
-SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) {
-  DWARFDIE definition;
-  Module::LookupInfo info(ConstString(label.lookup_name),
-                          lldb::eFunctionNameTypeFull,
-                          lldb::eLanguageTypeUnknown);
-
-  m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
-    if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
-      return IterationAction::Continue;
+static llvm::StringRef ClangToItaniumCtorKind(clang::CXXCtorType kind) {
+  switch (kind) {
+  case clang::CXXCtorType::Ctor_Complete:
+    return "C1";
+  case clang::CXXCtorType::Ctor_Base:
+    return "C2";
+  case clang::CXXCtorType::Ctor_Unified:
+    return "C4";
+  case clang::CXXCtorType::Ctor_CopyingClosure:
+  case clang::CXXCtorType::Ctor_DefaultClosure:
+  case clang::CXXCtorType::Ctor_Comdat:
+    llvm_unreachable("Unexpected constructor kind.");
+  }
+}
 
-    // We don't check whether the specification DIE for this function
-    // corresponds to the declaration DIE because the declaration might be in
-    // a type-unit but the definition in the compile-unit (and it's
-    // specifcation would point to the declaration in the compile-unit). We
-    // rely on the mangled name within the module to be enough to find us the
-    // unique definition.
-    definition = entry;
-    return IterationAction::Stop;
-  });
+static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) {
+  switch (kind) {
+  case clang::CXXDtorType::Dtor_Deleting:
+    return "D0";
+  case clang::CXXDtorType::Dtor_Complete:
+    return "D1";
+  case clang::CXXDtorType::Dtor_Base:
+    return "D2";
+  case clang::CXXDtorType::Dtor_Unified:
+    return "D4";
+  case clang::CXXDtorType::Dtor_Comdat:
+    llvm_unreachable("Unexpected destructor kind.");
+  }
+}
+
+static llvm::StringRef
+GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
+  const bool is_ctor = discriminator.consume_front("C");
+  if (!is_ctor && !discriminator.consume_front("D"))
+    return {};
+
+  uint64_t structor_kind;
+  if (!llvm::to_integer(discriminator, structor_kind))
+    return {};
+
+  if (is_ctor) {
+    if (structor_kind > clang::CXXCtorType::Ctor_Unified)
+      return {};
+
+    return ClangToItaniumCtorKind(
+        static_cast<clang::CXXCtorType>(structor_kind));
+  }
+
+  if (structor_kind > clang::CXXDtorType::Dtor_Unified)
+    return {};
+
+  return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
+}
+
+llvm::Expected<DWARFDIE>
+SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
+                                        const DWARFDIE &declaration) {
+  auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE {
+    DWARFDIE found;
+    Module::LookupInfo info(ConstString(lookup_name),
+                            lldb::eFunctionNameTypeFull,
+                            lldb::eLanguageTypeUnknown);
+
+    m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+      if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
+        return IterationAction::Continue;
+
+      found = entry;
+      return IterationAction::Stop;
+    });
+
+    return found;
+  };
+
+  DWARFDIE definition = do_lookup(label.lookup_name);
+  if (definition.IsValid())
+    return definition;
+
+  // This is not a structor lookup. Nothing else to be done here.
+  if (label.discriminator.empty())
+    return llvm::createStringError(
+        "no definition DIE found in this SymbolFile");
+
+  // We're doing a structor lookup. Maybe we didn't find the structor variant
+  // because the complete object structor was aliased to the base object
+  // structor. Try finding the alias instead.
+  //
+  // TODO: there are other reasons for why a subprogram definition might be
+  // missing. Ideally DWARF would tell us more details about which structor
+  // variant a DIE corresponds to and whether it's an alias.
+  auto subst_or_err =
+      CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+          label.lookup_name);
+  if (!subst_or_err)
+    return subst_or_err.takeError();
+
+  definition = do_lookup(*subst_or_err);
+
+  if (!definition.IsValid())
+    return llvm::createStringError(
+        "failed to find definition DIE for structor alias in fallback lookup");
 
   return definition;
 }
 
 llvm::Expected<SymbolContext>
-SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
+SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
 
+  if (!label.discriminator.empty()) {
+    llvm::StringRef from = label.discriminator[0] == 'C' ? "C4" : "D4";
+
+    llvm::StringRef variant = GetItaniumCtorDtorVariant(label.discriminator);
+    if (variant.empty())
+      return llvm::createStringError(
+          "failed to get Itanium variant for discriminator");
+
+    if (from == variant)
+      return llvm::createStringError(
+          "tried substituting unified structor variant into label");
+
+    // If we failed to substitute unified mangled name, don't try to do a lookup
+    // using the unified name because there may be multiple definitions for it
+    // in the index, and we wouldn't know which one to choose.
+    auto subst_or_err = CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(
+        label.lookup_name, from, variant);
+    if (!subst_or_err)
+      return llvm::joinErrors(
+          llvm::createStringError(llvm::formatv(
+              "failed to substitute {0} for {1} in mangled name {2}:", from,
+              variant, label.lookup_name)),
+          subst_or_err.takeError());
+
+    if (!*subst_or_err)
+      return llvm::createStringError(
+          llvm::formatv("got invalid substituted mangled named (substituted "
+                        "{0} for {1} in mangled name {2})",
+                        from, variant, label.lookup_name));
+
+    label.lookup_name = subst_or_err->GetStringRef();
+  }
+
   DWARFDIE die = GetDIE(label.symbol_id);
   if (!die.IsValid())
     return llvm::createStringError(
@@ -2520,11 +2639,13 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
   // Label was created using a declaration DIE. Need to fetch the definition
   // to resolve the function call.
   if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
-    auto definition = FindFunctionDefinition(label);
-    if (!definition)
-      return llvm::createStringError("failed to find definition DIE");
+    auto die_or_err = FindFunctionDefinition(label, die);
+    if (!die_or_err)
+      return llvm::joinErrors(
+          llvm::createStringError("failed to find definition DIE:"),
+          die_or_err.takeError());
 
-    die = std::move(definition);
+    die = std::move(*die_or_err);
   }
 
   SymbolContextList sc_list;

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index d7db8a3c0869f..608130cd068ce 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -378,7 +378,9 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// SymbolFile.
   ///
   /// \returns A valid definition DIE on success.
-  DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label);
+  llvm::Expected<DWARFDIE>
+  FindFunctionDefinition(const FunctionCallLabel &label,
+                         const DWARFDIE &declaration);
 
 protected:
   SymbolFileDWARF(const SymbolFileDWARF &) = delete;
@@ -445,7 +447,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
                                         DIEArray &&variable_dies);
 
   llvm::Expected<SymbolContext>
-  ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
+  ResolveFunctionCallLabel(FunctionCallLabel &label) override;
 
   // Given a die_offset, figure out the symbol context representing that die.
   bool ResolveFunction(const DWARFDIE &die, bool include_inlines,

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 9d7452a1988fa..8b8229a7020c5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1603,8 +1603,8 @@ void SymbolFileDWARFDebugMap::GetCompileOptions(
   });
 }
 
-llvm::Expected<SymbolContext> SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(
-    const FunctionCallLabel &label) {
+llvm::Expected<SymbolContext>
+SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) {
   const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id);
   SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
   if (!oso_dwarf)

diff  --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index e1f1df23951c6..bce1ed2671af0 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -145,7 +145,7 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
 
   llvm::Expected<SymbolContext>
-  ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
+  ResolveFunctionCallLabel(FunctionCallLabel &label) override;
 
 protected:
   enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };

diff  --git a/lldb/test/API/lang/cpp/abi_tag_structors/Makefile b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
new file mode 100644
index 0000000000000..7a5a8edfea638
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
@@ -0,0 +1,112 @@
+"""
+Test that we can call structors/destructors
+annotated (and thus mangled) with ABI tags.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AbiTagStructorsTestCase(TestBase):
+    def test_with_structor_linkage_names(self):
+        self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
+
+        lldbutil.run_to_source_breakpoint(
+            self, "Break here", lldb.SBFileSpec("main.cpp", False)
+        )
+
+        self.expect_expr(
+            "Tagged()",
+            result_type="Tagged",
+            result_children=[ValueCheck(name="x", value="15")],
+        )
+        self.expect_expr(
+            "Tagged(-17)",
+            result_type="Tagged",
+            result_children=[ValueCheck(name="x", value="-17")],
+        )
+        self.expect_expr("t1 = t2", result_type="Tagged")
+
+        self.expect("expr Tagged t3(t1)", error=False)
+        self.expect("expr t1.~Tagged()", error=False)
+
+        self.expect("expr t1.~Tagged()", error=False)
+
+        self.expect(
+            "expression -- struct $Derived : virtual public Tagged { int y; $Derived(int val) : Tagged(val) { y = x; } };",
+            error=False,
+        )
+        self.expect(
+            "expression -- struct $Derived2 : virtual public $Derived { int z; $Derived2() : $Derived(10) { z = y; } };",
+            error=False,
+        )
+        self.expect_expr(
+            "$Derived2 d; d",
+            result_type="$Derived2",
+            result_children=[
+                ValueCheck(
+                    name="$Derived",
+                    children=[
+                        ValueCheck(
+                            name="Tagged", children=[ValueCheck(name="x", value="15")]
+                        ),
+                        ValueCheck(name="y", value="15"),
+                    ],
+                ),
+                ValueCheck(name="z", value="15"),
+            ],
+        )
+
+        # Calls to deleting and base object destructor variants (D0 and D2 in Itanium ABI)
+        self.expect_expr(
+            "struct D : public HasVirtualDtor {}; D d; d.func()",
+            result_type="int",
+            result_value="10",
+        )
+
+    def test_no_structor_linkage_names(self):
+        """
+        Test that without linkage names on structor declarations we can't call
+        ABI-tagged structors.
+        """
+        self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+
+        lldbutil.run_to_source_breakpoint(
+            self, "Break here", lldb.SBFileSpec("main.cpp", False)
+        )
+
+        self.expect("expression Tagged(17)", error=True)
+        self.expect("expr Tagged t3(t1)", error=True)
+        self.expect("expr t1.~Tagged()", error=True)
+
+        ## Calls to deleting and base object destructor variants (D0 and D2 in Itanium ABI)
+        self.expect(
+            "expression -- struct D : public HasVirtualDtor {}; D d; d.func()",
+            error=True,
+        )
+
+        self.expect("expression -- Derived d(16); d", error=True)
+
+    def do_nested_structor_test(self):
+        """
+        Test that calling ABI-tagged ctors of function local classes is not supported,
+        but calling un-tagged functions is.
+        """
+        lldbutil.run_to_source_breakpoint(
+            self, "Break nested", lldb.SBFileSpec("main.cpp", False)
+        )
+
+        self.expect("expression Local()", error=False)
+        self.expect(
+            "expression TaggedLocal()", error=True, substrs=["Couldn't look up symbols"]
+        )
+
+    def test_nested_no_structor_linkage_names(self):
+        self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
+        self.do_nested_structor_test()
+
+    def test_nested_with_structor_linkage_names(self):
+        self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+        self.do_nested_structor_test()

diff  --git a/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp
new file mode 100644
index 0000000000000..ddf237491b839
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp
@@ -0,0 +1,62 @@
+#include <cstdio>
+
+struct Tagged {
+  [[gnu::abi_tag("Default")]] Tagged() : x(15) { std::puts(__func__); }
+  [[gnu::abi_tag("Value")]] Tagged(int val) : x(val) { std::puts(__func__); }
+  [[gnu::abi_tag("Copy")]] Tagged(const Tagged &lhs) : x(lhs.x) {
+    std::puts(__func__);
+  }
+  [[gnu::abi_tag("CopyAssign")]] Tagged &operator=(const Tagged &) {
+    std::puts(__func__);
+    return *this;
+  }
+  [[gnu::abi_tag("Dtor")]] ~Tagged() { std::puts(__func__); }
+
+  int x;
+};
+
+struct Base {
+  virtual ~Base() { std::puts(__func__); }
+  virtual int func() { return 5; }
+};
+
+struct HasVirtualDtor : public Base {
+  int func() override { return 10; }
+
+  [[gnu::abi_tag("VirtualDtor")]] ~HasVirtualDtor() override {
+    std::puts(__func__);
+  }
+};
+
+struct HasNestedCtor {
+  HasNestedCtor() {
+    struct TaggedLocal {
+      [[gnu::abi_tag("Local")]] TaggedLocal() { std::puts(__func__); }
+    };
+
+    struct Local {
+      Local() { std::puts(__func__); }
+    };
+
+    TaggedLocal l1;
+    Local l2;
+    std::puts("Break nested");
+  }
+};
+
+int main() {
+  Tagged t;
+  Tagged t1(10);
+  Tagged t2(t1);
+  t1 = t2;
+
+  Base b;
+  HasVirtualDtor vdtor;
+  vdtor.func();
+
+  std::puts("Break here");
+
+  HasNestedCtor nested;
+
+  return 0;
+}

diff  --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
index 70d7fd096c7cf..c0545c70c84ea 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
@@ -5,15 +5,14 @@
 
 
 class ExprDefinitionInDylibTestCase(TestBase):
-    NO_DEBUG_INFO_TESTCASE = True
 
     @skipIfWindows
-    def test(self):
+    def test_with_structor_linkage_names(self):
         """
         Tests that we can call functions whose definition
         is in a 
diff erent LLDB module than it's declaration.
         """
-        self.build()
+        self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
 
         target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
         self.assertTrue(target, VALID_TARGET)
@@ -31,10 +30,71 @@ def test(self):
         )
 
         self.expect_expr("f.method()", result_value="-72", result_type="int")
-        self.expect_expr("Foo()", result_type="Foo")
 
-        # FIXME: mangled name lookup for ABI-tagged ctors fails because
-        # the debug-info AST doesn't have ABI-tag information.
-        self.expect(
-            "expr Bar()", error=True, substrs=["error: Couldn't look up symbols"]
+        self.expect_expr("Foo(10)", result_type="Foo")
+
+        self.expect_expr("Base()", result_type="Base")
+
+        self.expect_expr("Bar()", result_type="Bar")
+
+        # Test a more complex setup: expression that has a three bases:
+        # 1. definition is in local module
+        # 2. definition is in 
diff erent module
+        # 3. definition is in expression context (and has it's own virtual base)
+        self.expect_expr(
+            "struct ExprBase : virtual Foo { int z; ExprBase() : Foo(11) { z = x; } }; struct Expr : virtual Local, virtual Foo, virtual ExprBase { int w; Expr() : Local(), Foo(12), ExprBase() { w = y; } }; Expr tmp; tmp",
+            result_type="Expr",
+            result_children=[
+                ValueCheck(
+                    name="Local",
+                    children=[
+                        ValueCheck(
+                            name="Foo", children=[ValueCheck(name="x", value="12")]
+                        ),
+                        ValueCheck(name="y", value="12"),
+                    ],
+                ),
+                ValueCheck(name="Foo", children=[ValueCheck(name="x", value="12")]),
+                ValueCheck(
+                    name="ExprBase",
+                    children=[
+                        ValueCheck(
+                            name="Foo", children=[ValueCheck(name="x", value="12")]
+                        ),
+                        ValueCheck(name="z", value="12"),
+                    ],
+                ),
+                ValueCheck(name="w", value="12"),
+            ],
         )
+
+    @skipIfWindows
+    def test_no_structor_linkage_names(self):
+        """
+        Tests that if structor declarations don't have linkage names, we can't
+        call ABI-tagged constructors. But non-tagged ones are fine.
+        """
+        self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+
+        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(target, VALID_TARGET)
+
+        env = self.registerSharedLibrariesWithTarget(target, ["lib"])
+
+        breakpoint = lldbutil.run_break_set_by_file_and_line(
+            self, "main.cpp", line_number("main.cpp", "return")
+        )
+
+        process = target.LaunchSimple(None, env, self.get_process_working_directory())
+
+        self.assertIsNotNone(
+            lldbutil.get_one_thread_stopped_at_breakpoint_id(self.process(), breakpoint)
+        )
+
+        self.expect_expr("f.method()", result_value="-72", result_type="int")
+
+        self.expect_expr("Foo(10)", result_type="Foo")
+
+        self.expect("Base()", error=True)
+
+        self.expect("Bar()", error=True)

diff  --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
index 1a08817f5cda1..aa3921ffe0b10 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
@@ -4,10 +4,14 @@
 
 int Foo::method() { return -72; }
 
-Foo::Foo() { std::puts(__func__); }
+Foo::Foo(int val) : x(val) { std::puts(__func__); }
 
 Foo::~Foo() { std::puts(__func__); }
 
 Bar::Bar() { std::puts(__func__); }
 
 Bar::~Bar() { std::puts(__func__); }
+
+Base::Base() { std::puts(__func__); }
+
+Base::~Base() { std::puts(__func__); }

diff  --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
index 5ec227946cba0..513c9a0f9c87c 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
@@ -3,11 +3,18 @@
 
 struct Foo {
   int method();
-  Foo();
+  Foo(int val);
   ~Foo();
+
+  int x;
+};
+
+struct Base {
+  [[gnu::abi_tag("BaseCtor")]] Base();
+  [[gnu::abi_tag("BaseDtor")]] ~Base();
 };
 
-struct Bar {
+struct Bar : public Base {
   [[gnu::abi_tag("Ctor")]] Bar();
   [[gnu::abi_tag("Dtor")]] ~Bar();
 };

diff  --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
index 4d6bece21ecac..ff43190b991cc 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
@@ -1,7 +1,18 @@
 #include "lib.h"
 
+struct Local : public virtual Foo {
+  Local();
+  ~Local();
+  int y;
+};
+
+Local::Local() : Foo(5) { y = x; }
+Local::~Local() {}
+
 int main() {
-  Foo f;
-  Bar b;
+  Foo f(5);
+  Base b1;
+  Bar b2;
+  Local l1;
   return f.method();
 }

diff  --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp
index 12f6dd515fd11..ceb567c28ab99 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -23,15 +23,15 @@ struct LabelTestCase {
 
 static LabelTestCase g_label_test_cases[] = {
     // Failure modes
-    {"bar:0x0:0x0:_Z3foov",
+    {"bar:blah:0x0:0x0:_Z3foov",
      {},
      {"expected function call label prefix '$__lldb_func' but found 'bar' "
       "instead."}},
-    {"$__lldb_func :0x0:0x0:_Z3foov",
+    {"$__lldb_func :blah:0x0:0x0:_Z3foov",
      {},
      {"expected function call label prefix '$__lldb_func' but found "
       "'$__lldb_func ' instead."}},
-    {"$__lldb_funcc:0x0:0x0:_Z3foov",
+    {"$__lldb_funcc:blah:0x0:0x0:_Z3foov",
      {},
      {"expected function call label prefix '$__lldb_func' but found "
       "'$__lldb_funcc' instead."}},
@@ -39,47 +39,52 @@ static LabelTestCase g_label_test_cases[] = {
     {"foo", {}, {"malformed function call label."}},
     {"$__lldb_func", {}, {"malformed function call label."}},
     {"$__lldb_func:", {}, {"malformed function call label."}},
-    {"$__lldb_func:0x0:0x0", {}, {"malformed function call label."}},
-    {"$__lldb_func:abc:0x0:_Z3foov",
+    {"$__lldb_func:blah", {}, {"malformed function call label."}},
+    {"$__lldb_func:blah:0x0", {}, {"malformed function call label."}},
+    {"$__lldb_func:111:0x0:0x0", {}, {"malformed function call label."}},
+    {"$__lldb_func:111:abc:0x0:_Z3foov",
      {},
      {"failed to parse module ID from 'abc'."}},
-    {"$__lldb_func:-1:0x0:_Z3foov",
+    {"$__lldb_func:111:-1:0x0:_Z3foov",
      {},
      {"failed to parse module ID from '-1'."}},
-    {"$__lldb_func:0x0invalid:0x0:_Z3foov",
+    {"$__lldb_func:111:0x0invalid:0x0:_Z3foov",
      {},
      {"failed to parse module ID from '0x0invalid'."}},
-    {"$__lldb_func:0x0 :0x0:_Z3foov",
+    {"$__lldb_func:111:0x0 :0x0:_Z3foov",
      {},
      {"failed to parse module ID from '0x0 '."}},
-    {"$__lldb_func:0x0:abc:_Z3foov",
+    {"$__lldb_func:blah:0x0:abc:_Z3foov",
      {},
      {"failed to parse symbol ID from 'abc'."}},
-    {"$__lldb_func:0x5:-1:_Z3foov",
+    {"$__lldb_func:blah:0x5:-1:_Z3foov",
      {},
      {"failed to parse symbol ID from '-1'."}},
-    {"$__lldb_func:0x5:0x0invalid:_Z3foov",
+    {"$__lldb_func:blah:0x5:0x0invalid:_Z3foov",
      {},
      {"failed to parse symbol ID from '0x0invalid'."}},
-    {"$__lldb_func:0x5:0x0 :_Z3foov",
+    {"$__lldb_func:blah:0x5:0x0 :_Z3foov",
      {},
      {"failed to parse symbol ID from '0x0 '."}},
-    {"$__lldb_func:0x0:0x0:_Z3foov",
+    {"$__lldb_func:blah:0x0:0x0:_Z3foov",
      {
+         /*.discriminator=*/"blah",
          /*.module_id=*/0x0,
          /*.symbol_id=*/0x0,
          /*.lookup_name=*/"_Z3foov",
      },
      {}},
-    {"$__lldb_func:0x0:0x0:abc:def:::a",
+    {"$__lldb_func::0x0:0x0:abc:def:::a",
      {
+         /*.discriminator=*/"",
          /*.module_id=*/0x0,
          /*.symbol_id=*/0x0,
          /*.lookup_name=*/"abc:def:::a",
      },
      {}},
-    {"$__lldb_func:0xd2:0xf0:$__lldb_func",
+    {"$__lldb_func:0x45:0xd2:0xf0:$__lldb_func",
      {
+         /*.discriminator=*/"0x45",
          /*.module_id=*/0xd2,
          /*.symbol_id=*/0xf0,
          /*.lookup_name=*/"$__lldb_func",
@@ -106,6 +111,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
   EXPECT_EQ(decoded_or_err->toString(), encoded);
   EXPECT_EQ(label_str, encoded);
 
+  EXPECT_EQ(decoded_or_err->discriminator, label.discriminator);
   EXPECT_EQ(decoded_or_err->module_id, label.module_id);
   EXPECT_EQ(decoded_or_err->symbol_id, label.symbol_id);
   EXPECT_EQ(decoded_or_err->lookup_name, label.lookup_name);
@@ -113,6 +119,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
   auto roundtrip_or_err = FunctionCallLabel::fromString(label_str);
   EXPECT_THAT_EXPECTED(roundtrip_or_err, llvm::Succeeded());
 
+  EXPECT_EQ(roundtrip_or_err->discriminator, label.discriminator);
   EXPECT_EQ(roundtrip_or_err->module_id, label.module_id);
   EXPECT_EQ(roundtrip_or_err->symbol_id, label.symbol_id);
   EXPECT_EQ(roundtrip_or_err->lookup_name, label.lookup_name);

diff  --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index 91cfc6839e777..f673cceae00dd 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -1215,10 +1215,10 @@ class TestTypeSystemClangAsmLabel
 };
 
 static AsmLabelTestCase g_asm_label_test_cases[] = {
-    {/*mangled=*/"$__lldb_func:0x0:0x0:_Z3foov",
+    {/*mangled=*/"$__lldb_func::0x0:0x0:_Z3foov",
      /*expected=*/"_Z3foov"},
-    {/*mangled=*/"$__lldb_func:0x0:0x0:foo",
-     /*expected=*/"$__lldb_func:0x0:0x0:foo"},
+    {/*mangled=*/"$__lldb_func::0x0:0x0:foo",
+     /*expected=*/"$__lldb_func::0x0:0x0:foo"},
     {/*mangled=*/"foo",
      /*expected=*/"foo"},
     {/*mangled=*/"_Z3foov",


        


More information about the lldb-commits mailing list