clayborg updated this revision to Diff 476754.
clayborg added a comment.
Changed the API comment describing the SBType::IsTypeForcefullyCompleted().
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D138259/new/
https://reviews.llvm.org/D138259
Files:
lldb/bindings/interface/SBType.i
lldb/include/lldb/API/SBType.h
lldb/include/lldb/Symbol/CompilerType.h
lldb/include/lldb/Symbol/TypeSystem.h
lldb/source/API/SBType.cpp
lldb/source/Core/ValueObject.cpp
lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
lldb/source/Symbol/CompilerType.cpp
lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py
lldb/unittests/Symbol/TestTypeSystemClang.cpp
Index: lldb/unittests/Symbol/TestTypeSystemClang.cpp
===================================================================
--- lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -394,7 +394,7 @@
RecordDecl *empty_base_decl = TypeSystemClang::GetAsRecordDecl(empty_base);
EXPECT_NE(nullptr, empty_base_decl);
- EXPECT_FALSE(TypeSystemClang::RecordHasFields(empty_base_decl));
+ EXPECT_FALSE(m_ast->RecordHasFields(empty_base_decl));
// Test that a record with direct fields returns true
CompilerType non_empty_base = m_ast->CreateRecordType(
@@ -408,7 +408,7 @@
TypeSystemClang::GetAsRecordDecl(non_empty_base);
EXPECT_NE(nullptr, non_empty_base_decl);
EXPECT_NE(nullptr, non_empty_base_field_decl);
- EXPECT_TRUE(TypeSystemClang::RecordHasFields(non_empty_base_decl));
+ EXPECT_TRUE(m_ast->RecordHasFields(non_empty_base_decl));
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
@@ -429,10 +429,9 @@
m_ast->GetAsCXXRecordDecl(empty_derived.GetOpaqueQualType());
RecordDecl *empty_derived_non_empty_base_decl =
TypeSystemClang::GetAsRecordDecl(empty_derived);
- EXPECT_EQ(1u, TypeSystemClang::GetNumBaseClasses(
+ EXPECT_EQ(1u, m_ast->GetNumBaseClasses(
empty_derived_non_empty_base_cxx_decl, false));
- EXPECT_TRUE(
- TypeSystemClang::RecordHasFields(empty_derived_non_empty_base_decl));
+ EXPECT_TRUE(m_ast->RecordHasFields(empty_derived_non_empty_base_decl));
// Test that a record with no direct fields, but fields in a virtual base
// returns true
@@ -452,10 +451,10 @@
m_ast->GetAsCXXRecordDecl(empty_derived2.GetOpaqueQualType());
RecordDecl *empty_derived_non_empty_vbase_decl =
TypeSystemClang::GetAsRecordDecl(empty_derived2);
- EXPECT_EQ(1u, TypeSystemClang::GetNumBaseClasses(
+ EXPECT_EQ(1u, m_ast->GetNumBaseClasses(
empty_derived_non_empty_vbase_cxx_decl, false));
EXPECT_TRUE(
- TypeSystemClang::RecordHasFields(empty_derived_non_empty_vbase_decl));
+ m_ast->RecordHasFields(empty_derived_non_empty_vbase_decl));
}
TEST_F(TestTypeSystemClang, TemplateArguments) {
@@ -969,4 +968,3 @@
ModuleSP module = t.GetExeModule();
EXPECT_EQ(module.get(), nullptr);
}
-
Index: lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py
===================================================================
--- lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py
+++ lldb/test/API/functionalities/limit-debug-info/TestLimitDebugInfo.py
@@ -16,10 +16,14 @@
type_ = exe.FindFirstType(name)
self.trace("type_: %s"%type_)
self.assertTrue(type_)
+ self.assertTrue(type_.IsTypeComplete())
+ self.assertFalse(type_.IsTypeForcefullyCompleted())
base = type_.GetDirectBaseClassAtIndex(0).GetType()
self.trace("base:%s"%base)
self.assertTrue(base)
self.assertEquals(base.GetNumberOfFields(), 0)
+ self.assertTrue(base.IsTypeComplete())
+ self.assertTrue(base.IsTypeForcefullyCompleted())
def _check_debug_info_is_limited(self, target):
# Without other shared libraries we should only see the member declared
@@ -28,6 +32,100 @@
self._check_type(target, "InheritsFromOne")
self._check_type(target, "InheritsFromTwo")
+ def _check_incomplete_frame_variable_output(self):
+ # Check that the display of the "frame variable" output identifies the
+ # incomplete types. Currently the expression parser will find the real
+ # definition for a type when running an expression for any forcefully
+ # completed types, but "frame variable" won't. I hope to fix this with
+ # a follow up patch, but if we don't find the actual definition we
+ # should clearly show this to the user by showing which types were
+ # incomplete. So this will test verifies the expected output for such
+ # types. We also need to verify the standard "frame variable" output
+ # which will inline all of the members on one line, versus the full
+ # output from "frame variable --raw" and a few other options.
+ # self.expect("frame variable two_as_member", error=True,
+ # substrs=["no member named 'one' in 'InheritsFromOne'"])
+
+ command_expect_pairs = [
+ # Test standard "frame variable" output for types to make sure
+ # "<incomplete type>" shows up where we expect it to
+ ["var two_as_member", [
+ "(TwoAsMember) ::two_as_member = (two = <incomplete type>, member = 47)"]
+ ],
+ ["var inherits_from_one", [
+ "(InheritsFromOne) ::inherits_from_one = (One = <incomplete type>, member = 47)"]
+ ],
+ ["var inherits_from_two", [
+ "(InheritsFromTwo) ::inherits_from_two = (Two = <incomplete type>, member = 47)"]
+ ],
+ ["var one_as_member", [
+ "(OneAsMember) ::one_as_member = (one = <incomplete type>, member = 47)"]
+ ],
+ ["var two_as_member", [
+ "(TwoAsMember) ::two_as_member = (two = <incomplete type>, member = 47)"]
+ ],
+ ["var array_of_one", [
+ "(array::One[3]) ::array_of_one = ([0] = <incomplete type>, [1] = <incomplete type>, [2] = <incomplete type>)"]
+ ],
+ ["var array_of_two", [
+ "(array::Two[3]) ::array_of_two = ([0] = <incomplete type>, [1] = <incomplete type>, [2] = <incomplete type>)"]
+ ],
+ ["var shadowed_one", [
+ "(ShadowedOne) ::shadowed_one = (func_shadow::One = <incomplete type>, member = 47)"]
+ ],
+
+ # Now test "frame variable --show-types output" which has multi-line
+ # output and should not always show classes that were forcefully
+ # completed to the user to let them know they have a type that should
+ # have been complete but wasn't.
+ ["var --show-types inherits_from_one", [
+ "(InheritsFromOne) ::inherits_from_one = {",
+ " (One) One = <incomplete type> {}",
+ " (int) member = 47",
+ "}"]
+ ],
+ ["var --show-types inherits_from_two", [
+ "(InheritsFromTwo) ::inherits_from_two = {",
+ " (Two) Two = <incomplete type> {}",
+ " (int) member = 47",
+ "}"]
+ ],
+ ["var --show-types one_as_member", [
+ "(OneAsMember) ::one_as_member = {",
+ " (member::One) one = <incomplete type> {}",
+ " (int) member = 47",
+ "}"]
+ ],
+ ["var --show-types two_as_member", [
+ "(TwoAsMember) ::two_as_member = {",
+ " (member::Two) two = <incomplete type> {}",
+ " (int) member = 47",
+ "}"]
+ ],
+ ["var --show-types array_of_one", [
+ "(array::One[3]) ::array_of_one = {",
+ " (array::One) [0] = <incomplete type> {}",
+ " (array::One) [1] = <incomplete type> {}",
+ " (array::One) [2] = <incomplete type> {}",
+ "}"]
+ ],
+ ["var --show-types array_of_two", [
+ "(array::Two[3]) ::array_of_two = {",
+ " (array::Two) [0] = <incomplete type> {}",
+ " (array::Two) [1] = <incomplete type> {}",
+ " (array::Two) [2] = <incomplete type> {}",
+ "}"]
+ ],
+ ["var --show-types shadowed_one", [
+ "(ShadowedOne) ::shadowed_one = {",
+ " (func_shadow::One) func_shadow::One = <incomplete type> {}",
+ " (int) member = 47",
+ "}"]
+ ],
+ ]
+ for command, expect_items in command_expect_pairs:
+ self.expect(command, substrs=expect_items)
+
@skipIf(bugnumber="pr46284", debug_info="gmodules")
@skipIfWindows # Clang emits type info even with -flimit-debug-info
# Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call
@@ -40,7 +138,7 @@
self._check_debug_info_is_limited(target)
lldbutil.run_to_name_breakpoint(self, "main",
- extra_images=["one", "two"])
+ extra_images=["one", "two"])
# But when other shared libraries are loaded, we should be able to see
# all members.
@@ -67,6 +165,8 @@
self.expect_expr("shadowed_one.member", result_value="47")
self.expect_expr("shadowed_one.one", result_value="142")
+ self._check_incomplete_frame_variable_output()
+
@skipIf(bugnumber="pr46284", debug_info="gmodules")
@skipIfWindows # Clang emits type info even with -flimit-debug-info
# Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call
@@ -110,6 +210,8 @@
substrs=["calling 'one' with incomplete return type 'result::One'"])
self.expect_expr("get_two().member", result_value="224")
+ self._check_incomplete_frame_variable_output()
+
@skipIf(bugnumber="pr46284", debug_info="gmodules")
@skipIfWindows # Clang emits type info even with -flimit-debug-info
# Requires DW_CC_pass_by_* attributes from Clang 7 to correctly call
@@ -155,3 +257,5 @@
substrs=["calling 'get_two' with incomplete return type 'result::Two'"])
self.expect("expr get_two().member", error=True,
substrs=["calling 'get_two' with incomplete return type 'result::Two'"])
+
+ self._check_incomplete_frame_variable_output()
Index: lldb/source/Symbol/CompilerType.cpp
===================================================================
--- lldb/source/Symbol/CompilerType.cpp
+++ lldb/source/Symbol/CompilerType.cpp
@@ -86,6 +86,12 @@
return false;
}
+bool CompilerType::IsForcefullyCompleted() const {
+ if (IsValid())
+ return m_type_system->IsForcefullyCompleted(m_type);
+ return false;
+}
+
bool CompilerType::IsConst() const {
if (IsValid())
return m_type_system->IsConst(m_type);
Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
===================================================================
--- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -303,8 +303,16 @@
static clang::AccessSpecifier
UnifyAccessSpecifiers(clang::AccessSpecifier lhs, clang::AccessSpecifier rhs);
- static uint32_t GetNumBaseClasses(const clang::CXXRecordDecl *cxx_record_decl,
- bool omit_empty_base_classes);
+ uint32_t GetNumBaseClasses(const clang::CXXRecordDecl *cxx_record_decl,
+ bool omit_empty_base_classes);
+
+ uint32_t GetIndexForRecordChild(const clang::RecordDecl *record_decl,
+ clang::NamedDecl *canonical_decl,
+ bool omit_empty_base_classes);
+
+ uint32_t GetIndexForRecordBase(const clang::RecordDecl *record_decl,
+ const clang::CXXBaseSpecifier *base_spec,
+ bool omit_empty_base_classes);
/// Synthesize a clang::Module and return its ID or a default-constructed ID.
OptionalClangModuleID GetOrCreateClangModule(llvm::StringRef name,
@@ -374,7 +382,9 @@
bool FieldIsBitfield(clang::FieldDecl *field, uint32_t &bitfield_bit_size);
- static bool RecordHasFields(const clang::RecordDecl *record_decl);
+ bool RecordHasFields(const clang::RecordDecl *record_decl);
+
+ bool BaseSpecifierIsEmpty(const clang::CXXBaseSpecifier *b);
CompilerType CreateObjCClass(llvm::StringRef name,
clang::DeclContext *decl_ctx,
@@ -641,6 +651,8 @@
bool GetCompleteType(lldb::opaque_compiler_type_t type) override;
+ bool IsForcefullyCompleted(lldb::opaque_compiler_type_t type) override;
+
// Accessors
ConstString GetTypeName(lldb::opaque_compiler_type_t type,
Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
===================================================================
--- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -1803,6 +1803,17 @@
return true;
}
}
+
+ // We always want forcefully completed types to show up so we can print a
+ // message in the summary that indicates that the type is incomplete.
+ // This will help users know when they are running into issues with
+ // -flimit-debug-info instead of just seeing nothing if this is a base class
+ // (since we were hiding empty base classes), or nothing when you turn open
+ // an valiable whose type was incomplete.
+ ClangASTMetadata *meta_data = GetMetadata(record_decl);
+ if (meta_data && meta_data->IsForcefullyCompleted())
+ return true;
+
return false;
}
@@ -1830,7 +1841,7 @@
return GetType(ast.getObjCInterfaceType(decl));
}
-static inline bool BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) {
+bool TypeSystemClang::BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) {
return !TypeSystemClang::RecordHasFields(b->getType()->getAsCXXRecordDecl());
}
@@ -6593,9 +6604,10 @@
return CompilerType();
}
-static uint32_t GetIndexForRecordBase(const clang::RecordDecl *record_decl,
- const clang::CXXBaseSpecifier *base_spec,
- bool omit_empty_base_classes) {
+uint32_t TypeSystemClang::GetIndexForRecordBase(
+ const clang::RecordDecl *record_decl,
+ const clang::CXXBaseSpecifier *base_spec,
+ bool omit_empty_base_classes) {
uint32_t child_idx = 0;
const clang::CXXRecordDecl *cxx_record_decl =
@@ -6620,9 +6632,9 @@
return UINT32_MAX;
}
-static uint32_t GetIndexForRecordChild(const clang::RecordDecl *record_decl,
- clang::NamedDecl *canonical_decl,
- bool omit_empty_base_classes) {
+uint32_t TypeSystemClang::GetIndexForRecordChild(
+ const clang::RecordDecl *record_decl, clang::NamedDecl *canonical_decl,
+ bool omit_empty_base_classes) {
uint32_t child_idx = TypeSystemClang::GetNumBaseClasses(
llvm::dyn_cast<clang::CXXRecordDecl>(record_decl),
omit_empty_base_classes);
@@ -9988,3 +10000,19 @@
m_isolated_asts[feature] = std::move(new_ast);
return *m_isolated_asts[feature];
}
+
+bool TypeSystemClang::IsForcefullyCompleted(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+ const clang::RecordType *record_type =
+ llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr());
+ if (record_type) {
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ assert(record_decl);
+ ClangASTMetadata *metadata = GetMetadata(record_decl);
+ if (metadata)
+ return metadata->IsForcefullyCompleted();
+ }
+ }
+ return false;
+}
Index: lldb/source/Core/ValueObject.cpp
===================================================================
--- lldb/source/Core/ValueObject.cpp
+++ lldb/source/Core/ValueObject.cpp
@@ -594,6 +594,14 @@
const TypeSummaryOptions &options) {
destination.clear();
+ // If we have a forcefully completed type, don't try and show a summary from
+ // a valid summary string or function because the type is not complete and
+ // no member variables or member functions will be available.
+ if (GetCompilerType().IsForcefullyCompleted()) {
+ destination = "<incomplete type>";
+ return true;
+ }
+
// ideally we would like to bail out if passing NULL, but if we do so we end
// up not providing the summary for function pointers anymore
if (/*summary_ptr == NULL ||*/ m_flags.m_is_getting_summary)
Index: lldb/source/API/SBType.cpp
===================================================================
--- lldb/source/API/SBType.cpp
+++ lldb/source/API/SBType.cpp
@@ -582,6 +582,13 @@
return eTemplateArgumentKindNull;
}
+bool SBType::IsTypeForcefullyCompleted() {
+ LLDB_INSTRUMENT_VA(this);
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(false).IsForcefullyCompleted();
+ return false;
+}
+
SBTypeList::SBTypeList() : m_opaque_up(new TypeListImpl()) {
LLDB_INSTRUMENT_VA(this);
}
Index: lldb/include/lldb/Symbol/TypeSystem.h
===================================================================
--- lldb/include/lldb/Symbol/TypeSystem.h
+++ lldb/include/lldb/Symbol/TypeSystem.h
@@ -201,6 +201,10 @@
virtual bool GetCompleteType(lldb::opaque_compiler_type_t type) = 0;
+ virtual bool IsForcefullyCompleted(lldb::opaque_compiler_type_t type) {
+ return false;
+ }
+
// AST related queries
virtual uint32_t GetPointerByteSize() = 0;
Index: lldb/include/lldb/Symbol/CompilerType.h
===================================================================
--- lldb/include/lldb/Symbol/CompilerType.h
+++ lldb/include/lldb/Symbol/CompilerType.h
@@ -152,6 +152,8 @@
bool GetCompleteType() const;
/// \}
+ bool IsForcefullyCompleted() const;
+
/// AST related queries.
/// \{
size_t GetPointerByteSize() const;
Index: lldb/include/lldb/API/SBType.h
===================================================================
--- lldb/include/lldb/API/SBType.h
+++ lldb/include/lldb/API/SBType.h
@@ -205,6 +205,29 @@
bool IsTypeComplete();
+ /// Return true if this type should have been complete in the debug
+ /// information but it the full definition was omitted.
+ ///
+ /// Returns true for types that were incomplete in the debug information but
+ /// should have been complete. When the debugger constructs types, we must
+ /// have enough information to reconstruct a type. When we need a complete
+ /// type, such as when a class A inherits from class B, we need a full
+ /// definition for class B so that we can construct class A. Another instance
+ /// is we have a variable that is an instance of a type (not just a pointer or
+ /// reference) we must also have the full definition of the class in order to
+ /// display the variable to the user. Compiler options (-flimit-debug-info)
+ /// can cause the compiler to not emit full debug information for some types
+ /// in order to reduce debug information size. The assumption is the full
+ /// definition of this type will be available in some of the debug information
+ /// in a debug session, possibly even in another executable or shared
+ /// library's debug information. If a full definition for a type was required
+ /// but we can't find a full definition, this function will return true. Type
+ /// objects may return true to SBType::IsTypeComplete() as the types were
+ /// internally made complete to allow the type that depends on this type to be
+ /// created, but this function indicates that debug information should have
+ /// had a complete definition for the type, but we were not able to find one.
+ bool IsTypeForcefullyCompleted();
+
uint32_t GetTypeFlags();
bool GetDescription(lldb::SBStream &description,
Index: lldb/bindings/interface/SBType.i
===================================================================
--- lldb/bindings/interface/SBType.i
+++ lldb/bindings/interface/SBType.i
@@ -867,6 +867,9 @@
bool
IsTypeComplete ();
+ bool
+ IsTypeForcefullyCompleted ();
+
%feature("docstring",
"Returns the `TypeFlags` values for this type.
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits