From e439a463a30833f1c7d366ed722f0f12d1682638 Mon Sep 17 00:00:00 2001 From: Emre Kultursay Date: Mon, 26 Apr 2021 15:20:33 +0200 Subject: [PATCH] [lldb] Use forward type in pointer-to-member This change is similar in spirit to the change at: https://reviews.llvm.org/rG34c697c85e9d0af11a72ac4df5578aac94a627b3 It fixes the problem where the layout of a type was being accessed while its base classes were not populated yet; which caused an incorrect layout to be produced and cached. This fixes PR50054 Reviewed By: teemperor Differential Revision: https://reviews.llvm.org/D100977 --- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 2 +- .../lazy-loading/TestLazyLoading.py | 4 +++ .../test/API/functionalities/lazy-loading/main.cpp | 2 ++ .../Makefile | 3 ++ ...TestPointerToMemberTypeDependingOnParentSize.py | 24 +++++++++++++++ .../main.cpp | 35 ++++++++++++++++++++++ 6 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/Makefile create mode 100644 lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/TestPointerToMemberTypeDependingOnParentSize.py create mode 100644 lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/main.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index c417f80..55fd589 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1361,7 +1361,7 @@ TypeSP DWARFASTParserClang::ParsePointerToMemberType( dwarf->ResolveTypeUID(attrs.containing_type.Reference(), true); CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType(); - CompilerType class_clang_type = class_type->GetLayoutCompilerType(); + CompilerType class_clang_type = class_type->GetForwardCompilerType(); CompilerType clang_type = TypeSystemClang::CreateMemberPointerType( class_clang_type, pointee_clang_type); diff --git a/lldb/test/API/functionalities/lazy-loading/TestLazyLoading.py b/lldb/test/API/functionalities/lazy-loading/TestLazyLoading.py index 326315c..de5154e 100644 --- a/lldb/test/API/functionalities/lazy-loading/TestLazyLoading.py +++ b/lldb/test/API/functionalities/lazy-loading/TestLazyLoading.py @@ -41,6 +41,7 @@ class TestCase(TestBase): class_we_enter_decl = [class_decl_kind, "ClassWeEnter"] class_member_decl = [struct_decl_kind, "ClassMember"] class_static_member_decl = [struct_decl_kind, "StaticClassMember"] + class_pointer_to_member_decl = [struct_decl_kind, "PointerToMember"] unused_class_member_decl = [struct_decl_kind, "UnusedClassMember"] unused_class_member_ptr_decl = [struct_decl_kind, "UnusedClassMemberPtr"] @@ -58,6 +59,7 @@ class TestCase(TestBase): self.assert_decl_not_loaded(self.class_in_namespace_decl) self.assert_decl_not_loaded(self.class_member_decl) self.assert_decl_not_loaded(self.class_static_member_decl) + self.assert_decl_not_loaded(self.class_pointer_to_member_decl) self.assert_decl_not_loaded(self.unused_class_member_decl) def get_ast_dump(self): @@ -232,6 +234,8 @@ class TestCase(TestBase): self.assert_decl_loaded(self.class_member_decl) # We didn't load the type of the unused static member. self.assert_decl_not_completed(self.class_static_member_decl) + # We didn't load the type of the unused pointer-to-member member. + self.assert_decl_not_completed(self.class_pointer_to_member_decl) # This should not have loaded anything else. self.assert_decl_not_loaded(self.other_struct_decl) diff --git a/lldb/test/API/functionalities/lazy-loading/main.cpp b/lldb/test/API/functionalities/lazy-loading/main.cpp index bb8f56e..013fb02 100644 --- a/lldb/test/API/functionalities/lazy-loading/main.cpp +++ b/lldb/test/API/functionalities/lazy-loading/main.cpp @@ -26,6 +26,7 @@ struct ClassMember { int i; }; struct StaticClassMember { int i; }; struct UnusedClassMember { int i; }; struct UnusedClassMemberPtr { int i; }; +struct PointerToMember { int i; }; namespace NS { class ClassInNamespace { @@ -36,6 +37,7 @@ public: int dummy; // Prevent bug where LLDB always completes first member. ClassMember member; static StaticClassMember static_member; + int (PointerToMember::*ptr_to_member); UnusedClassMember unused_member; UnusedClassMemberPtr *unused_member_ptr; int enteredFunction() { diff --git a/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/Makefile b/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/TestPointerToMemberTypeDependingOnParentSize.py b/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/TestPointerToMemberTypeDependingOnParentSize.py new file mode 100644 index 0000000..fd97815 --- /dev/null +++ b/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/TestPointerToMemberTypeDependingOnParentSize.py @@ -0,0 +1,24 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test(self): + """ + This tests a pointer-to-member member which class part is the + surrounding class. LLDB should *not* try to generate the record layout + of the class when parsing pointer-to-member types while parsing debug + info (as the references class might not be complete when the type is + parsed). + """ + self.build() + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + + # Force the record layout for 'ToLayout' to be generated by printing + # a value of it's type. + self.expect("target variable test_var") diff --git a/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/main.cpp b/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/main.cpp new file mode 100644 index 0000000..4787691 --- /dev/null +++ b/lldb/test/API/lang/cpp/pointer_to_member_type_depending_on_parent_size/main.cpp @@ -0,0 +1,35 @@ +// This class just serves as an indirection between LLDB and Clang. LLDB might +// be tempted to check the member type of DependsOnParam2 for whether it's +// in some 'currently-loading' state before trying to produce the record layout. +// By inheriting from ToLayout this will make LLDB just check if +// DependsOnParam1 is currently being loaded (which it's not) but it won't +// check if all the types DependsOnParam2 is depending on for its layout are +// currently parsed. +template struct DependsOnParam1 : ToLayoutParam {}; +// This class forces the memory layout of it's type parameter to be created. +template struct DependsOnParam2 { + DependsOnParam1 m; +}; + +// This is the class that LLDB has to generate the record layout for. +struct ToLayout { + // The class part of this pointer-to-member type has a memory layout that + // depends on the surrounding class. If LLDB eagerly tries to layout the + // class part of a pointer-to-member type while parsing, then layouting this + // type should cause a test failure (as we aren't done parsing ToLayout + // at this point). + int DependsOnParam2::* pointer_to_member_member; + // Some dummy member variable. This is only there so that Clang can detect + // that the record layout is inconsistent (i.e., the number of fields in the + // layout doesn't fit to the fields in the declaration). + int some_member; +}; + +// Emit the definition of DependsOnParam2. It seems Clang won't +// emit the definition of a class template if it's only used in the class part +// of a pointer-to-member type. +DependsOnParam2 x; + +ToLayout test_var; + +int main() { return test_var.some_member; } -- 2.7.4