diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 0b632751574ad..8a32d8f4aa35b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -351,6 +351,41 @@ static void PrepareContextToReceiveMembers(TypeSystemClang &ast, } } +/// Returns \c true if a forward declaration in C or C++ is actually an +/// Objective-C++ forward declaration. Otherwise, or on error, returns +/// \c false. +static bool IsCppForwardDeclObjC(SymbolFileDWARF &dwarf, + const ParsedDWARFTypeAttributes &attrs) { + assert(attrs.is_forward_declaration); + + if (Language::LanguageIsObjC(attrs.class_language)) + return false; + + TypeQuery query(attrs.name); + TypeResults results; + + if (SymbolFileDWARFDebugMap *debug_map_symfile = dwarf.GetDebugMapSymfile()) + debug_map_symfile->FindTypes(query, results); + else + dwarf.FindTypes(query, results); + + // Check that all types we found are Objective-C++ types. + // Otherwise we're dealing with an actual ODR violation and + // we can't say for sure what language this forward declaration + // referred to. + bool all_objc_types = true; + results.GetTypeMap().ForEach([&](const lldb::TypeSP &t) -> bool { + assert(t); + + all_objc_types &= Language::LanguageIsObjC( + t->GetForwardCompilerType().GetMinimumLanguage()); + + return true; + }); + + return all_objc_types; +} + ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) { DWARFAttributes attributes = die.GetAttributes(); for (size_t i = 0; i < attributes.Size(); ++i) { @@ -1810,6 +1845,9 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, } if (attrs.is_forward_declaration) { + if (IsCppForwardDeclObjC(*dwarf, attrs)) + attrs.class_language = eLanguageTypeObjC_plus_plus; + // See if the type comes from a Clang module and if so, track down // that type. TypeSP type_sp = ParseTypeFromClangModule(sc, die, log); diff --git a/lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test b/lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test new file mode 100644 index 0000000000000..30109c2943c9b --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test @@ -0,0 +1,70 @@ +# REQUIRES: system-darwin + +# In this test we have two CUs with conflicting forward declaration +# depending on the CU language (one is C++ and the other is Objective-C++). +# We are then stopped in the C++ CU and try to print the type, at which +# point LLDB will try to make it into an Clang AST node. If LLDB were to +# interpret the type as C++ instead of Objective-C, we'd violate Clang +# invariants and crash. +# +# RUN: split-file %s %t +# RUN: %clangxx_host -c -g -x objective-c++ %t/request.m -o %t/request_objc.o +# RUN: %clangxx_host -c -g %t/main.cpp -o %t/main.o +# RUN: %clangxx_host %t/main.o %t/request_objc.o -framework Foundation -o %t/a.out +# +# RUN: %lldb %t/a.out \ +# RUN: -o "breakpoint set -p return -X main" \ +# RUN: -o run \ +# RUN: -o "frame variable r" \ +# RUN: -o exit | FileCheck %s +# +# RUN: dsymutil %t/a.out +# +# RUN: %lldb %t/a.out \ +# RUN: -o "breakpoint set -p return -X main" \ +# RUN: -o run \ +# RUN: -o "frame variable r" \ +# RUN: -o exit | FileCheck %s --check-prefix=CHECK-DSYM + +# CHECK: (lldb) frame variable r +# CHECK-NEXT: (Request) ::r = (m_request = "Hello, World!") + +# CHECK-DSYM: (lldb) frame variable r +# CHECK-DSYM-NEXT: (Request) ::r = (m_request = "Hello, World!") + +#--- lib.h +#ifndef LIB_H_IN +#define LIB_H_IN + +#ifdef __OBJC__ +@class NSString; +#else +class NSString; +#endif + +struct Request { + NSString * m_request = nullptr; +}; + +#endif // _H_IN + +#--- main.cpp +#include "lib.h" + +void process(Request *); + +Request r; + +int main() { + process(&r); + return 0; +} + +#--- request.m +#import + +#include "lib.h" + +void process(Request * r) { + r->m_request = @"Hello, World!"; +}