Skip to content

[LLDB] Fix GetIndexOfChildMemberWithName to handle anonymous structs. #138487

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 3 commits into from
May 8, 2025

Conversation

cmtice
Copy link
Contributor

@cmtice cmtice commented May 5, 2025

When handling anonymous structs, GetIndexOfChildMemberWithName needs to add the number of non-empty base classes to the child index, to get the actual correct index. It was not doing so. This fixes that.

When handling anonymous structs, GetIndexOfChildMemberWithName needs
to add the number of non-empty base classes to the child index, to get
the actual correct index. It was not doing so. This fixes that.
@cmtice cmtice requested a review from JDevlieghere as a code owner May 5, 2025 06:46
@llvmbot llvmbot added the lldb label May 5, 2025
@cmtice cmtice requested a review from labath May 5, 2025 06:46
@llvmbot
Copy link
Member

llvmbot commented May 5, 2025

@llvm/pr-subscribers-lldb

Author: None (cmtice)

Changes

When handling anonymous structs, GetIndexOfChildMemberWithName needs to add the number of non-empty base classes to the child index, to get the actual correct index. It was not doing so. This fixes that.


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

4 Files Affected:

  • (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+8-1)
  • (added) lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile (+3)
  • (added) lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py (+27)
  • (added) lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp (+33)
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 1a2b3d4133e51..dd8d144510056 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -6743,7 +6743,14 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
           if (field_name.empty()) {
             CompilerType field_type = GetType(field->getType());
             std::vector<uint32_t> save_indices = child_indexes;
-            child_indexes.push_back(child_idx);
+            if (field_type.IsAnonymousType())
+              // We have to add on the number of non-empty base classes to this
+              // index!
+              child_indexes.push_back(
+                  child_idx +
+                  TypeSystemClang::GetNumBaseClasses(cxx_record_decl, true));
+            else
+              child_indexes.push_back(child_idx);
             if (field_type.GetIndexOfChildMemberWithName(
                     name, omit_empty_base_classes, child_indexes))
               return child_indexes.size();
diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py
new file mode 100644
index 0000000000000..2295ecfe81ff7
--- /dev/null
+++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py
@@ -0,0 +1,27 @@
+"""
+Test that we properly print multiple types.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import decorators
+
+
+class TestTypeLookupAnonStruct(TestBase):
+    def test_lookup_anon_struct(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, '// Set breakpoint here', lldb.SBFileSpec('main.cpp')
+        )
+
+        self.expect_var_path('unnamed_derived.y', value='2')
+        self.expect_var_path('unnamed_derived.z', value='13')
+        self.expect('frame variable "derb.x"', error=True,
+                    substrs=['"x" is not a member of "(DerivedB) derb"'])
+        self.expect('frame variable "derb.y"', error=True,
+                    substrs=['"y" is not a member of "(DerivedB) derb"'])
+        self.expect_var_path('derb.w', value='14')
+        self.expect_var_path('derb.k', value='15')
+        self.expect_var_path('derb.a.x', value='1')
+        self.expect_var_path('derb.a.y', value='2')
diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp
new file mode 100644
index 0000000000000..18dd997ea9563
--- /dev/null
+++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp
@@ -0,0 +1,33 @@
+int main(int argc, char** argv) {
+  struct A {
+    struct {
+      int x = 1;
+    };
+    int y = 2;
+  } a;
+
+   struct B {
+    // Anonymous struct inherits another struct.
+    struct : public A {
+      int z = 3;
+    };
+    int w = 4;
+    A a;
+  } b;
+
+   struct : public A {
+     struct {
+       int z = 13;
+     };
+   } unnamed_derived;
+
+   struct DerivedB : public B {
+     struct {
+       // `w` in anonymous struct overrides `w` from `B`.
+       int w = 14;
+       int k = 15;
+     };
+   } derb;
+
+   return 0; // Set breakpoint here
+}

Copy link

github-actions bot commented May 5, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
View the diff from clang-format here.
diff --git a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp
index a9288e646..6bb2000e3 100644
--- a/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp
+++ b/lldb/test/API/lang/cpp/type_lookup_anon_struct/main.cpp
@@ -15,9 +15,7 @@ int main(int argc, char **argv) {
     A a;
   } b;
 
-
-  struct EmptyBase {
-  };
+  struct EmptyBase {};
 
   struct : public A {
     struct {

Copy link

github-actions bot commented May 5, 2025

⚠️ Python code formatter, darker found issues in your code. ⚠️

You can test this locally with the following command:
darker --check --diff -r HEAD~1...HEAD lldb/test/API/lang/cpp/type_lookup_anon_struct/TestCppTypeLookupAnonStruct.py
View the diff from darker here.
--- TestCppTypeLookupAnonStruct.py	2025-05-08 03:34:04.000000 +0000
+++ TestCppTypeLookupAnonStruct.py	2025-05-08 03:37:33.205966 +0000
@@ -18,16 +18,16 @@
         self.expect_var_path("unnamed_derived.y", value="2")
         self.expect_var_path("unnamed_derived.z", value="13")
         self.expect(
             'frame variable "derb.x"',
             error=True,
-            substrs=['"x" is not a member of "(DerivedB) derb"']
+            substrs=['"x" is not a member of "(DerivedB) derb"'],
         )
         self.expect(
             'frame variable "derb.y"',
             error=True,
-            substrs=['"y" is not a member of "(DerivedB) derb"']
+            substrs=['"y" is not a member of "(DerivedB) derb"'],
         )
         self.expect_var_path("derb.w", value="14")
         self.expect_var_path("derb.k", value="15")
         self.expect_var_path("derb.a.x", value="1")
         self.expect_var_path("derb.a.y", value="2")

@labath labath requested a review from Michael137 May 5, 2025 08:31
@labath
Copy link
Collaborator

labath commented May 5, 2025

I'm not intimately familiar with this code, but I am somewhat suspicious of implementation.

Adding the number of base classes to the result makes sense to me. What surprises me is that this should be done only for fields with anonymous types. Can you explain why should the index of a field depend on whether its type is anonymous or not?

I am also surprised by the hardcoding of omit_empty_base_classes to true in the GetNumBaseClasses call. All of the other calls in this function pass the value from the argument. Why should this one be special?

@cmtice
Copy link
Contributor Author

cmtice commented May 6, 2025

I'm not intimately familiar with this code, but I am somewhat suspicious of implementation.

Adding the number of base classes to the result makes sense to me. What surprises me is that this should be done only for fields with anonymous types. Can you explain why should the index of a field depend on whether its type is anonymous or not?

I knew that I needed to add it for anonymous structs; I wasn't sure about other cases. I've tested it more now, and it seems good to add it in all cases, so I will do that.

I am also surprised by the hardcoding of omit_empty_base_classes to true in the GetNumBaseClasses call. All of the other calls in this function pass the value from the argument. Why should this one be special?

I knew that in the case of anonymous structs we did not want to count empty base classes; since this line was for anonymous structs, I hard-coded it to make sure. Now that I've updated the code to always add the number of base classes, I'm removing that hard-coding.

structs). Remove hard-codimg for omitting empty base classes.
Copy link
Collaborator

@labath labath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good now. Thanks. I'm surprised that something like derb.x doesn't work -- both for "frame var" and in C++. You're not changing that, so it bears no relation to this patch, but overall, but overall, I think it wouldn't be unreasonable to make these kinds of expressions work in "frame var".


struct DerivedB : public B {
struct {
// `w` in anonymous struct overrides `w` from `B`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the precise term is "shadows"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that.

Comment on lines 6746 to 6747
// We have to add on the number of non-empty base classes to this
// index!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is redundant. The non-empty base classes is no longer true now that we're forwarding omit_empty_base_classes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the comment

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some day if we could somehow move all of this into Clang, that'd make me happy :)

@@ -0,0 +1,33 @@
int main(int argc, char** argv) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test that involves empty base classes?

@cmtice cmtice merged commit 92d3029 into llvm:main May 8, 2025
5 of 9 checks passed
petrhosek pushed a commit to petrhosek/llvm-project that referenced this pull request May 8, 2025
…llvm#138487)

When handling anonymous structs, GetIndexOfChildMemberWithName needs to
add the number of non-empty base classes to the child index, to get the
actual correct index. It was not doing so. This fixes that.
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