-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[LLDB][NativePDB] Complete array member types in AST builder #156370
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
Conversation
@llvm/pr-subscribers-lldb Author: nerix (Nerixyz) ChangesIn 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 ( struct G {
int foo = 1;
};
struct H {
G g[2];
};
H h; Running With this PR, To hit the assertion, the layout needs to be requested (e.g. by getting a child member). That's why I used both Unfortunately, this doesn't allow Full diff: https://github.com/llvm/llvm-project/pull/156370.diff 4 Files Affected:
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
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way we do it for DWARF (for better or for worse) is that we complete the array element type, not the array type itself. FWIW, that's what completing a ConstantArrayType
does anyway. It completes the element type.
Is there a place in the PDB plugin where we can do the same thing we do for DWARF?
llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Lines 1507 to 1508 in 74b9484
CompilerType array_element_type = element_type->GetForwardCompilerType(); | |
TypeSystemClang::RequireCompleteType(array_element_type); |
As in, "if an array type is encountered, it's immediately completed"? Because in native PDB (with this PR too), it would be delayed.
Yes, in llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp Lines 1170 to 1172 in 74b9484
Would this be a better place? |
I meant, "completing a ConstantArrayType" is the same as "completing the element type". This PR implements the former, whereas the DWARF plugin we opted for the latter.
Yea that seems like the right thing to do |
Actually, what you want to do is probably just call llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp Lines 685 to 695 in 6042e09
(I suspect this is why the PDB plugin passes this test?) But I suspect this code came before |
struct I { | ||
I(); | ||
}; | ||
I::I() = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, was testing something unrelated...
It wasn't unrelated (I undid that locally).
It's to test that arrays of forward declared types work. I had to add a new type there to make ensure there was no attempt to complete it from the previous target variable
calls that don't go through arrays.
This reverts commit 28ee7f4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM as it aligns with dwarf plugin's behaviour.
In native PDB, types of 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:
Running
target variable h
would result in a request to completeH
. This will run theUdtRecordCompleter
over the struct fields. When it encounters a data member, it tries to complete its type. This would previously only complete records. However, types of constant array members (in our caseG
) need to be completed as well, as they are required to compute the layout of the record.With this PR,
PdbASTBuilder::CreateArrayType
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
andexpression
.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).