teemperor created this revision. teemperor added reviewers: martong, balazske, a.sidorin, shafik. Herald added subscribers: cfe-commits, rnkovacs. Herald added a project: clang.
We are currently implementing support in LLDB that reconstructs the STL templates from the target program in the expression evaluator. This reconstruction happens during the import process from our debug info AST into the expression evaluation AST[1], which means we need a way to intercept the ASTImporter import process in some controlled fashion. This patch adds a shim to the ASTImporter that allows us to intercept any Import calls an ASTImporter may make (either externally or while importing internally). We then provide a shim in LLDB that does do our template reconstruction. The shim is on purpose not reusing some ASTImporter functionalities like the map of already imported decls because we want to keep it flexible. If a Shim wants to use this functionality it needs to update/check the map itself. [1] The reason for this is that we can't reliable import templates with the ASTImporter, so actually reconstructing the template in the debug info AST and then importing it doesn't work. It would also be much slower. Repository: rC Clang https://reviews.llvm.org/D59485 Files: clang/include/clang/AST/ASTImporter.h clang/lib/AST/ASTImporter.cpp clang/unittests/AST/ASTImporterTest.cpp
Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -316,11 +316,13 @@ std::unique_ptr<ASTUnit> Unit; TranslationUnitDecl *TUDecl = nullptr; std::unique_ptr<ASTImporter> Importer; - TU(StringRef Code, StringRef FileName, ArgVector Args) + ImportShim *Shim; + TU(StringRef Code, StringRef FileName, ArgVector Args, + ImportShim *Shim = nullptr) : Code(Code), FileName(FileName), Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)), - TUDecl(Unit->getASTContext().getTranslationUnitDecl()) { + TUDecl(Unit->getASTContext().getTranslationUnitDecl()), Shim(Shim) { Unit->enableSourceFileDiagnostics(); } @@ -331,6 +333,7 @@ new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(), Unit->getASTContext(), Unit->getFileManager(), false, &LookupTable)); + Importer->setShim(Shim); } assert(&ToAST->getASTContext() == &Importer->getToContext()); createVirtualFileIfNeeded(ToAST, FileName, Code); @@ -401,11 +404,12 @@ // Must not be called more than once within the same test. std::tuple<Decl *, Decl *> getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode, - Language ToLang, StringRef Identifier = DeclToImportID) { + Language ToLang, StringRef Identifier = DeclToImportID, + ImportShim *Shim = nullptr) { ArgVector FromArgs = getArgVectorForLanguage(FromLang), ToArgs = getArgVectorForLanguage(ToLang); - FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs); + FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Shim); TU &FromTU = FromTUs.back(); assert(!ToAST); @@ -455,6 +459,12 @@ return ToAST->getASTContext().getTranslationUnitDecl(); } + ASTImporter &getImporter(Decl *From, Language ToLang) { + lazyInitToAST(ToLang, "", OutputFileName); + TU *FromTU = findFromTU(From); + return *FromTU->Importer; + } + // Import the given Decl into the ToCtx. // May be called several times in a given test. // The different instances of the param From may have different ASTContext. @@ -544,6 +554,81 @@ EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2)); } +namespace { +class RedirectShim : public ImportShim { + llvm::Optional<Decl *> Import(ASTImporter &Importer, Decl *FromD) override { + auto *ND = dyn_cast<NamedDecl>(FromD); + if (!ND) + return {}; + if (ND->getName() != "shouldNotBeImported") + return {}; + for (Decl *D : Importer.getToContext().getTranslationUnitDecl()->decls()) + if (auto ND = dyn_cast<NamedDecl>(D)) + if (ND->getName() == "realDecl") + return ND; + return {}; + } +}; +} // namespace + +struct ShimTestCase : ASTImporterOptionSpecificTestBase {}; + +// Test that the shim can intercept an import call. +TEST_P(ShimTestCase, InterceptImport) { + RedirectShim Shim; + Decl *From, *To; + std::tie(From, To) = getImportedDecl("class shouldNotBeImported {};", + Lang_CXX, "class realDecl {};", Lang_CXX, + "shouldNotBeImported", &Shim); + auto *Imported = cast<CXXRecordDecl>(To); + EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl"); + + // Make sure the Shim prevented the importing of the decl. + auto *ToTU = Imported->getTranslationUnitDecl(); + auto Pattern = functionDecl(hasName("shouldNotBeImported")); + unsigned count = + DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern); + EXPECT_EQ(0U, count); +} + +// Test that when we indirectly import a declaration the shim still works. Also +// tests that the shim allows the import process to continue when we try to +// import a declaration that we ignore in the shim. +TEST_P(ShimTestCase, InterceptIndirectImport) { + RedirectShim Shim; + Decl *From, *To; + std::tie(From, To) = + getImportedDecl("class shouldNotBeImported {};" + "class F { shouldNotBeImported f; };", + Lang_CXX, "class realDecl {};", Lang_CXX, "F", &Shim); + + // Make sure the Shim prevented the importing of the decl. + auto *ToTU = To->getTranslationUnitDecl(); + auto Pattern = functionDecl(hasName("shouldNotBeImported")); + unsigned count = + DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern); + EXPECT_EQ(0U, count); +} + +namespace { +class NoOpShim : public ImportShim { + llvm::Optional<Decl *> Import(ASTImporter &Importer, Decl *FromD) override { + return {}; + } +}; +} // namespace + +// Test a shim that just does nothing. +TEST_P(ShimTestCase, NoOpShim) { + NoOpShim Shim; + Decl *From, *To; + std::tie(From, To) = + getImportedDecl("class declToImport {};", Lang_CXX, "class realDecl {};", + Lang_CXX, DeclToImportID, &Shim); + auto *Imported = cast<CXXRecordDecl>(To); + EXPECT_EQ(Imported->getQualifiedNameAsString(), "declToImport"); +} + TEST_P(ImportExpr, ImportStringLiteral) { MatchVerifier<Decl> Verifier; testImport( @@ -5512,6 +5597,9 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase, DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ShimTestCase, + DefaultTestValuesForRunOptions, ); + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions, DefaultTestValuesForRunOptions, ); Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -7827,6 +7827,14 @@ if (!FromD) return nullptr; + // First check if the Shim wants to rewrite this import call. + if (Shim) { + llvm::Optional<Decl *> NewD = Shim->Import(*this, FromD); + // Shim has given us a decl and we pretend we successfully imported. + if (NewD) + return *NewD; + } + ASTNodeImporter Importer(*this); // Check whether we've already imported this declaration. Index: clang/include/clang/AST/ASTImporter.h =================================================================== --- clang/include/clang/AST/ASTImporter.h +++ clang/include/clang/AST/ASTImporter.h @@ -78,6 +78,20 @@ // the different entries in a given redecl chain. llvm::SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D); + class ASTImporter; + /// Shim that allows intercepting the import process of the ASTImporter. + struct ImportShim { + virtual ~ImportShim() = default; + /// Called from the ASTImporter before the given Decl is imported. + /// + /// If this method returns a Decl, the ASTImporter will abort the current + /// import step and treat the returned decl as if it was just imported. + /// If this method returns nothing, the ASTImporter continues to import the + /// given Decl on its own. + virtual llvm::Optional<Decl *> Import(ASTImporter &Importer, + Decl *FromD) = 0; + }; + /// Imports selected nodes from one AST context into another context, /// merging AST nodes where appropriate. class ASTImporter { @@ -137,6 +151,10 @@ /// (which we have already complained about). NonEquivalentDeclSet NonEquivalentDecls; + /// The Shim that should rewrite the import calls of this ASTImporter, or + /// a nullptr of this ASTImporter has no shim. + ImportShim *Shim = nullptr; + using FoundDeclsTy = SmallVector<NamedDecl *, 2>; FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name); @@ -170,6 +188,10 @@ /// to-be-completed forward declarations when possible. bool isMinimalImport() const { return Minimal; } + ImportShim *getShim() { return Shim; } + + void setShim(ImportShim *S) { Shim = S; } + /// \brief Import the given object, returns the result. /// /// \param To Import the object into this variable.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits