
When LLDB's DWARF parser is parsing the member DIEs of a struct/class it currently fully resolves the types of static member variables in a class before adding the respective `VarDecl` to the record. For record types fully resolving the type will also parse the member DIEs of the respective class. The other way of resolving is just 'forward' resolving the type which will try to load only the minimum amount of information about the type (for records that would only be the name/kind of the type). Usually we always resolve types on-demand so it's rarely useful to speculatively fully resolve them on the first use. This patch changes makes that we only 'forward' resolve the types of static members. This solves the fact that LLDB unnecessarily loads debug information to parse the type if it's maybe not needed later and it also avoids a crash where the parsed type might in turn reference the surrounding class that is currently being parsed. The new test case demonstrates the crash that might happen. The crash happens with the following steps: 1. We parse class `ToLayout` and it's members. 2. We parse the static class member and fully resolve its type (`DependsOnParam2<ToLayout>`). 3. That type has a non-static class member `DependsOnParam1<ToLayout>` for which LLDB will try to calculate the size. 4. The layout (and size)`DependsOnParam1<ToLayout>` turns depends on the `ToLayout` size/layout. 5. Clang will calculate the record layout/size for `ToLayout` even though we are currently parsing it and it's missing it's non-static member. The created is missing the offset for the yet unparsed non-static member. If we later try to get the offset we end up hitting different asserts. Most common is the one in `TypeSystemClang::DumpValue` where it checks that the record layout has offsets for the current FieldDecl. ``` assert(field_idx < record_layout.getFieldCount()); ``` Fixed rdar://67910011 Reviewed By: shafik Differential Revision: https://reviews.llvm.org/D100180
73 lines
1.8 KiB
C++
73 lines
1.8 KiB
C++
//----------------------------------------------------------------------------//
|
|
// Struct loading declarations.
|
|
|
|
struct StructFirstMember { int i; };
|
|
struct StructBehindPointer { int i; };
|
|
struct StructBehindRef { int i; };
|
|
struct StructMember { int i; };
|
|
|
|
StructBehindRef struct_instance;
|
|
|
|
struct SomeStruct {
|
|
StructFirstMember *first;
|
|
StructBehindPointer *ptr;
|
|
StructMember member;
|
|
StructBehindRef &ref = struct_instance;
|
|
};
|
|
|
|
struct OtherStruct {
|
|
int member_int;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------//
|
|
// Class loading declarations.
|
|
|
|
struct ClassMember { int i; };
|
|
struct StaticClassMember { int i; };
|
|
struct UnusedClassMember { int i; };
|
|
struct UnusedClassMemberPtr { int i; };
|
|
|
|
namespace NS {
|
|
class ClassInNamespace {
|
|
int i;
|
|
};
|
|
class ClassWeEnter {
|
|
public:
|
|
int dummy; // Prevent bug where LLDB always completes first member.
|
|
ClassMember member;
|
|
static StaticClassMember static_member;
|
|
UnusedClassMember unused_member;
|
|
UnusedClassMemberPtr *unused_member_ptr;
|
|
int enteredFunction() {
|
|
return member.i; // Location: class function
|
|
}
|
|
};
|
|
StaticClassMember ClassWeEnter::static_member;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------//
|
|
// Function we can stop in.
|
|
|
|
int functionWithOtherStruct() {
|
|
OtherStruct other_struct_var;
|
|
other_struct_var.member_int++; // Location: other struct function
|
|
return other_struct_var.member_int;
|
|
}
|
|
|
|
int functionWithMultipleLocals() {
|
|
SomeStruct struct_var;
|
|
OtherStruct other_struct_var;
|
|
NS::ClassInNamespace namespace_class;
|
|
other_struct_var.member_int++; // Location: multiple locals function
|
|
return other_struct_var.member_int;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
NS::ClassWeEnter c;
|
|
c.enteredFunction();
|
|
|
|
functionWithOtherStruct();
|
|
functionWithMultipleLocals();
|
|
return 0;
|
|
}
|