https://github.com/Nerixyz created 
https://github.com/llvm/llvm-project/pull/156370

In native PDB, types of constant array members were not completed when 
completing a struct. This lead to an assertion failure later when trying to 
print a variable of the struct (`Shell/SymbolFile/PDB/udt-layout.test`, see 
#114906).
The following shows the issue:
```cpp
struct G {
  int foo = 1;
};
struct H {
  G g[2];
};
H h;
```
Running `target variable h` would result in a request to complete `H`. This 
will run the `UdtRecordCompleter` over the struct fields. When it encounters a 
data member, [it tries to complete its 
type](https://github.com/llvm/llvm-project/blob/bf4486eb29c3009ddf68968b021c6fd98e3c2d52/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp#L260).
 This would previously only complete records. However, types of constant array 
members (in our case `G`) need to be completed as well, as they are required to 
compute the layout of the record.

With this PR, `TypeSystemClang::RequireCompleteType` will also complete the 
types of constant array members.

To hit the assertion, the layout needs to be requested (e.g. by getting a child 
member). That's why I used both `target variable` and `expression`.

Unfortunately, this doesn't allow `Shell/SymbolFile/PDB/udt-layout.test` to run 
with the native plugin yet, because it checks for the order in which base 
classes are printed. The DIA plugin puts virtual bases first, while the native 
plugin puts them last (where they are in memory).

>From 967d3453e997ed25c3548898d69a40d079d307b1 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerix...@outlook.de>
Date: Mon, 1 Sep 2025 21:02:33 +0200
Subject: [PATCH] [LLDB] Complete constant array member types in class members

---
 .../TypeSystem/Clang/TypeSystemClang.cpp      |  15 ++-
 .../NativePDB/Inputs/incomplete-tag-type.cpp  |   5 +
 .../NativePDB/incomplete-tag-type.cpp         |  45 --------
 .../NativePDB/incomplete-tag-type.test        | 109 ++++++++++++++++++
 4 files changed, 126 insertions(+), 48 deletions(-)
 delete mode 100644 lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp
 create mode 100644 
lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test

diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp 
b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 39aacdb58e694..038677f68b991 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -9577,12 +9577,21 @@ TypeSystemClang::DeclContextGetTypeSystemClang(const 
CompilerDeclContext &dc) {
 }
 
 void TypeSystemClang::RequireCompleteType(CompilerType type) {
+  if (!type)
+    return;
+
+  clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+  if (qual_type.isNull())
+    return;
+
   // Technically, enums can be incomplete too, but we don't handle those as 
they
   // are emitted even under -flimit-debug-info.
-  if (!TypeSystemClang::IsCXXClassType(type))
-    return;
+  bool is_constant_array = qual_type->isConstantArrayType();
+  bool is_cxx_record = qual_type->getAsCXXRecordDecl() != nullptr;
+  if (is_constant_array || is_cxx_record)
+    type.GetCompleteType();
 
-  if (type.GetCompleteType())
+  if (!is_cxx_record)
     return;
 
   // No complete definition in this module.  Mark the class as complete to
diff --git 
a/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp 
b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp
index c930338905445..d08f49d1014ba 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp
@@ -13,3 +13,8 @@ struct E {
   E();
 };
 E::E() = default;
+
+struct I {
+  I();
+};
+I::I() = default;
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp 
b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp
deleted file mode 100644
index 7bc7e618667f7..0000000000000
--- a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// clang-format off
-// REQUIRES: lld, x86
-
-// RUN: %clang_cl --target=x86_64-windows-msvc -c /Fo%t1.obj -- 
%p/Inputs/incomplete-tag-type.cpp
-// RUN: %clang_cl --target=x86_64-windows-msvc /O1 /Z7 -c /Fo%t2.obj -- %s
-// RUN: lld-link /debug:full /nodefaultlib /entry:main %t1.obj %t2.obj 
/out:%t.exe /pdb:%t.pdb
-// RUN: %lldb -f %t.exe -o \
-// RUN:   "settings set interpreter.stop-command-source-on-error false" \
-// RUN:   -o "expression b" -o "expression d" -o "expression static_e_ref" -o 
"exit" 2>&1 | FileCheck %s
-
-// CHECK: (lldb) expression b
-// CHECK: (B) $0 = {}
-// CHECK: (lldb) expression d
-// CHECK: (D) $1 = {}
-// CHECK: (lldb) expression static_e_ref
-// CHECK: error:{{.*}}incomplete type 'E' where a complete type is required
-
-// Complete base class.
-struct A { int x; A(); };
-struct B : A {};
-B b;
-
-// Complete data member.
-struct C {
-  C();
-};
-
-struct D {
-  C c;
-};
-D d;
-
-// Incomplete static data member should return error.
-struct E {
-  E();
-};
-
-struct F {
-  static E static_e;
-};
-
-E F::static_e = E();
-E& static_e_ref = F::static_e;
-
-int main(){}
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test 
b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test
new file mode 100644
index 0000000000000..f30866ccdd6f0
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test
@@ -0,0 +1,109 @@
+# REQUIRES: lld, x86
+
+# RUN: split-file %s %t
+
+# RUN: %clang_cl --target=x86_64-windows-msvc -c /Fo%t1.obj -- 
%p/Inputs/incomplete-tag-type.cpp
+# RUN: %clang_cl --target=x86_64-windows-msvc /O1 /Z7 -c /Fo%t2.obj -- 
%t/main.cpp
+# RUN: lld-link /debug:full /nodefaultlib /entry:main %t1.obj %t2.obj 
/out:%t.exe /pdb:%t.pdb
+
+# RUN: %lldb -f %t.exe -s %t/target-var.input 2>&1 | FileCheck %s 
--check-prefix=TARGET-VAR
+# RUN: %lldb -f %t.exe -s %t/expr.input 2>&1 | FileCheck %s --check-prefix=EXPR
+
+#--- main.cpp
+
+// Complete base class.
+struct A { int x; A(); };
+struct B : A {};
+B b;
+
+// Complete data member.
+struct C {
+  C();
+};
+
+struct D {
+  C c;
+};
+D d;
+
+// Incomplete static data member should return error.
+struct E {
+  E();
+};
+
+struct F {
+  static E static_e;
+};
+
+E F::static_e = E();
+E& static_e_ref = F::static_e;
+
+struct G {
+  int foo = 1;
+};
+struct H {
+  G g[2];
+};
+H h;
+
+struct I {
+  I();
+};
+struct J {
+  I i[2];
+};
+J j;
+
+
+int main(){}
+
+#--- target-var.input
+
+target variable b
+target variable d
+target variable h
+target variable j
+target variable static_e_ref
+exit
+
+#--- expr.input
+
+settings set interpreter.stop-command-source-on-error false
+expression b
+expression d
+expression h
+expression j
+expression static_e_ref
+exit
+
+# TARGET-VAR:      (lldb) target variable b
+# TARGET-VAR-NEXT: (B) b = (A = <incomplete type>)
+# TARGET-VAR-NEXT: (lldb) target variable d
+# TARGET-VAR-NEXT: (D) d = {}
+# TARGET-VAR-NEXT: (lldb) target variable h
+# TARGET-VAR-NEXT: (H) h = {
+# TARGET-VAR-NEXT:   g = {
+# TARGET-VAR-NEXT:     [0] = (foo = 1)
+# TARGET-VAR-NEXT:     [1] = (foo = 1)
+# TARGET-VAR-NEXT:   }
+# TARGET-VAR-NEXT: }
+# TARGET-VAR-NEXT: (lldb) target variable j
+# TARGET-VAR-NEXT: (J) j = {}
+# TARGET-VAR-NEXT: (lldb) target variable static_e_ref
+# TARGET-VAR-NEXT: (E &) static_e_ref = 0x{{.*}} <incomplete type "E">
+
+# EXPR:      (lldb) expression b
+# EXPR-NEXT: (B) $0 = {}
+# EXPR-NEXT: (lldb) expression d
+# EXPR-NEXT: (D) $1 = {}
+# EXPR-NEXT: (lldb) expression h
+# EXPR-NEXT: (H) $2 = {
+# EXPR-NEXT:   g = {
+# EXPR-NEXT:     [0] = (foo = 1)
+# EXPR-NEXT:     [1] = (foo = 1)
+# EXPR-NEXT:   }
+# EXPR-NEXT: }
+# EXPR-NEXT: (lldb) expression j
+# EXPR-NEXT: (J) $3 = {}
+# EXPR-NEXT: (lldb) expression static_e_ref
+# EXPR:      error:{{.*}}incomplete type 'E' where a complete type is required

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to