Skip to content

[lldb][DWARFASTParserClang] Adjust language type for conflicting Objective-C++ forward declarations #130768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Michael137
Copy link
Member

@Michael137 Michael137 commented Mar 11, 2025

WebKit does things like the following:

#ifdef __OBJC__
@class NSString;
#else
class NSString;
#endif

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 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 #90663 (previously we would've just found the Objective-C definition for this forward declaration). This is similar to what motivated #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.

…ctive-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.
@llvmbot
Copy link
Member

llvmbot commented Mar 11, 2025

@llvm/pr-subscribers-lldb

Author: Michael Buch (Michael137)

Changes

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 #90663 (previously we would've just found the Objective-C definition for this forward declaration). This is similar to what motivated #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.


Full diff: https://github.com/llvm/llvm-project/pull/130768.diff

2 Files Affected:

  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (+38)
  • (added) lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test (+70)
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 <Foundation/Foundation.h>
+
+#include "lib.h"
+
+void process(Request * r) {
+  r->m_request = @"Hello, World!";
+}

if (Language::LanguageIsObjC(attrs.class_language))
return false;

TypeQuery query(attrs.name);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hoping we can be smarter about this lookup to not incur a big penalty for doing these type lookups

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also some filtering we got to do on the context, we only want to consider types in the global namespace

@labath
Copy link
Collaborator

labath commented Mar 11, 2025

oh boy.. this is going to be messy.

So what would happen if we treated class NSString and @class NSString as two distinct types? I guess that would be a problem because then we could end up with duplicate versions of everything that depends on that type (two void foo(NSString)s, depending on whether it's parsed in a C++ or ObjC CU)? (I'm guessing the reason that it uses this hack is because it wants to refer to NSString from generic code.)

@@ -1810,6 +1845,9 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
}

