Skip to content

[lldb] Make SBType::FindDirectNestedType work with expression ASTs #89183

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

Merged
merged 2 commits into from
Apr 19, 2024

Conversation

labath
Copy link
Collaborator

@labath labath commented Apr 18, 2024

The types we get out of expressions will not have an associated symbol file, so the current method of looking up the type will fail. Instead, I plumb the query through the TypeSystem class. This correctly finds the type in both cases (importing it into the expression AST if needed). I haven't measured, but it should also be more efficient than doing a type lookup (at least, after the type has already been found once).

@llvmbot
Copy link
Member

llvmbot commented Apr 18, 2024

@llvm/pr-subscribers-lldb

Author: Pavel Labath (labath)

Changes

The types we get out of expressions will not have an associated symbol file, so the current method of looking up the type will fail. Instead, I plumb the query through the TypeSystem class. This correctly finds the type in both cases (importing it into the expression AST if needed). I haven't measured, but it should also be more efficient than doing a type lookup (at least, after the type has already been found once).


Full diff: https://github.com/llvm/llvm-project/pull/89183.diff

5 Files Affected:

  • (modified) lldb/include/lldb/Symbol/TypeSystem.h (+7)
  • (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+30)
  • (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h (+4)
  • (modified) lldb/source/Symbol/Type.cpp (+1-14)
  • (modified) lldb/test/API/python_api/type/TestTypeList.py (+19-16)
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index f647fcbf1636ea..3a927d313b823d 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -28,6 +28,7 @@
 #include "lldb/Symbol/CompilerDeclContext.h"
 #include "lldb/Symbol/Type.h"
 #include "lldb/lldb-private.h"
+#include "lldb/lldb-types.h"
 
 class PDBASTParser;
 
@@ -363,6 +364,12 @@ class TypeSystem : public PluginInterface,
       lldb::opaque_compiler_type_t type, llvm::StringRef name,
       bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) = 0;
 
+  virtual CompilerType
+  GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
+                              llvm::StringRef name) {
+    return CompilerType();
+  }
+
   virtual bool IsTemplateType(lldb::opaque_compiler_type_t type);
 
   virtual size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type,
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index be0ddb06f82c18..2621f682011b41 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -6997,6 +6997,36 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
   return UINT32_MAX;
 }
 
+CompilerType
+TypeSystemClang::GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
+                                             llvm::StringRef name) {
+  if (!type || name.empty())
+    return CompilerType();
+
+  clang::QualType qual_type = RemoveWrappingTypes(GetCanonicalQualType(type));
+  const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+
+  switch (type_class) {
+  case clang::Type::Record: {
+    if (!GetCompleteType(type))
+      return CompilerType();
+    const clang::RecordType *record_type =
+        llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+    const clang::RecordDecl *record_decl = record_type->getDecl();
+
+    clang::DeclarationName decl_name(&getASTContext().Idents.get(name));
+    for (NamedDecl *decl : record_decl->lookup(decl_name)) {
+      if (auto *tag_decl = dyn_cast<clang::TagDecl>(decl))
+        return GetType(getASTContext().getTagDeclType(tag_decl));
+    }
+    break;
+  }
+  default:
+    break;
+  }
+  return CompilerType();
+}
+
 bool TypeSystemClang::IsTemplateType(lldb::opaque_compiler_type_t type) {
   if (!type)
     return false;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 05c303baa41640..cfe5327e14f823 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -897,6 +897,10 @@ class TypeSystemClang : public TypeSystem {
                                 bool omit_empty_base_classes,
                                 std::vector<uint32_t> &child_indexes) override;
 
+  CompilerType
+  GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
+                              llvm::StringRef name) override;
+
   bool IsTemplateType(lldb::opaque_compiler_type_t type) override;
 
   size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type,
diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index 44a24d7178f562..5c49c4726157fb 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -1177,20 +1177,7 @@ CompilerType TypeImpl::FindDirectNestedType(llvm::StringRef name) {
   if (name.empty())
     return CompilerType();
   auto type_system = GetTypeSystem(/*prefer_dynamic*/ false);
-  auto *symbol_file = type_system->GetSymbolFile();
-  if (!symbol_file)
-    return CompilerType();
-  auto decl_context = type_system->GetCompilerDeclContextForType(m_static_type);
-  if (!decl_context.IsValid())
-    return CompilerType();
-  TypeQuery query(decl_context, ConstString(name),
-                  TypeQueryOptions::e_find_one);
-  TypeResults results;
-  symbol_file->FindTypes(query, results);
-  TypeSP type_sp = results.GetFirstType();
-  if (type_sp)
-    return type_sp->GetFullCompilerType();
-  return CompilerType();
+  return type_system->GetDirectNestedTypeWithName(m_static_type.GetOpaqueQualType(), name);
 }
 
 bool TypeMemberFunctionImpl::IsValid() {
diff --git a/lldb/test/API/python_api/type/TestTypeList.py b/lldb/test/API/python_api/type/TestTypeList.py
index eba5e17355c3f8..b27842eb3be87a 100644
--- a/lldb/test/API/python_api/type/TestTypeList.py
+++ b/lldb/test/API/python_api/type/TestTypeList.py
@@ -18,6 +18,21 @@ def setUp(self):
         self.source = "main.cpp"
         self.line = line_number(self.source, "// Break at this line")
 
+    def _find_nested_type_in_Task_pointer(self, pointer_type):
+        self.assertTrue(pointer_type)
+        self.DebugSBType(pointer_type)
+        pointer_info_type = pointer_type.template_args[1]
+        self.assertTrue(pointer_info_type)
+        self.DebugSBType(pointer_info_type)
+
+        pointer_masks1_type = pointer_info_type.FindDirectNestedType("Masks1")
+        self.assertTrue(pointer_masks1_type)
+        self.DebugSBType(pointer_masks1_type)
+
+        pointer_masks2_type = pointer_info_type.FindDirectNestedType("Masks2")
+        self.assertTrue(pointer_masks2_type)
+        self.DebugSBType(pointer_masks2_type)
+
     @skipIf(compiler="clang", compiler_version=["<", "17.0"])
     def test(self):
         """Exercise SBType and SBTypeList API."""
@@ -151,22 +166,10 @@ def test(self):
         invalid_type = task_type.FindDirectNestedType(None)
         self.assertFalse(invalid_type)
 
-        # Check that FindDirectNestedType works with types from AST
-        pointer = frame0.FindVariable("pointer")
-        pointer_type = pointer.GetType()
-        self.assertTrue(pointer_type)
-        self.DebugSBType(pointer_type)
-        pointer_info_type = pointer_type.template_args[1]
-        self.assertTrue(pointer_info_type)
-        self.DebugSBType(pointer_info_type)
-
-        pointer_masks1_type = pointer_info_type.FindDirectNestedType("Masks1")
-        self.assertTrue(pointer_masks1_type)
-        self.DebugSBType(pointer_masks1_type)
-
-        pointer_masks2_type = pointer_info_type.FindDirectNestedType("Masks2")
-        self.assertTrue(pointer_masks2_type)
-        self.DebugSBType(pointer_masks2_type)
+        # Check that FindDirectNestedType works with types from module and
+        # expression ASTs.
+        self._find_nested_type_in_Task_pointer(frame0.FindVariable("pointer").GetType())
+        self._find_nested_type_in_Task_pointer(frame0.EvaluateExpression("pointer").GetType())
 
         # We'll now get the child member 'id' from 'task_head'.
         id = task_head.GetChildMemberWithName("id")

Copy link

github-actions bot commented Apr 18, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link

github-actions bot commented Apr 18, 2024

✅ With the latest revision this PR passed the Python code formatter.

@labath
Copy link
Collaborator Author

labath commented Apr 18, 2024

In terms of #68705, I think this will fix the issue where the data formatter works on frame var foo but not expr -- foo. At least that's the problem I ran into when trying to use this in my own formatter..

One thing I'd like to get feedback on is whether this should be looking into base classes. The discussion on the original PR focuses on lexically nested types, but going through base classes (following usual language rules) is another form of nesting. Both the existing and the new implementations don't do that, but I think it would be more natural to do it. Of course, then the function can definitely return more than one result...

The types we get out of expressions will not have an associated symbol
file, so the current method of looking up the type will fail. Instead, I
plumb the query through the TypeSystem class. This correctly finds the
type in both cases (importing it into the expression AST if needed). I
haven't measured, but it should also be more efficient than doing a type
lookup (at least, after the type has already been found once).
@Endilll
Copy link
Contributor

Endilll commented Apr 18, 2024

One thing I'd like to get feedback on is whether this should be looking into base classes. The discussion on the original PR focuses on lexically nested types, but going through base classes (following usual language rules) is another form of nesting. Both the existing and the new implementations don't do that, but I think it would be more natural to do it. Of course, then the function can definitely return more than one result...

What you're describing here is in line with member lookup described in the Standard. While it might be a useful facility, it's at odds with the intent of FindDirectNestedTypes() to be a small building block for efficient AST traversal. For the use case I have for this function, any amount of lookup is going to regress the performance of the formatter, and subsequently responsiveness of the debugger. So if you're going to pursue this, I suggest exposing this as a new function.

@Michael137
Copy link
Member

One thing I'd like to get feedback on is whether this should be looking into base classes. The discussion on the original PR focuses on lexically nested types, but going through base classes (following usual language rules) is another form of nesting. Both the existing and the new implementations don't do that, but I think it would be more natural to do it. Of course, then the function can definitely return more than one result...

What you're describing here is in line with member lookup described in the Standard. While it might be a useful facility, it's at odds with the intent of FindDirectNestedTypes() to be a small building block for efficient AST traversal. For the use case I have for this function, any amount of lookup is going to regress the performance of the formatter, and subsequently responsiveness of the debugger. So if you're going to pursue this, I suggest exposing this as a new function.

I don't expect the simple DeclContext lookup proposed here to be expensive. What is expensive from the data-formatters point of view is either full-blown expression evaluation or a global search in DWARF for some type.

Re. looking at base classes, don't have a strong opinion on it. There's some precedent for looking at base classes already in TypeSystemClang (e.g., GetIndexOfChildWithName). But as you say @labath, that would require an API change

@labath
Copy link
Collaborator Author

labath commented Apr 18, 2024

One thing I'd like to get feedback on is whether this should be looking into base classes. The discussion on the original PR focuses on lexically nested types, but going through base classes (following usual language rules) is another form of nesting. Both the existing and the new implementations don't do that, but I think it would be more natural to do it. Of course, then the function can definitely return more than one result...

What you're describing here is in line with member lookup described in the Standard. While it might be a useful facility, it's at odds with the intent of FindDirectNestedTypes() to be a small building block for efficient AST traversal. For the use case I have for this function, any amount of lookup is going to regress the performance of the formatter, and subsequently responsiveness of the debugger. So if you're going to pursue this, I suggest exposing this as a new function.

I don't expect the simple DeclContext lookup proposed here to be expensive. What is expensive from the data-formatters point of view is either full-blown expression evaluation or a global search in DWARF for some type.

Agreed. This should be the same lookup that clang does every time is sees a ::. Going through (potentially many) calls to FindTypes would be a different story. The reason I was asking is because I was not sure if this behavior was just a side-effect of how the original implementation worked, or if it was the intention all along. Judging by the response, it's the latter.

Re. looking at base classes, don't have a strong opinion on it. There's some precedent for looking at base classes already in TypeSystemClang (e.g., GetIndexOfChildWithName). But as you say @labath, that would require an API change

That's makes sense. Even if it was not the intention, the API makes this the only reasonable implementation. I have no use for the base-class traversal (may change in the future). If there's is a need for it, we can add a different API.

@Endilll
Copy link
Contributor

Endilll commented Apr 19, 2024

@Michael137 suggested that I check that performance of FindDirectNestedType doesn't regress (at least for my use case), since I have custom instrumentation in my formatter. I can confirm that 3 versions of this function (#68705, #74786, and this PR) exhibit the same level of performance. Raw numbers (in nanoseconds) fluctuate a lot, but I don't see anything concerning:

This PR
-------
2,611,025
1,988,893
2,878,981
1,873,220

main branch (#74786)
----
1,973,071
2,542,073
1,509,624

Initial implementation (#68705)
------
2,029,233
2,477,041
1,315,462

@labath
Copy link
Collaborator Author

labath commented Apr 19, 2024

@Michael137 suggested that I check that performance of FindDirectNestedType doesn't regress (at least for my use case), since I have custom instrumentation in my formatter. I can confirm that 3 versions of this function (#68705, #74786, and this PR) exhibit the same level of performance. Raw numbers (in nanoseconds) fluctuate a lot, but I don't see anything concerning:

This PR
-------
2,611,025
1,988,893
2,878,981
1,873,220

main branch (#74786)
----
1,973,071
2,542,073
1,509,624

Initial implementation (#68705)
------
2,029,233
2,477,041
1,315,462

Cool. Thanks for confirming that.

@labath labath merged commit e7c042f into llvm:main Apr 19, 2024
@labath labath deleted the nested_type branch April 19, 2024 12:16
aniplcc pushed a commit to aniplcc/llvm-project that referenced this pull request Apr 21, 2024
…lvm#89183)

The types we get out of expressions will not have an associated symbol
file, so the current method of looking up the type will fail. Instead, I
plumb the query through the TypeSystem class. This correctly finds the
type in both cases (importing it into the expression AST if needed). I
haven't measured, but it should also be more efficient than doing a type
lookup (at least, after the type has already been found once).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants