Skip to content

Commit cc7980f

Browse files
committed
[lldb][DWARFASTParserClang] Adjust language type for conflicting Objective-C++ forward declarations
WebKit does things like the following: ``` @Class NSString; class NSString; ``` This would produce DWARF where `NSString` is a C++ forward declaration in a C++ CU, and an Objective-C forward declaration in an Objective-C++ CU. But the type really is only ever used as an Objective-C type, so it's not a "problematic" ODR violation. This confuses LLDB, however, because when we parse a this in the context of a C++ CU, we cannot tell whether to create a `clang::RecordType` or `clang::ObjCInterfaceType`. If we create a `RecordType` from this, we run into all sort of issues later down the line. This is an unfortunate use-case, which no longer works after llvm#90663 (previously we would've just found the Objective-C definition for this forward declaration). This is similar to what motivated llvm#119860, though in that case there was a way to disambiguate the forward declarations in DWARF by fixing it on the compiler side. In this case, we really can't do anything because we're dealing with CUs of different languages. This patch attempts to fix this by doing a `FindTypes` lookup for the forward declaration in question, and checking whether all the types we found were in fact Objective-C types, in which case, it's an Objective-C++ forward declaration.
1 parent 17b4be8 commit cc7980f

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,41 @@ static void PrepareContextToReceiveMembers(TypeSystemClang &ast,
351351
}
352352
}
353353

354+
/// Returns \c true if a forward declaration in C or C++ is actually an
355+
/// Objective-C++ forward declaration. Otherwise, or on error, returns
356+
/// \c false.
357+
static bool IsCppForwardDeclObjC(SymbolFileDWARF &dwarf,
358+
const ParsedDWARFTypeAttributes &attrs) {
359+
assert(attrs.is_forward_declaration);
360+
361+
if (Language::LanguageIsObjC(attrs.class_language))
362+
return false;
363+
364+
TypeQuery query(attrs.name);
365+
TypeResults results;
366+
367+
if (SymbolFileDWARFDebugMap *debug_map_symfile = dwarf.GetDebugMapSymfile())
368+
debug_map_symfile->FindTypes(query, results);
369+
else
370+
dwarf.FindTypes(query, results);
371+
372+
// Check that all types we found are Objective-C++ types.
373+
// Otherwise we're dealing with an actual ODR violation and
374+
// we can't say for sure what language this forward declaration
375+
// referred to.
376+
bool all_objc_types = true;
377+
results.GetTypeMap().ForEach([&](const lldb::TypeSP &t) -> bool {
378+
assert(t);
379+
380+
all_objc_types &= Language::LanguageIsObjC(
381+
t->GetForwardCompilerType().GetMinimumLanguage());
382+
383+
return true;
384+
});
385+
386+
return all_objc_types;
387+
}
388+
354389
ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) {
355390
DWARFAttributes attributes = die.GetAttributes();
356391
for (size_t i = 0; i < attributes.Size(); ++i) {
@@ -1810,6 +1845,9 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
18101845
}
18111846

18121847
if (attrs.is_forward_declaration) {
1848+
if (IsCppForwardDeclObjC(*dwarf, attrs))
1849+
attrs.class_language = eLanguageTypeObjC_plus_plus;
1850+
18131851
// See if the type comes from a Clang module and if so, track down
18141852
// that type.
18151853
TypeSP type_sp = ParseTypeFromClangModule(sc, die, log);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# REQUIRES: system-darwin
2+
3+
# In this test we have two CUs with conflicting forward declaration
4+
# depending on the CU language (one is C++ and the other is Objective-C++).
5+
# We are then stopped in the C++ CU and try to print the type, at which
6+
# point LLDB will try to make it into an Clang AST node. If LLDB were to
7+
# interpret the type as C++ instead of Objective-C, we'd violate Clang
8+
# invariants and crash.
9+
#
10+
# RUN: split-file %s %t
11+
# RUN: %clangxx_host -c -g -x objective-c++ %t/request.m -o %t/request_objc.o
12+
# RUN: %clangxx_host -c -g %t/main.cpp -o %t/main.o
13+
# RUN: %clangxx_host %t/main.o %t/request_objc.o -framework Foundation -o %t/a.out
14+
#
15+
# RUN: %lldb %t/a.out \
16+
# RUN: -o "breakpoint set -p return -X main" \
17+
# RUN: -o run \
18+
# RUN: -o "frame variable r" \
19+
# RUN: -o exit | FileCheck %s
20+
#
21+
# RUN: dsymutil %t/a.out
22+
#
23+
# RUN: %lldb %t/a.out \
24+
# RUN: -o "breakpoint set -p return -X main" \
25+
# RUN: -o run \
26+
# RUN: -o "frame variable r" \
27+
# RUN: -o exit | FileCheck %s --check-prefix=CHECK-DSYM
28+
29+
# CHECK: (lldb) frame variable r
30+
# CHECK-NEXT: (Request) ::r = (m_request = "Hello, World!")
31+
32+
# CHECK-DSYM: (lldb) frame variable r
33+
# CHECK-DSYM-NEXT: (Request) ::r = (m_request = "Hello, World!")
34+
35+
#--- lib.h
36+
#ifndef LIB_H_IN
37+
#define LIB_H_IN
38+
39+
#ifdef __OBJC__
40+
@class NSString;
41+
#else
42+
class NSString;
43+
#endif
44+
45+
struct Request {
46+
NSString * m_request = nullptr;
47+
};
48+
49+
#endif // _H_IN
50+
51+
#--- main.cpp
52+
#include "lib.h"
53+
54+
void process(Request *);
55+
56+
Request r;
57+
58+
int main() {
59+
process(&r);
60+
return 0;
61+
}
62+
63+
#--- request.m
64+
#import <Foundation/Foundation.h>
65+
66+
#include "lib.h"
67+
68+
void process(Request * r) {
69+
r->m_request = @"Hello, World!";
70+
}

0 commit comments

Comments
 (0)