if (attrs.is_forward_declaration) {
if (IsCppForwardDeclObjC(*dwarf, attrs))
attrs.class_language = eLanguageTypeObjC_plus_plus;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean we do full global search for every C++ fwddecl we encounter?

@ZequanWu
Copy link
Contributor

Since this makes lldb to do type searching more aggressively, I want to get a sense of performance impact on simplified template names from this PR. When I running the a test program with LLDB (with this PR), LLDB crashes. I don't have a reduced test case yet, will probably working on getting one when I have time:

Please include the directory content when filing a bug report
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /usr/local/google/home/zequanwu/work/llvm-project/out/cmake-reldbg/bin/lldb blaze-bin/storage/contribservice/utils/configuration/contribution_schema/builtin_field_spec_spanner_semantics_factory_test -o "b builtin_field_spec_spanner_semantics_factory_test.cc:866" -o r -o "expr this" -o "expr *this" -batch
 #0 0x00005584fdde1dd8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /usr/local/google/home/zequanwu/work/llvm-project/llvm/lib/Support/Unix/Signals.inc:804:13
 #1 0x00005584fdde00a0 llvm::sys::RunSignalHandlers() /usr/local/google/home/zequanwu/work/llvm-project/llvm/lib/Support/Signals.cpp:106:18
 #2 0x00005584fdde2591 SignalHandler(int, siginfo_t*, void*) /usr/local/google/home/zequanwu/work/llvm-project/llvm/lib/Support/Unix/Signals.inc:0:3
 #3 0x00007f9658c49e20 (/lib/x86_64-linux-gnu/libc.so.6+0x3fe20)
 #4 0x00007f9658c9de5c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007f9658c49d82 raise ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x00007f965f371a58 SignalHandler(int, siginfo_t*, void*) /usr/local/google/home/zequanwu/work/llvm-project/llvm/lib/Support/Unix/Signals.inc:420:1
 #7 0x00007f9658c49e20 (/lib/x86_64-linux-gnu/libc.so.6+0x3fe20)
 #8 0x00007f965f2360ca lldb_private::plugin::dwarf::SymbolFileDWARF::FindTypes(lldb_private::TypeQuery const&, lldb_private::TypeResults&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:2692:12
 #9 0x00007f965f26439c IsCppForwardDeclObjC(lldb_private::plugin::dwarf::SymbolFileDWARF&, ParsedDWARFTypeAttributes const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:376:8
#10 0x00007f965f26439c DWARFASTParserClang::ParseStructureLikeDIE(lldb_private::SymbolContext const&, lldb_private::plugin::dwarf::DWARFDIE const&, ParsedDWARFTypeAttributes&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:1848:9
#11 0x00007f965f262004 std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>::__shared_ptr(std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>&&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1533:20
#12 0x00007f965f262004 std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>&&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1621:2
#13 0x00007f965f262004 std::shared_ptr<lldb_private::Type>::operator=(std::shared_ptr<lldb_private::Type>&&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr.h:439:27
#14 0x00007f965f262004 DWARFASTParserClang::ParseTypeFromDWARF(lldb_private::SymbolContext const&, lldb_private::plugin::dwarf::DWARFDIE const&, bool*) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:613:15
#15 0x00007f965f236d39 std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>::operator bool() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1671:23
#16 0x00007f965f236d39 lldb_private::plugin::dwarf::SymbolFileDWARF::ParseType(lldb_private::SymbolContext const&, lldb_private::plugin::dwarf::DWARFDIE const&, bool*) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:3101:7
#17 0x00007f965f23161e std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>::__shared_ptr(std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>&&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1533:20
#18 0x00007f965f23161e std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>&&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1621:2
#19 0x00007f965f23161e std::shared_ptr<lldb_private::Type>::operator=(std::shared_ptr<lldb_private::Type>&&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr.h:439:27
#20 0x00007f965f23161e lldb_private::plugin::dwarf::SymbolFileDWARF::GetTypeForDIE(lldb_private::plugin::dwarf::DWARFDIE const&, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:2848:15
#21 0x00007f965f22fee3 std::__shared_ptr<lldb_private::Type, (__gnu_cxx::_Lock_policy)2>::get() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1667:16
#22 0x00007f965f22fee3 lldb_private::plugin::dwarf::SymbolFileDWARF::ResolveType(lldb_private::plugin::dwarf::DWARFDIE const&, bool, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:1629:63
#23 0x00007f965f226bab lldb_private::plugin::dwarf::SymbolFileDWARF::ResolveTypeUID(lldb_private::plugin::dwarf::DWARFDIE const&, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:0:12
#24 0x00007f965f26bc54 DWARFASTParserClang::ParseInheritance(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType, lldb::AccessType, std::shared_ptr<lldb_private::Module> const&, std::vector<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>, std::allocator<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>>>&, lldb_private::ClangASTImporter::LayoutInfo&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:1615:23
#25 0x00007f965f26e143 DWARFASTParserClang::ParseChildMembers(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType const&, std::vector<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>, std::allocator<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>>>&, std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>&, std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>&, std::vector<DWARFASTParserClang::DelayedAddObjCClassProperty, std::allocator<DWARFASTParserClang::DelayedAddObjCClassProperty>>&, lldb::AccessType, lldb_private::ClangASTImporter::LayoutInfo&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:3206:7
#26 0x00007f965f26d5c6 __gnu_cxx::__normal_iterator<lldb_private::plugin::dwarf::DWARFDIE*, std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>>::__normal_iterator(lldb_private::plugin::dwarf::DWARFDIE* const&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/stl_iterator.h:1068:20
#27 0x00007f965f26d5c6 std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>::begin() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/stl_vector.h:874:16
#28 0x00007f965f26d5c6 DWARFASTParserClang::CompleteRecordType(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:2219:28
#29 0x00007f965f26e93a DWARFASTParserClang::CompleteTypeFromDWARF(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::Type*, lldb_private::CompilerType const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:0:5
#30 0x00007f965f230c18 lldb_private::plugin::dwarf::SymbolFileDWARF::CompleteType(lldb_private::CompilerType&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:1622:21
#31 0x00007f965edbf069 lldb_private::Type::ResolveCompilerType(lldb_private::Type::ResolveState) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Symbol/Type.cpp:739:7
#32 0x00007f965edbf3e7 std::__weak_count<(__gnu_cxx::_Lock_policy)2>::__weak_count(std::__weak_count<(__gnu_cxx::_Lock_policy)2> const&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1155:19
#33 0x00007f965edbf3e7 std::__weak_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2>::__weak_ptr(std::__weak_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2> const&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1995:7
#34 0x00007f965edbf3e7 std::weak_ptr<lldb_private::TypeSystem>::weak_ptr(std::weak_ptr<lldb_private::TypeSystem> const&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr.h:830:7
#35 0x00007f965edbf3e7 lldb_private::CompilerType::CompilerType(lldb_private::CompilerType const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Symbol/CompilerType.h:93:9
#36 0x00007f965edbf3e7 lldb_private::Type::GetFullCompilerType() /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Symbol/Type.cpp:773:10
#37 0x00007f965f26bc74 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count(std::__weak_count<(__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1254:17
#38 0x00007f965f26bc74 std::__shared_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2>::__shared_ptr(std::__weak_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1733:9
#39 0x00007f965f26bc74 std::shared_ptr<lldb_private::TypeSystem>::shared_ptr(std::weak_ptr<lldb_private::TypeSystem> const&, std::nothrow_t) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr.h:535:9
#40 0x00007f965f26bc74 std::weak_ptr<lldb_private::TypeSystem>::lock() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr.h:874:16
#41 0x00007f965f26bc74 lldb_private::CompilerType::operator bool() const /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Symbol/CompilerType.h:117:26
#42 0x00007f965f26bc74 DWARFASTParserClang::ParseInheritance(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType, lldb::AccessType, std::shared_ptr<lldb_private::Module> const&, std::vector<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>, std::allocator<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>>>&, lldb_private::ClangASTImporter::LayoutInfo&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:1628:3
#43 0x00007f965f26e143 DWARFASTParserClang::ParseChildMembers(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType const&, std::vector<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>, std::allocator<std::unique_ptr<clang::CXXBaseSpecifier, std::default_delete<clang::CXXBaseSpecifier>>>>&, std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>&, std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>&, std::vector<DWARFASTParserClang::DelayedAddObjCClassProperty, std::allocator<DWARFASTParserClang::DelayedAddObjCClassProperty>>&, lldb::AccessType, lldb_private::ClangASTImporter::LayoutInfo&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:3206:7
#44 0x00007f965f26d5c6 __gnu_cxx::__normal_iterator<lldb_private::plugin::dwarf::DWARFDIE*, std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>>::__normal_iterator(lldb_private::plugin::dwarf::DWARFDIE* const&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/stl_iterator.h:1068:20
#45 0x00007f965f26d5c6 std::vector<lldb_private::plugin::dwarf::DWARFDIE, std::allocator<lldb_private::plugin::dwarf::DWARFDIE>>::begin() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/stl_vector.h:874:16
#46 0x00007f965f26d5c6 DWARFASTParserClang::CompleteRecordType(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:2219:28
#47 0x00007f965f26e93a DWARFASTParserClang::CompleteTypeFromDWARF(lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::Type*, lldb_private::CompilerType const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp:0:5
#48 0x00007f965f230c18 lldb_private::plugin::dwarf::SymbolFileDWARF::CompleteType(lldb_private::CompilerType&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp:1622:21
#49 0x00007f965f2be7d3 std::__weak_count<(__gnu_cxx::_Lock_policy)2>::~__weak_count() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1167:6
#50 0x00007f965f2be7d3 std::__weak_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2>::~__weak_ptr() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1997:29
#51 0x00007f965f2be7d3 lldb_private::CompilerType::~CompilerType() /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Symbol/CompilerType.h:37:7
#52 0x00007f965f2be7d3 lldb_private::TypeSystemClang::CompleteTagDecl(clang::TagDecl*) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp:9099:3
#53 0x00007f965f2c378a clang::TagDecl::isCompleteDefinition() const /usr/local/google/home/zequanwu/work/llvm-project/llvm/../clang/include/clang/AST/Decl.h:3688:46
#54 0x00007f965f2c378a GetCompleteRecordType(clang::ASTContext*, clang::QualType, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp:2641:26
#55 0x00007f965f2a963a GetCompleteQualType(clang::ASTContext*, clang::QualType, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp:2744:13
#56 0x00007f965f2b5746 lldb_private::TypeSystemClang::GetIndexOfChildMemberWithName(void*, llvm::StringRef, bool, std::vector<unsigned int, std::allocator<unsigned int>>&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp:6742:11
#57 0x00007f965ed7eaf5 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:337:8
#58 0x00007f965ed7eaf5 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1069:11
#59 0x00007f965ed7eaf5 std::__shared_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1525:31
#60 0x00007f965ed7eaf5 lldb_private::CompilerType::TypeSystemSPWrapper::~TypeSystemSPWrapper() /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Symbol/CompilerType.h:50:9
#61 0x00007f965ed7eaf5 lldb_private::CompilerType::GetIndexOfChildMemberWithName(llvm::StringRef, bool, std::vector<unsigned int, std::allocator<unsigned int>>&) const /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Symbol/CompilerType.cpp:971:14
#62 0x00007f965f2b5aa1 lldb_private::TypeSystemClang::GetIndexOfChildMemberWithName(void*, llvm::StringRef, bool, std::vector<unsigned int, std::allocator<unsigned int>>&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp:0:0
#63 0x00007f965ed7eaf5 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:337:8
#64 0x00007f965ed7eaf5 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1069:11
#65 0x00007f965ed7eaf5 std::__shared_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1525:31
#66 0x00007f965ed7eaf5 lldb_private::CompilerType::TypeSystemSPWrapper::~TypeSystemSPWrapper() /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Symbol/CompilerType.h:50:9
#67 0x00007f965ed7eaf5 lldb_private::CompilerType::GetIndexOfChildMemberWithName(llvm::StringRef, bool, std::vector<unsigned int, std::allocator<unsigned int>>&) const /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Symbol/CompilerType.cpp:971:14
#68 0x00007f965eeffd78 std::__weak_count<(__gnu_cxx::_Lock_policy)2>::~__weak_count() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1167:6
#69 0x00007f965eeffd78 std::__weak_ptr<lldb_private::TypeSystem, (__gnu_cxx::_Lock_policy)2>::~__weak_ptr() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1997:29
#70 0x00007f965eeffd78 lldb_private::CompilerType::~CompilerType() /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Symbol/CompilerType.h:37:7
#71 0x00007f965eeffd78 lldb_private::ValueObject::GetChildMemberWithName(llvm::StringRef, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/ValueObject/ValueObject.cpp:428:7
#72 0x00007f96612c71f5 std::__shared_ptr<lldb_private::ValueObject, (__gnu_cxx::_Lock_policy)2>::operator bool() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1671:16
#73 0x00007f96612c71f5 lldb_private::ClangExpressionUtil::GetLambdaValueObject(lldb_private::StackFrame*) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionUtil.cpp:21:9
#74 0x00007f96612d28f8 std::__shared_ptr<lldb_private::ValueObject, (__gnu_cxx::_Lock_policy)2>::operator bool() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/shared_ptr_base.h:1671:16
#75 0x00007f96612d28f8 (anonymous namespace)::AddLambdaCaptureDecls(lldb_private::StreamString&, lldb_private::StackFrame*, (anonymous namespace)::TokenVerifier const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp:231:12
#76 0x00007f96612d28f8 lldb_private::ClangExpressionSourceCode::AddLocalVariableDecls(lldb_private::StreamString&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, lldb_private::StackFrame*) const /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp:324:7
#77 0x00007f96612d3626 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_data() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/basic_string.h:228:28
#78 0x00007f96612d3626 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_is_local() const /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/basic_string.h:269:6
#79 0x00007f96612d3626 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_dispose() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/basic_string.h:287:7
#80 0x00007f96612d3626 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::~basic_string() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/basic_string.h:809:9
#81 0x00007f96612d3626 lldb_private::ClangExpressionSourceCode::GetText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, lldb_private::ExecutionContext&, bool, bool, llvm::ArrayRef<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>) const /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp:427:9
#82 0x00007f96612de50b lldb_private::ClangUserExpression::CreateSourceCode(lldb_private::DiagnosticManager&, lldb_private::ExecutionContext&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp:424:9
#83 0x00007f96612df342 std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>::~vector() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/stl_vector.h:735:30
#84 0x00007f96612df342 lldb_private::ClangUserExpression::PrepareForParsing(lldb_private::DiagnosticManager&, lldb_private::ExecutionContext&, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp:549:3
#85 0x00007f96612e017c lldb_private::ClangUserExpression::Parse(lldb_private::DiagnosticManager&, lldb_private::ExecutionContext&, lldb_private::ExecutionPolicy, bool, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp:653:7
#86 0x00007f965ecf50a0 lldb_private::UserExpression::Evaluate(lldb_private::ExecutionContext&, lldb_private::EvaluateExpressionOptions const&, llvm::StringRef, llvm::StringRef, std::shared_ptr<lldb_private::ValueObject>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, lldb_private::ValueObject*) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Expression/UserExpression.cpp:280:27
#87 0x00007f965ee664f0 lldb_private::Target::EvaluateExpression(llvm::StringRef, lldb_private::ExecutionContextScope*, std::shared_ptr<lldb_private::ValueObject>&, lldb_private::EvaluateExpressionOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, lldb_private::ValueObject*) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Target/Target.cpp:2858:25
#88 0x00007f96611a2794 lldb_private::CommandObjectExpression::EvaluateExpression(llvm::StringRef, lldb_private::Stream&, lldb_private::Stream&, lldb_private::CommandReturnObject&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp:428:38
#89 0x00007f96611a39f5 lldb_private::CommandObjectExpression::DoExecute(llvm::StringRef, lldb_private::CommandReturnObject&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp:668:7
#90 0x00007f965ed45d60 lldb_private::CommandObject::Cleanup() /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Interpreter/CommandObject.cpp:268:3
#91 0x00007f965ed45d60 lldb_private::CommandObjectRaw::Execute(char const*, lldb_private::CommandReturnObject&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Interpreter/CommandObject.cpp:855:5
#92 0x00007f965ed34d65 lldb_private::ElapsedTime::~ElapsedTime() /usr/local/google/home/zequanwu/work/llvm-project/lldb/include/lldb/Target/Statistics.h:88:36
#93 0x00007f965ed34d65 lldb_private::CommandInterpreter::HandleCommand(char const*, lldb_private::LazyBool, lldb_private::CommandReturnObject&, bool) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:2128:3
#94 0x00007f965ed395f0 lldb_private::CommandInterpreter::IOHandlerInputComplete(lldb_private::IOHandler&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:3229:15
#95 0x00007f965ec8aa94 lldb_private::IOHandlerEditline::Run() /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Core/IOHandler.cpp:0:22
#96 0x00007f965ec66bbc __gthread_mutex_lock(pthread_mutex_t*) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14/bits/gthr-default.h:762:12
#97 0x00007f965ec66bbc __gthread_recursive_mutex_lock(pthread_mutex_t*) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14/bits/gthr-default.h:824:10
#98 0x00007f965ec66bbc std::recursive_mutex::lock() /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/mutex:125:17
#99 0x00007f965ec66bbc std::lock_guard<std::recursive_mutex>::lock_guard(std::recursive_mutex&) /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/std_mutex.h:250:19
#100 0x00007f965ec66bbc lldb_private::Debugger::RunIOHandlers() /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Core/Debugger.cpp:1163:45
#101 0x00007f965ed3b856 lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:3519:5
#102 0x00007f965ea73d53 lldb::SBDebugger::RunCommandInterpreter(lldb::SBCommandInterpreterRunOptions const&) /usr/local/google/home/zequanwu/work/llvm-project/lldb/source/API/SBDebugger.cpp:1288:14
#103 0x00005584fddc2b8d Driver::MainLoop() /usr/local/google/home/zequanwu/work/llvm-project/lldb/tools/driver/Driver.cpp:558:17
#104 0x00005584fddc3a74 main /usr/local/google/home/zequanwu/work/llvm-project/lldb/tools/driver/Driver.cpp:810:26
#105 0x00007f9658c33d68 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#106 0x00007f9658c33e25 call_init ./csu/../csu/libc-start.c:128:20
#107 0x00007f9658c33e25 __libc_start_main ./csu/../csu/libc-start.c:347:5
#108 0x00005584fddbfd51 _start (/usr/local/google/home/zequanwu/work/llvm-project/out/cmake-reldbg/bin/lldb+0x22d51)
[1]    341741 segmentation fault (core dumped)  /usr/local/google/home/zequanwu/work/llvm-project/out/cmake-reldbg/bin/lldb

@Michael137
Copy link
Member Author

oh boy.. this is going to be messy.

So what would happen if we treated class NSString and @class NSString as two distinct types? I guess that would be a problem because then we could end up with duplicate versions of everything that depends on that type (two void foo(NSString)s, depending on whether it's parsed in a C++ or ObjC CU)? (I'm guessing the reason that it uses this hack is because it wants to refer to NSString from generic code.)

I'll try giving this a shot. If we can pull this off that'd be great. We'll probably need to modify the CompleteRecordType logic to realize that the definition DIE we found for a forward declaration is actually objective-c, and if so, create a new type from it. In theory I'd prefer this much more over what I proposed. Though can't say how feasible it is to do yet, let me try

Since this makes lldb to do type searching more aggressively, I want to get a sense of performance impact on simplified template names from this PR

If we were to go down the route I proposed, I would only do a FindTypes call for DIEs at the root namespace. And since this is only searching within the current module/library, not across all modules in the target, I suspect the performance cost wouldn't be an issue. But I'll try to get some numbers (if the approach Pavel suggested doesn't work out)

@labath
Copy link
Collaborator

labath commented Mar 12, 2025

So what would happen if we treated class NSString and @class NSString as two distinct types? I guess that would be a problem because then we could end up with duplicate versions of everything that depends on that type (two void foo(NSString)s, depending on whether it's parsed in a C++ or ObjC CU)? (I'm guessing the reason that it uses this hack is because it wants to refer to NSString from generic code.)

I'll try giving this a shot. If we can pull this off that'd be great. We'll probably need to modify the CompleteRecordType logic to realize that the definition DIE we found for a forward declaration is actually objective-c, and if so, create a new type from it. In theory I'd prefer this much more over what I proposed. Though can't say how feasible it is to do yet, let me try

I'm not sure how creating a new type would help, since at that point the c++ type already exists and could be referred to from a bunch of places. I was imagining we would just recognise that we did not find a C++ definition for this type and then do whatever we do when we don't find a definition (leave the type incomplete? forcefully complete it?...). Later, if we reach the type through an ObjC declaration, we find the ObjC defintion and complete it using that.

I think the tricky part is what happens with types like your struct Request { NSString * m_request;};. If we parse the definition of Request from a c++ unit, it will end up referring the the (incomplete) C++ version of NSString, which will prevent us from inspecting it, even if we happen to be in objc code. It might be possible to do some switcheroos in the AST importer, similar to how we replace a "forcefully completed" type from one module with an actual definition from another one. Or we could say people get what they deserve (I don't know how much you care about actually supporting this use case vs. simply not crashing).

Since this makes lldb to do type searching more aggressively, I want to get a sense of performance impact on simplified template names from this PR

If we were to go down the route I proposed, I would only do a FindTypes call for DIEs at the root namespace. And since this is only searching within the current module/library, not across all modules in the target, I suspect the performance cost wouldn't be an issue. But I'll try to get some numbers (if the approach Pavel suggested doesn't work out)

For our use cases, ~all of the code is in a single module anyway, so not searching in other modules doesn't help much. Limiting it to the global namespace definitely helps, but due to how the accelerator tables work, it still means that you have to iterate through all DIEs with the given name anywhere in the program, just to find out whether they are at the top level or not. I don't know what how much of a performance hit would that be in practice.

What would help is if we could short-circuit this code in the (common, on linux) situation where you don't have any ObjC code in your module. I'm not sure if we have a way to do that. (I don't know if we're currently parsing all CU dies, but if we are, we could check if any of them is an ObjC unit)

I also think the FindTypes call is unnecessarily heavy (and dangerously recursive) for this. Since, in this world, we want to treat the two types as equivalent, I don't think we need to find all possible definitions of that type. I think it should be sufficient to settle on a canonical definition for the type (per the ODR rule and all), so we can just pick the first one we find. And we can do the search at the DWARF DIE level -- and then also record the definition die in the unique type map, so that we pick the same one when its time to complete the type.

@Michael137
Copy link
Member Author

I'm not sure how creating a new type would help, since at that point the c++ type already exists and could be referred to from a bunch of places. I was imagining we would just recognise that we did not find a C++ definition for this type and then do whatever we do when we don't find a definition (leave the type incomplete? forcefully complete it?...). Later, if we reach the type through an ObjC declaration, we find the ObjC defintion and complete it using that.

I think the tricky part is what happens with types like your struct Request { NSString * m_request;};. If we parse the definition of Request from a c++ unit, it will end up referring the the (incomplete) C++ version of NSString, which will prevent us from inspecting it, even if we happen to be in objc code. It might be possible to do some switcheroos in the AST importer, similar to how we replace a "forcefully completed" type from one module with an actual definition from another one. Or we could say people get what they deserve (I don't know how much you care about actually supporting this use case vs. simply not crashing).

Good point, yea ideally we do want to support showing the value. Unfortunately that's something users have been relying upon. Modelling this similar to how we do forceful completion logic hasn't crossed my mind, but now I'm more inclined to go with the DWARF search route you suggested

What would help is if we could short-circuit this code in the (common, on linux) situation where you don't have any ObjC code in your module. I'm not sure if we have a way to do that. (I don't know if we're currently parsing all CU dies, but if we are, we could check if any of them is an ObjC unit). I also think the FindTypes call is unnecessarily heavy (and dangerously recursive) for this. Since, in this world, we want to treat the two types as equivalent, I don't think we need to find all possible definitions of that type. I think it should be sufficient to settle on a canonical definition for the type (per the ODR rule and all), so we can just pick the first one we find. And we can do the search at the DWARF DIE level -- and then also record the definition die in the unique type map, so that we pick the same one when its time to complete the type.

Essentially you're suggesting going back to pre-#90663, but only if we have ObjC/ObjC++ CUs in the current module? Seems sensible to me. Will also help prevent similar issues from creeping up in the future

@cmtice cmtice self-assigned this Mar 12, 2025
@labath
Copy link
Collaborator

labath commented Mar 13, 2025

Essentially you're suggesting going back to pre-#90663, but only if we have ObjC/ObjC++ CUs in the current module? Seems sensible to me. Will also help prevent similar issues from creeping up in the future

Yes, I think that is the best approach do to do definition search (devil's in the details of course), though I'm still not very happy about having to do this search in the first place.

Overall, I think I'd like the "forceful completion" approach more, though I understand it's fraught with more perils. I'm reasonably confident it can be made to work in the expression evaluator, but things like "frame variable" (which work directly on the module AST) are trickier, and I never got that to work with -flimit-debug-info (ran out of time). This case should be slightly simpler than that for two reasons:

  • both of the types are in the same module (no need to jump to another module to search for the definition)
  • You don't need to support cases where another class inherits from a "forcefully completed" class

So, it might be possible to implement something like this inside the CompilerType class, where if you get asked for a property of a type, and you see that it's one of these weird "maybe ObjC" types, you return the relevant property of the ObjC counterpart instead.

@Michael137
Copy link
Member Author

Essentially you're suggesting going back to pre-#90663, but only if we have ObjC/ObjC++ CUs in the current module? Seems sensible to me. Will also help prevent similar issues from creeping up in the future

Yes, I think that is the best approach do to do definition search (devil's in the details of course), though I'm still not very happy about having to do this search in the first place.

Overall, I think I'd like the "forceful completion" approach more, though I understand it's fraught with more perils. I'm reasonably confident it can be made to work in the expression evaluator, but things like "frame variable" (which work directly on the module AST) are trickier, and I never got that to work with -flimit-debug-info (ran out of time). This case should be slightly simpler than that for two reasons:

  • both of the types are in the same module (no need to jump to another module to search for the definition)
  • You don't need to support cases where another class inherits from a "forcefully completed" class

So, it might be possible to implement something like this inside the CompilerType class, where if you get asked for a property of a type, and you see that it's one of these weird "maybe ObjC" types, you return the relevant property of the ObjC counterpart instead.

Coming back to this now. IIUC, you're saying when we CompleteType one of these C++ forward declarations, if FindDefintionDIE returns an Objective-C definition, we bail out of starting/completing the definition and keep it as a C++ forward declaration but mark the CompilerType as one of those mixed decls? I guess what I'm not seeing is when and where we would stash away the Objective-C decl so the CompilerType can do the switch? Also, to make this work with the expression evaluator we need to make sure that when Clang asks for a complete definition using the ExternalASTSource, we will be able to provide one

@labath
Copy link
Collaborator

labath commented Mar 20, 2025

I was imagining storing this in the ClangASTMetadata class (that's where the forceful completion bit is)?
I don't know if it can be done without increasing the class size, but I guess that doesn't matter for the purpose of determining feasibility.

Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Mar 21, 2025
…types

This patch is a temporary alternative to llvm#130768

If we detect a situation where a forward declaration is C++ and the definition DIE is Objective-C, then just don't try to complete the type (it would crash otherwise). In the long term we might want to add support for completing such types

rdar://145959981
@Michael137
Copy link
Member Author

Michael137 commented Mar 21, 2025

Interestingly this alternative stop-gap (where we just bail from CompleteRecordType) on our Swift fork, seems to print the objective-C++ just fine. I'm a bit confused as to why tbh...

Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Mar 21, 2025
…types

This patch is a temporary alternative to llvm#130768

If we detect a situation where a forward declaration is C++ and the definition DIE is Objective-C, then just don't try to complete the type (it would crash otherwise). In the long term we might want to add support for completing such types

rdar://145959981
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants