Index: lldb/include/lldb/Symbol/ClangASTContext.h =================================================================== --- lldb/include/lldb/Symbol/ClangASTContext.h +++ lldb/include/lldb/Symbol/ClangASTContext.h @@ -110,6 +110,9 @@ clang::TargetInfo *getTargetInfo(); + void setSema(clang::Sema *s); + clang::Sema *getSema() { return m_sema; } + void Clear(); const char *GetTargetTriple(); @@ -1053,6 +1056,10 @@ uint32_t m_pointer_byte_size; bool m_ast_owned; bool m_can_evaluate_expressions; + // The sema associated that is currently used to build this ASTContext. + // May be null if we are already done parsing this ASTContext or the + // ASTContext wasn't created by parsing source code. + clang::Sema * m_sema = nullptr; // clang-format on private: //------------------------------------------------------------------ Index: lldb/include/lldb/Symbol/ClangASTImporter.h =================================================================== --- lldb/include/lldb/Symbol/ClangASTImporter.h +++ lldb/include/lldb/Symbol/ClangASTImporter.h @@ -23,6 +23,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Symbol/CompilerDeclContext.h" +#include "lldb/Symbol/StdModuleHandler.h" #include "lldb/lldb-types.h" #include "llvm/ADT/DenseMap.h" @@ -243,6 +244,32 @@ m_decls_to_deport(nullptr), m_decls_already_deported(nullptr), m_master(master), m_source_ctx(source_ctx) {} + struct StdModuleScope { + StdModuleHandler m_handler; + Minion &m_minion; + bool m_valid = false; + StdModuleScope(Minion &minion, clang::ASTContext *dst_ctx) + : m_minion(minion) { + // If the minion doesn't have a StdModuleHandler yet, place one. + if (!minion.m_std_handler) { + m_handler = StdModuleHandler(minion, dst_ctx); + m_valid = true; + minion.m_std_handler = &m_handler; + } + } + ~StdModuleScope() { + if (m_valid) { + // Make sure no one messed with the handler we placed. + assert(m_minion.m_std_handler == &m_handler); + m_minion.m_std_handler = nullptr; + } + } + }; + + protected: + llvm::Expected ImportInternal(clang::Decl *From) override; + + public: // A call to "InitDeportWorkQueues" puts the minion into deport mode. // In deport mode, every copied Decl that could require completion is // recorded and placed into the decls_to_deport set. @@ -266,10 +293,16 @@ clang::Decl *GetOriginalDecl(clang::Decl *To) override; + /// Decls we should ignore when mapping decls back to their original + /// ASTContext. Used by the StdModuleHandler to mark declarations that + /// were created from the 'std' C++ module to prevent that the Importer + /// tries to sync them with the broken equivalent in the debug info AST. + std::set m_decls_to_ignore; std::set *m_decls_to_deport; std::set *m_decls_already_deported; ClangASTImporter &m_master; clang::ASTContext *m_source_ctx; + StdModuleHandler *m_std_handler = nullptr; }; typedef std::shared_ptr MinionSP; Index: lldb/include/lldb/Symbol/StdModuleHandler.h =================================================================== --- /dev/null +++ lldb/include/lldb/Symbol/StdModuleHandler.h @@ -0,0 +1,49 @@ +//===-- StdModuleHandler.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StdModuleHandler_h_ +#define liblldb_StdModuleHandler_h_ + +#include "clang/AST/ASTImporter.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/StringSet.h" + +namespace lldb_private { + +/// Handles importing declarations that are also in the 'std' C++ module. +/// +/// In general this class expects that the target ASTContext of our import +/// process is setup to use C++ modules and has the 'std' module attached. +class StdModuleHandler { + clang::ASTImporter *m_importer = nullptr; + + clang::Sema *m_sema = nullptr; + /// List of template names this class currently supports. These are the + /// template names inside the 'std' namespace such as 'vector' or 'list'. + llvm::StringSet<> m_supported_templates; + + /// Tries to manually instantiate the given foreign declaration in the target + /// context (designated by m_sema). + llvm::Optional tryInstantiateStdTemplate(clang::Decl *d); + +public: + StdModuleHandler() = default; + StdModuleHandler(clang::ASTImporter &importer, clang::ASTContext *target); + + /// Attempts to import the given decl into the target ASTContext by + /// deserializing it from the 'std' module. This function returns a decl if a + /// decl has been deserialized from the 'std' module. Otherwise this function + /// returns nothing. + llvm::Optional Import(clang::Decl *d); + + bool isValid() const { return m_sema != nullptr; } +}; + +} // namespace lldb_private + +#endif // liblldb_StdModuleHandler_h_ Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-basic/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-basic/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-basic/TestBasicDeque.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-basic/TestBasicDeque.py @@ -0,0 +1,41 @@ +""" +Test basic std::list functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBasicDeque(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front()", substrs=['(int) $1 = 3']) + self.expect("expr (int)a.back()", substrs=['(int) $2 = 2']) + + self.expect("expr std::sort(a.begin(), a.end())") + self.expect("expr (int)a.front()", substrs=['(int) $3 = 1']) + self.expect("expr (int)a.back()", substrs=['(int) $4 = 3']) + + self.expect("expr std::reverse(a.begin(), a.end())") + self.expect("expr (int)a.front()", substrs=['(int) $5 = 3']) + self.expect("expr (int)a.back()", substrs=['(int) $6 = 1']) + + self.expect("expr (int)(*a.begin())", substrs=['(int) $7 = 3']) + self.expect("expr (int)(*a.rbegin())", substrs=['(int) $8 = 1']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-basic/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-basic/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + std::deque a = {3, 1, 2}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-dbg-info-content/TestDbgInfoContentDeque.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-dbg-info-content/TestDbgInfoContentDeque.py @@ -0,0 +1,37 @@ +""" +Test std::deque functionality with a decl from dbg info as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestDbgInfoContentDeque(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front()->a", substrs=['(int) $1 = 3']) + self.expect("expr (int)a.back()->a", substrs=['(int) $2 = 2']) + + self.expect("expr std::reverse(a.begin(), a.end())") + self.expect("expr (int)a.front()->a", substrs=['(int) $3 = 2']) + self.expect("expr (int)a.back()->a", substrs=['(int) $4 = 3']) + + self.expect("expr (int)(a.begin()->a)", substrs=['(int) $5 = 2']) + self.expect("expr (int)(a.rbegin()->a)", substrs=['(int) $6 = 3']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/deque-dbg-info-content/main.cpp @@ -0,0 +1,10 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::deque a = {{3}, {1}, {2}}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-basic/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-basic/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-basic/TestBasicForwardList.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-basic/TestBasicForwardList.py @@ -0,0 +1,34 @@ +""" +Test basic std::forward_list functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBasicList(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)std::distance(a.begin(), a.end())", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front()", substrs=['(int) $1 = 3']) + + self.expect("expr a.sort()") + self.expect("expr (int)a.front()", substrs=['(int) $2 = 1']) + + self.expect("expr (int)(*a.begin())", substrs=['(int) $3 = 1']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-basic/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-basic/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + std::forward_list a = {3, 1, 2}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardList.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardList.py @@ -0,0 +1,31 @@ +""" +Test std::forward_list functionality with a decl from debug info as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestDbgInfoContentForwardList(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)std::distance(a.begin(), a.end())", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front()->a", substrs=['(int) $1 = 3']) + + self.expect("expr (int)(a.begin()->a)", substrs=['(int) $2 = 3']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/forward_list-dbg-info-content/main.cpp @@ -0,0 +1,10 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::forward_list a = {{3}, {1}, {2}}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-basic/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-basic/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-basic/TestBasicList.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-basic/TestBasicList.py @@ -0,0 +1,41 @@ +""" +Test basic std::list functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBasicList(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front()", substrs=['(int) $1 = 3']) + self.expect("expr (int)a.back()", substrs=['(int) $2 = 2']) + + self.expect("expr a.sort()") + self.expect("expr (int)a.front()", substrs=['(int) $3 = 1']) + self.expect("expr (int)a.back()", substrs=['(int) $4 = 3']) + + self.expect("expr std::reverse(a.begin(), a.end())") + self.expect("expr (int)a.front()", substrs=['(int) $5 = 3']) + self.expect("expr (int)a.back()", substrs=['(int) $6 = 1']) + + self.expect("expr (int)(*a.begin())", substrs=['(int) $7 = 3']) + self.expect("expr (int)(*a.rbegin())", substrs=['(int) $8 = 1']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-basic/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-basic/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + std::list a = {3, 1, 2}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-dbg-info-content/TestDbgInfoContentList.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-dbg-info-content/TestDbgInfoContentList.py @@ -0,0 +1,38 @@ +""" +Test basic std::list functionality but with a declaration from +the debug info (the Foo struct) as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestDbgInfoContentList(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front().a", substrs=['(int) $1 = 3']) + self.expect("expr (int)a.back().a", substrs=['(int) $2 = 2']) + + self.expect("expr std::reverse(a.begin(), a.end())") + self.expect("expr (int)a.front().a", substrs=['(int) $3 = 2']) + self.expect("expr (int)a.back().a", substrs=['(int) $4 = 3']) + + self.expect("expr (int)(a.begin()->a)", substrs=['(int) $5 = 2']) + self.expect("expr (int)(a.rbegin()->a)", substrs=['(int) $6 = 3']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/list-dbg-info-content/main.cpp @@ -0,0 +1,10 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::list a = {{3}, {1}, {2}}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContent.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContent.py @@ -0,0 +1,33 @@ +""" +Test std::shared_ptr functionality with a class from debug info as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestSharedPtr(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (int)s->a", substrs=['(int) $0 = 3']) + self.expect("expr (int)(s->a = 5)", substrs=['(int) $1 = 5']) + self.expect("expr (int)s->a", substrs=['(int) $2 = 5']) + self.expect("expr (bool)s", substrs=['(bool) $3 = true']) + self.expect("expr s.reset()") + self.expect("expr (bool)s", substrs=['(bool) $4 = false']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr-dbg-info-content/main.cpp @@ -0,0 +1,11 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::shared_ptr s(new Foo); + s->a = 3; + return s->a; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr/TestSharedPtr.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr/TestSharedPtr.py @@ -0,0 +1,33 @@ +""" +Test basic std::shared_ptr functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestSharedPtr(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (int)*s", substrs=['(int) $0 = 3']) + self.expect("expr (int)(*s = 5)", substrs=['(int) $1 = 5']) + self.expect("expr (int)*s", substrs=['(int) $2 = 5']) + self.expect("expr (bool)s", substrs=['(bool) $3 = true']) + self.expect("expr s.reset()") + self.expect("expr (bool)s", substrs=['(bool) $4 = false']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/shared_ptr/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) { + std::shared_ptr s(new int); + *s = 3; + return *s; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr-dbg-info-content/TestUniquePtrDbgInfoContent.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr-dbg-info-content/TestUniquePtrDbgInfoContent.py @@ -0,0 +1,33 @@ +""" +Test std::unique_ptr functionality with a decl from debug info as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestUniquePtrDbgInfoContent(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (int)s->a", substrs=['(int) $0 = 3']) + self.expect("expr (int)(s->a = 5)", substrs=['(int) $1 = 5']) + self.expect("expr (int)s->a", substrs=['(int) $2 = 5']) + self.expect("expr (bool)s", substrs=['(bool) $3 = true']) + self.expect("expr s.reset()") + self.expect("expr (bool)s", substrs=['(bool) $4 = false']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr-dbg-info-content/main.cpp @@ -0,0 +1,11 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::shared_ptr s(new Foo); + s->a = 3; + return s->a; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr/TestUniquePtr.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr/TestUniquePtr.py @@ -0,0 +1,33 @@ +""" +Test basic std::unique_ptr functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestUniquePtr(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (int)*s", substrs=['(int) $0 = 3']) + self.expect("expr (int)(*s = 5)", substrs=['(int) $1 = 5']) + self.expect("expr (int)*s", substrs=['(int) $2 = 5']) + self.expect("expr (bool)s", substrs=['(bool) $3 = true']) + self.expect("expr s.reset()") + self.expect("expr (bool)s", substrs=['(bool) $4 = false']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/unique_ptr/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) { + std::shared_ptr s(new int); + *s = 3; + return *s; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-basic/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-basic/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-basic/TestBasicVector.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-basic/TestBasicVector.py @@ -0,0 +1,57 @@ +""" +Test basic std::vector functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBasicVector(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front()", substrs=['(int) $1 = 3']) + self.expect("expr (int)a[1]", substrs=['(int) $2 = 1']) + self.expect("expr (int)a.back()", substrs=['(int) $3 = 2']) + + self.expect("expr std::sort(a.begin(), a.end())") + self.expect("expr (int)a.front()", substrs=['(int) $4 = 1']) + self.expect("expr (int)a[1]", substrs=['(int) $5 = 2']) + self.expect("expr (int)a.back()", substrs=['(int) $6 = 3']) + + self.expect("expr std::reverse(a.begin(), a.end())") + self.expect("expr (int)a.front()", substrs=['(int) $7 = 3']) + self.expect("expr (int)a[1]", substrs=['(int) $8 = 2']) + self.expect("expr (int)a.back()", substrs=['(int) $9 = 1']) + + self.expect("expr (int)(*a.begin())", substrs=['(int) $10 = 3']) + self.expect("expr (int)(*a.rbegin())", substrs=['(int) $11 = 1']) + + self.expect("expr a.pop_back()") + self.expect("expr (int)a.back()", substrs=['(int) $12 = 2']) + self.expect("expr (size_t)a.size()", substrs=['(size_t) $13 = 2']) + + self.expect("expr (int)a.at(0)", substrs=['(int) $14 = 3']) + + self.expect("expr a.push_back(4)") + self.expect("expr (int)a.back()", substrs=['(int) $15 = 4']) + self.expect("expr (size_t)a.size()", substrs=['(size_t) $16 = 3']) + + self.expect("expr a.emplace_back(5)") + self.expect("expr (int)a.back()", substrs=['(int) $17 = 5']) + self.expect("expr (size_t)a.size()", substrs=['(size_t) $18 = 4']) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-basic/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-basic/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + std::vector a = {3, 1, 2}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-bool/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-bool/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-bool/TestBoolVector.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-bool/TestBoolVector.py @@ -0,0 +1,34 @@ +""" +Test basic std::vector functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestBoolVector(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 4']) + self.expect("expr (bool)a.front()", substrs=['(bool) $1 = false']) + self.expect("expr (bool)a[1]", substrs=['(bool) $2 = true']) + self.expect("expr (bool)a.back()", substrs=['(bool) $3 = true']) + + self.expect("expr (bool)(*a.begin())", substrs=['(bool) $4 = false']) + self.expect("expr (bool)(*a.rbegin())", substrs=['(bool) $5 = true']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-bool/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-bool/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + std::vector a = {0, 1, 0, 1}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-dbg-info-content/TestDbgInfoContentVector.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-dbg-info-content/TestDbgInfoContentVector.py @@ -0,0 +1,47 @@ +""" +Test basic std::vector functionality but with a declaration from +the debug info (the Foo struct) as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestDbgInfoContentVector(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 3']) + self.expect("expr (int)a.front().a", substrs=['(int) $1 = 3']) + self.expect("expr (int)a[1].a", substrs=['(int) $2 = 1']) + self.expect("expr (int)a.back().a", substrs=['(int) $3 = 2']) + + self.expect("expr std::reverse(a.begin(), a.end())") + self.expect("expr (int)a.front().a", substrs=['(int) $4 = 2']) + + self.expect("expr (int)(a.begin()->a)", substrs=['(int) $5 = 2']) + self.expect("expr (int)(a.rbegin()->a)", substrs=['(int) $6 = 3']) + + self.expect("expr a.pop_back()") + self.expect("expr (int)a.back().a", substrs=['(int) $7 = 1']) + self.expect("expr (size_t)a.size()", substrs=['(size_t) $8 = 2']) + + self.expect("expr (int)a.at(0).a", substrs=['(int) $9 = 2']) + + self.expect("expr a.push_back({4})") + self.expect("expr (int)a.back().a", substrs=['(int) $10 = 4']) + self.expect("expr (size_t)a.size()", substrs=['(size_t) $11 = 3']) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-dbg-info-content/main.cpp @@ -0,0 +1,10 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::vector a = {{3}, {1}, {2}}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-of-vectors/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-of-vectors/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-of-vectors/TestVectorOfVectors.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-of-vectors/TestVectorOfVectors.py @@ -0,0 +1,30 @@ +""" +Test std::vector functionality when it's contents are vectors. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestVectorOfVectors(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (size_t)a.size()", substrs=['(size_t) $0 = 2']) + self.expect("expr (int)a.front().front()", substrs=['(int) $1 = 1']) + self.expect("expr (int)a[1][1]", substrs=['(int) $2 = 2']) + self.expect("expr (int)a.back().at(0)", substrs=['(int) $3 = 3']) Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-of-vectors/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/vector-of-vectors/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + std::vector > a = {{1, 2, 3}, {3, 2, 1}}; + return 0; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr-dbg-info-content/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr-dbg-info-content/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtr.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtr.py @@ -0,0 +1,33 @@ +""" +Test std::weak_ptr functionality with a decl from debug info as content. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestDbgInfoContentWeakPtr(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (int)w.lock()->a", substrs=['(int) $0 = 3']) + self.expect("expr (int)(w.lock()->a = 5)", substrs=['(int) $1 = 5']) + self.expect("expr (int)w.lock()->a", substrs=['(int) $2 = 5']) + self.expect("expr w.use_count()", substrs=['(long) $3 = 1']) + self.expect("expr w.reset()") + self.expect("expr w.use_count()", substrs=['(long) $4 = 0']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr-dbg-info-content/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr-dbg-info-content/main.cpp @@ -0,0 +1,12 @@ +#include + +struct Foo { + int a; +}; + +int main(int argc, char **argv) { + std::shared_ptr s(new Foo); + s->a = 3; + std::weak_ptr w = s; + return s->a; // Set break point at this line. +} Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make +USE_LIBCPP := 1 +CXXFLAGS += $(MANDATORY_CXXMODULE_BUILD_CFLAGS) +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr/TestWeakPtr.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr/TestWeakPtr.py @@ -0,0 +1,33 @@ +""" +Test basic std::weak_ptr functionality. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestSharedPtr(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # FIXME: This should work on more setups, so remove these + # skipIf's in the future. + @add_test_categories(["libc++"]) + @skipIf(compiler=no_match("clang")) + @skipIf(oslist=no_match(["linux"])) + @skipIf(debug_info=no_match(["dwarf"])) + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set break point at this line.", lldb.SBFileSpec("main.cpp")) + + self.runCmd("settings set target.import-std-module true") + + self.expect("expr (int)*w.lock()", substrs=['(int) $0 = 3']) + self.expect("expr (int)(*w.lock() = 5)", substrs=['(int) $1 = 5']) + self.expect("expr (int)*w.lock()", substrs=['(int) $2 = 5']) + self.expect("expr w.use_count()", substrs=['(long) $3 = 1']) + self.expect("expr w.reset()") + self.expect("expr w.use_count()", substrs=['(long) $4 = 0']) + Index: lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/expression_command/import-std-module/weak_ptr/main.cpp @@ -0,0 +1,8 @@ +#include + +int main(int argc, char **argv) { + std::shared_ptr s(new int); + *s = 3; + std::weak_ptr w = s; + return *s; // Set break point at this line. +} Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h @@ -217,6 +217,8 @@ //------------------------------------------------------------------ virtual void FindExternalVisibleDecls(NameSearchContext &context); + clang::Sema *getSema(); + void SetImportInProgress(bool import_in_progress) { m_import_in_progress = import_in_progress; } Index: lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -763,6 +763,10 @@ } } +clang::Sema *ClangASTSource::getSema() { + return ClangASTContext::GetASTContext(m_ast_context)->getSema(); +} + bool ClangASTSource::IgnoreName(const ConstString name, bool ignore_all_dollar_names) { static const ConstString id_name("id"); Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -970,8 +970,10 @@ *Consumer, TU_Complete, completion_consumer)); m_compiler->setASTConsumer(std::move(Consumer)); - if (ast_context.getLangOpts().Modules) + if (ast_context.getLangOpts().Modules) { m_compiler->createModuleManager(); + m_ast_context->setSema(&m_compiler->getSema()); + } ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap(); if (decl_map) { @@ -1008,6 +1010,10 @@ &m_compiler->getSema()); ParseAST(m_compiler->getSema(), false, false); } + + // Make sure we have no pointer to the Sema we are about to destroy. + if (ast_context.getLangOpts().Modules) + m_ast_context->setSema(nullptr); // Destroy the Sema. This is necessary because we want to emulate the // original behavior of ParseAST (which also destroys the Sema after parsing). m_compiler->setSema(nullptr); Index: lldb/source/Symbol/CMakeLists.txt =================================================================== --- lldb/source/Symbol/CMakeLists.txt +++ lldb/source/Symbol/CMakeLists.txt @@ -26,6 +26,7 @@ LineTable.cpp LocateSymbolFile.cpp ObjectFile.cpp + StdModuleHandler.cpp Symbol.cpp SymbolContext.cpp SymbolFile.cpp Index: lldb/source/Symbol/ClangASTContext.cpp =================================================================== --- lldb/source/Symbol/ClangASTContext.cpp +++ lldb/source/Symbol/ClangASTContext.cpp @@ -52,6 +52,7 @@ #include "clang/Basic/TargetOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/LangStandard.h" +#include "clang/Sema/Sema.h" #ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG #undef NDEBUG @@ -794,6 +795,12 @@ m_pointer_byte_size = 0; } +void ClangASTContext::setSema(Sema *s) { + // Ensure that the new sema actually belongs to our ASTContext. + assert(s == nullptr || &s->getASTContext() == m_ast_up.get()); + m_sema = s; +} + const char *ClangASTContext::GetTargetTriple() { return m_target_triple.c_str(); } Index: lldb/source/Symbol/ClangASTImporter.cpp =================================================================== --- lldb/source/Symbol/ClangASTImporter.cpp +++ lldb/source/Symbol/ClangASTImporter.cpp @@ -16,6 +16,8 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" #include "llvm/Support/raw_ostream.h" #include @@ -58,6 +60,8 @@ clang::QualType type) { MinionSP minion_sp(GetMinion(dst_ast, src_ast)); + Minion::StdModuleScope std_scope(*minion_sp, dst_ast); + if (minion_sp) return minion_sp->Import(type); @@ -99,6 +103,8 @@ minion_sp = GetMinion(dst_ast, src_ast); + Minion::StdModuleScope std_scope(*minion_sp, dst_ast); + if (minion_sp) { clang::Decl *result = minion_sp->Import(decl); @@ -557,6 +563,7 @@ MinionSP minion_sp(GetMinion(&decl->getASTContext(), decl_origin.ctx)); + Minion::StdModuleScope std_scope(*minion_sp, &decl->getASTContext()); if (minion_sp) minion_sp->ImportDefinitionTo(decl, decl_origin.decl); @@ -624,6 +631,8 @@ MinionSP minion_sp(GetMinion(&tag_decl->getASTContext(), decl_origin.ctx)); + Minion::StdModuleScope std_scope(*minion_sp, &tag_decl->getASTContext()); + TagDecl *origin_tag_decl = llvm::dyn_cast(decl_origin.decl); for (Decl *origin_child_decl : origin_tag_decl->decls()) { @@ -816,6 +825,23 @@ ClangASTImporter::MapCompleter::~MapCompleter() { return; } +llvm::Expected ClangASTImporter::Minion::ImportInternal(Decl *From) { + if (m_std_handler) { + llvm::Optional D = m_std_handler->Import(From); + if (D) { + // Make sure we don't use this decl later to map it back to it's original + // decl. The decl the StdModuleHandler created has nothing to do with + // the one from debug info, and linking those two would just cause the + // ASTImporter to try 'updating' the module decl with the minimal one from + // the debug info. + m_decls_to_ignore.insert(*D); + return *D; + } + } + + return ASTImporter::ImportInternal(From); +} + void ClangASTImporter::Minion::InitDeportWorkQueues( std::set *decls_to_deport, std::set *decls_already_deported) { @@ -943,6 +969,11 @@ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + // Some decls shouldn't be tracked here because they were not created by + // copying 'from' to 'to'. Just exit early for those. + if (m_decls_to_ignore.find(to) != m_decls_to_ignore.end()) + return clang::ASTImporter::Imported(from, to); + lldb::user_id_t user_id = LLDB_INVALID_UID; ClangASTMetadata *metadata = m_master.GetDeclMetadata(from); if (metadata) Index: lldb/source/Symbol/StdModuleHandler.cpp =================================================================== --- /dev/null +++ lldb/source/Symbol/StdModuleHandler.cpp @@ -0,0 +1,262 @@ +//===-- StdModuleHandler.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/StdModuleHandler.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "clang/Sema/Lookup.h" +#include "llvm/Support/Error.h" + +using namespace lldb_private; +using namespace clang; + +StdModuleHandler::StdModuleHandler(ASTImporter &importer, ASTContext *target) + : m_importer(&importer), + m_sema(ClangASTContext::GetASTContext(target)->getSema()) { + + std::initializer_list supported_names = { + // containers + "vector", + "list", + "forward_list", + "deque", + // pointers + "shared_ptr", + "unique_ptr", + "weak_ptr", + // utility + "allocator", + }; + m_supported_templates.insert(supported_names.begin(), supported_names.end()); +} + +/// Builds a list of scopes that point into the given context. +/// +/// \param sema The sema that will be using the scopes. +/// \param ctxt The context that the scope should look into. +/// \param result A list of scopes. The scopes need to be freed by the caller +/// (except the TUScope which is owned by the sema). +static void makeScopes(Sema &sema, DeclContext *ctxt, + std::vector &result) { + // FIXME: The result should be a list of unique_ptrs, but the TUScope makes + // this currently impossible as its owned by the Sema. + + if (auto parent = ctxt->getParent()) { + makeScopes(sema, parent, result); + + Scope *scope = + new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics()); + scope->setEntity(ctxt); + result.push_back(scope); + } else + result.push_back(sema.TUScope); +} + +/// Uses the Sema to look up the given name in the given DeclContext. +static std::unique_ptr +emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) { + IdentifierInfo &ident = sema.getASTContext().Idents.get(name); + + std::unique_ptr lookup_result; + lookup_result.reset(new LookupResult(sema, DeclarationName(&ident), + SourceLocation(), + Sema::LookupOrdinaryName)); + + // Usually during parsing we already encountered the scopes we would use. But + // here don't have these scopes so we have to emulate the behavior of the + // Sema during parsing. + std::vector scopes; + makeScopes(sema, ctxt, scopes); + + // Now actually perform the lookup with the sema. + sema.LookupName(*lookup_result, scopes.back()); + + // Delete all the allocated scopes beside the translation unit scope (which + // has depth 0). + for (Scope *s : scopes) + if (s->getDepth() != 0) + delete s; + + return lookup_result; +} + +/// Error class for handling problems when finding a certain DeclContext. +struct MissingDeclContext : public llvm::ErrorInfo { + + static char ID; + + MissingDeclContext(DeclContext *context, std::string error) + : m_context(context), m_error(error) {} + + DeclContext *m_context; + std::string m_error; + + void log(llvm::raw_ostream &OS) const override { + OS << llvm::formatv("error when reconstructing context of kind {0}:{1}", + m_context->getDeclKindName(), m_error); + } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } +}; + +char MissingDeclContext::ID = 0; + +/// Given a foreign decl context, this function finds the equivalent local +/// decl context in the ASTContext of the given Sema. Potentially deserializes +/// decls from the 'std' module if necessary. +static llvm::Expected +getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) { + + // Inline namespaces don't matter for lookups, so let's skip them. + while (foreign_ctxt && foreign_ctxt->isInlineNamespace()) + foreign_ctxt = foreign_ctxt->getParent(); + + // If the foreign context is the TU, we just return the local TU. + if (foreign_ctxt->isTranslationUnit()) + return sema.getASTContext().getTranslationUnitDecl(); + + // Recursively find/build the parent DeclContext. + llvm::Expected parent = + getEqualLocalDeclContext(sema, foreign_ctxt->getParent()); + if (!parent) + return parent; + + // We currently only support building namespaces. + if (foreign_ctxt->isNamespace()) { + NamedDecl *ns = llvm::dyn_cast(foreign_ctxt); + llvm::StringRef ns_name = ns->getName(); + + auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent); + for (NamedDecl *named_decl : *lookup_result) { + if (DeclContext *DC = llvm::dyn_cast(named_decl)) + return DC->getPrimaryContext(); + } + return llvm::make_error( + foreign_ctxt, + "Couldn't find namespace " + ns->getQualifiedNameAsString()); + } + + return llvm::make_error(foreign_ctxt, "Unknown context "); +} + +/// Returns true iff tryInstantiateStdTemplate supports instantiating a template +/// with the given template arguments. +static bool templateArgsAreSupported(ArrayRef a) { + for (const TemplateArgument &arg : a) { + switch (arg.getKind()) { + case TemplateArgument::Type: + case TemplateArgument::Integral: + break; + default: + // TemplateArgument kind hasn't been handled yet. + return false; + } + } + return true; +} + +llvm::Optional StdModuleHandler::tryInstantiateStdTemplate(Decl *d) { + // If we don't have a template to instiantiate, then there is nothing to do. + auto td = dyn_cast(d); + if (!td) + return {}; + + // We only care about templates in the std namespace. + if (!td->getDeclContext()->isStdNamespace()) + return {}; + + // We have a whitelist of supported template names. + if (m_supported_templates.find(td->getName()) == m_supported_templates.end()) + return {}; + + // Early check if we even support instantiating this template. We do this + // before we import anything into the target AST. + auto &foreign_args = td->getTemplateInstantiationArgs(); + if (!templateArgsAreSupported(foreign_args.asArray())) + return {}; + + // Find the local DeclContext that corresponds to the DeclContext of our + // decl we want to import. + auto to_context = getEqualLocalDeclContext(*m_sema, td->getDeclContext()); + if (!to_context) + return {}; + + // Look up the template in our local context. + std::unique_ptr lookup = + emulateLookupInCtxt(*m_sema, td->getName(), *to_context); + + ClassTemplateDecl *new_class_template = nullptr; + for (auto LD : *lookup) { + if ((new_class_template = dyn_cast(LD))) + break; + } + if (!new_class_template) + return {}; + + // Import the foreign template arguments. + llvm::SmallVector imported_args; + + // If this logic is changed, also update templateArgsAreSupported. + for (const TemplateArgument &arg : foreign_args.asArray()) { + switch (arg.getKind()) { + case TemplateArgument::Type: { + llvm::Expected type = m_importer->Import_New(arg.getAsType()); + if (!type) + return {}; + imported_args.push_back(TemplateArgument(*type)); + break; + } + case TemplateArgument::Integral: { + llvm::APSInt integral = arg.getAsIntegral(); + llvm::Expected type = m_importer->Import(arg.getIntegralType()); + if (!type) + return {}; + imported_args.push_back( + TemplateArgument(d->getASTContext(), integral, *type)); + break; + } + default: + assert(false && "templateArgsAreSupported not updated?"); + } + } + + // Find the class template specialization declaration that + // corresponds to these arguments. + void *InsertPos = nullptr; + ClassTemplateSpecializationDecl *found_decl = + new_class_template->findSpecialization(imported_args, InsertPos); + if (!found_decl) { + // Instantiate the template. + found_decl = ClassTemplateSpecializationDecl::Create( + m_sema->getASTContext(), + new_class_template->getTemplatedDecl()->getTagKind(), + new_class_template->getDeclContext(), + new_class_template->getTemplatedDecl()->getLocation(), + new_class_template->getLocation(), new_class_template, imported_args, + nullptr); + new_class_template->AddSpecialization(found_decl, InsertPos); + new_class_template->dumpColor(); + if (new_class_template->isOutOfLine()) + found_decl->setLexicalDeclContext( + new_class_template->getLexicalDeclContext()); + } + + if (found_decl) + return found_decl; + + return {}; +} + +llvm::Optional StdModuleHandler::Import(Decl *d) { + if (!isValid()) + return {}; + + return tryInstantiateStdTemplate(d); +}