[Lldb-commits] [PATCH] D67803: [lldb] Fix that importing decls in a TagDecl end up in wrong declaration context (partly reverts D61333)

Raphael Isemann via Phabricator via lldb-commits lldb-commits at lists.llvm.org
Fri Sep 20 00:30:28 PDT 2019


teemperor created this revision.
teemperor added reviewers: friss, martong.
Herald added subscribers: lldb-commits, JDevlieghere, abidh, christof, rnkovacs, aprantl.
Herald added a project: LLDB.

In D61333 <https://reviews.llvm.org/D61333> we dropped some code from ClangASTSource that checks if imported declarations
ended up in the right DeclContext. While this code wasn't tested by the test suite (or better, it was hit
by the test suite but we didn't have any checks that were affected) and the code seems pointless
(as usually Decls should end up in the right DeclContext), it actually broke the data formatters in LLDB
and causes a bunch of obscure bugs where structs suddenly miss all their members. The first report we got about
this was that printing a std::map doesn't work anymore when simply doing "expr m" (m is the std::map).

This patch reverts D61333 <https://reviews.llvm.org/D61333> partly and reintroduces the check in a more stricter way (we actually check now that
we *move* the Decl and it is in a single DeclContext). This should fix all the problems we currently have until
we figure out how to properly fix the underlying issues. I changed the order of some std::map formatter tests
which is currently the most reliable way to test this problem (it's a tricky setup, see description below).

Fixes rdar://55502701 and rdar://55129537

--------------------------------------

Some more explanation what is actually going on and what is going wrong:

The situation we have is that if we have a `std::map m` and do a `expr m`, we end up seeing an empty map
(even if `m` has elements). The reason for this is that our data formatter sees that std::pair<int, int> has no
members. However, `frame var m` works just fine (and fixes all following `expr m` calls).

The reason for why `expr` breaks std::map is that we actually copy the std::map nodes in two steps in the
three ASTContexts that are involved: The debug information ASTContext (D-AST), the expression ASTContext
we created for the current expression (E-AST) and the persistent ASTContext we use for our $variables (P-AST).

When doing `expr m` we do a minimal import of `std::map` from D-AST to E-AST just do the type checking/codegen.
This copies std::map itself and does a minimal.import of `std::pair<int, int>` (that is, we don't actually import
the `first` and `second` members as we don't need them for anything). After the expression is done, we take
the expression result and copy it from E-AST to P-AST. This imports the E-AST's `std::pair` into P-AST which still
has no `first` and `second` as they are still undeserialized. Once we are in P-AST, the data formatter tries to
inspect `std::map` (and also `std::pair` as that's what the elements are) and it asks for the `std::pair` members.
We see that `std::pair` has undeserialized members and go to the ExternalASTSource to ask for them. However,
P-ASTs ExternalASTSource points to D-AST (and not E-AST, which `std::pair` came from). It can't point to E-AST
as that is only temporary and already gone (and also doesn't actually contain all decls we have in P-AST).

So we go to D-AST to get the `std::pair` members. The ASTImporter is asked to copy over `std::pair` members
and first checks if `std::pair` is already in P-AST. However, it only finds the std::pair we got from E-AST, so it
can't use it's map of already imported declarations and does a comparison between the `std::pair` decls we have
Because the ASTImporter thinks they are different declarations, it creates a second `std::pair` and fills in the
members `first` and `second` into the second `std::pair`. However, the data formatter is looking at the first
`std::pair` which still has no members as they are in the other decl. Now we pretend we have no declarations
and just print an empty map as a fallback.

The hack we had before fixed this issue by moving `first` and `second` to the first declaration which makes
the formatters happy as they can now see the members in the DeclContext they are querying.

Obviously this is a temporary patch until we get a real fix but I'm not sure what's the best way to fix this.
Implementing that the ClassTemplateSpecializationDecl actually understands that the two std::pair's are the same
decl fixes the issue, but this doesn't fix the bug for all declarations. My preferred solution would be to
complete all declarations in E-AST before they get moved to P-AST (as we anyway have to do this from what I can
tell), but that might have unintended side-effects and not sure what's the best way to implement this.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D67803

Files:
  lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py
  lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp


Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
@@ -650,6 +650,20 @@
 
         m_ast_importer_sp->RequireCompleteType(copied_field_type);
       }
+      auto decl_context_non_const = const_cast<DeclContext *>(decl_context);
+
+      // The decl ended up in the wrong DeclContext. Let's fix that so
+      // the decl we copied will actually be found.
+      // FIXME: This is a horrible hack that shouldn't be necessary. However
+      // it seems our current setup sometimes fails to copy decls to the right
+      // place. See rdar://55129537.
+      if (copied_decl->getDeclContext() != decl_context) {
+        assert(copied_decl->getDeclContext()->containsDecl(copied_decl));
+        copied_decl->getDeclContext()->removeDecl(copied_decl);
+        copied_decl->setDeclContext(decl_context_non_const);
+        assert(!decl_context_non_const->containsDecl(copied_decl));
+        decl_context_non_const->addDeclInternal(copied_decl);
+      }
     } else {
       SkippedDecls = true;
     }
Index: lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py
+++ lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py
@@ -52,13 +52,26 @@
         self.addTearDownHook(cleanup)
 
         ns = self.namespace
-        self.expect('frame variable ii',
+        self.expect('p ii',
+                    substrs=['%s::map' % ns,
+                             'size=0',
+                             '{}'])
+        self.expect('frame var ii',
                     substrs=['%s::map' % ns,
                              'size=0',
                              '{}'])
 
         lldbutil.continue_to_breakpoint(self.process(), bkpt)
 
+        self.expect('p ii',
+                    substrs=['%s::map' % ns, 'size=2',
+                             '[0] = ',
+                             'first = 0',
+                             'second = 0',
+                             '[1] = ',
+                             'first = 1',
+                             'second = 1'])
+
         self.expect('frame variable ii',
                     substrs=['%s::map' % ns, 'size=2',
                              '[0] = ',
@@ -81,7 +94,7 @@
 
         lldbutil.continue_to_breakpoint(self.process(), bkpt)
 
-        self.expect("frame variable ii",
+        self.expect("p ii",
                     substrs=['%s::map' % ns, 'size=8',
                              '[5] = ',
                              'first = 5',
@@ -90,7 +103,7 @@
                              'first = 7',
                              'second = 1'])
 
-        self.expect("p ii",
+        self.expect("frame var ii",
                     substrs=['%s::map' % ns, 'size=8',
                              '[5] = ',
                              'first = 5',


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D67803.220956.patch
Type: text/x-patch
Size: 3283 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/lldb-commits/attachments/20190920/ce9f0ffe/attachment-0001.bin>


More information about the lldb-commits mailing list