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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to