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: /// -/// ::: +/// :::: /// /// 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 - 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 lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { - llvm::SmallVector components; - label.split(components, ":", /*MaxSplit=*/3); + llvm::SmallVector 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::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 -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(structor_kind)); + } + + if (structor_kind > clang::CXXDtorType::Dtor_Unified) + return {}; + + return ClangToItaniumDtorKind(static_cast(structor_kind)); +} + +llvm::Expected +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 -SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { +SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) { std::lock_guard 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 + 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 - 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 SymbolFileDWARFDebugMap::ResolveFunctionCallLabel( - const FunctionCallLabel &label) { +llvm::Expected +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 &args) override; llvm::Expected - 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 + +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 different 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 different 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",