120e90495SChuanqi Xu //== unittests/Serialization/LoadSpecLazily.cpp ----------------------========// 220e90495SChuanqi Xu // 320e90495SChuanqi Xu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 420e90495SChuanqi Xu // See https://llvm.org/LICENSE.txt for license information. 520e90495SChuanqi Xu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 620e90495SChuanqi Xu // 720e90495SChuanqi Xu //===----------------------------------------------------------------------===// 820e90495SChuanqi Xu 920e90495SChuanqi Xu #include "clang/Frontend/CompilerInstance.h" 1020e90495SChuanqi Xu #include "clang/Frontend/FrontendAction.h" 1120e90495SChuanqi Xu #include "clang/Frontend/FrontendActions.h" 1220e90495SChuanqi Xu #include "clang/Parse/ParseAST.h" 1320e90495SChuanqi Xu #include "clang/Serialization/ASTDeserializationListener.h" 1420e90495SChuanqi Xu #include "clang/Tooling/Tooling.h" 1520e90495SChuanqi Xu #include "gtest/gtest.h" 1620e90495SChuanqi Xu 1720e90495SChuanqi Xu using namespace llvm; 1820e90495SChuanqi Xu using namespace clang; 1920e90495SChuanqi Xu using namespace clang::tooling; 2020e90495SChuanqi Xu 2120e90495SChuanqi Xu namespace { 2220e90495SChuanqi Xu 2320e90495SChuanqi Xu class LoadSpecLazilyTest : public ::testing::Test { 2420e90495SChuanqi Xu void SetUp() override { 2520e90495SChuanqi Xu ASSERT_FALSE( 2620e90495SChuanqi Xu sys::fs::createUniqueDirectory("load-spec-lazily-test", TestDir)); 2720e90495SChuanqi Xu } 2820e90495SChuanqi Xu 2920e90495SChuanqi Xu void TearDown() override { sys::fs::remove_directories(TestDir); } 3020e90495SChuanqi Xu 3120e90495SChuanqi Xu public: 3220e90495SChuanqi Xu SmallString<256> TestDir; 3320e90495SChuanqi Xu 3420e90495SChuanqi Xu void addFile(StringRef Path, StringRef Contents) { 3520e90495SChuanqi Xu ASSERT_FALSE(sys::path::is_absolute(Path)); 3620e90495SChuanqi Xu 3720e90495SChuanqi Xu SmallString<256> AbsPath(TestDir); 3820e90495SChuanqi Xu sys::path::append(AbsPath, Path); 3920e90495SChuanqi Xu 4020e90495SChuanqi Xu ASSERT_FALSE( 4120e90495SChuanqi Xu sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); 4220e90495SChuanqi Xu 4320e90495SChuanqi Xu std::error_code EC; 4420e90495SChuanqi Xu llvm::raw_fd_ostream OS(AbsPath, EC); 4520e90495SChuanqi Xu ASSERT_FALSE(EC); 4620e90495SChuanqi Xu OS << Contents; 4720e90495SChuanqi Xu } 4820e90495SChuanqi Xu 4920e90495SChuanqi Xu std::string GenerateModuleInterface(StringRef ModuleName, 5020e90495SChuanqi Xu StringRef Contents) { 5120e90495SChuanqi Xu std::string FileName = llvm::Twine(ModuleName + ".cppm").str(); 5220e90495SChuanqi Xu addFile(FileName, Contents); 5320e90495SChuanqi Xu 5420e90495SChuanqi Xu IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = 5520e90495SChuanqi Xu llvm::vfs::createPhysicalFileSystem(); 5620e90495SChuanqi Xu IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 5720e90495SChuanqi Xu CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions()); 5820e90495SChuanqi Xu CreateInvocationOptions CIOpts; 5920e90495SChuanqi Xu CIOpts.Diags = Diags; 6020e90495SChuanqi Xu CIOpts.VFS = VFS; 6120e90495SChuanqi Xu 6220e90495SChuanqi Xu std::string CacheBMIPath = 6320e90495SChuanqi Xu llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str(); 6420e90495SChuanqi Xu std::string PrebuiltModulePath = 6520e90495SChuanqi Xu "-fprebuilt-module-path=" + TestDir.str().str(); 6620e90495SChuanqi Xu const char *Args[] = {"clang++", 6720e90495SChuanqi Xu "-std=c++20", 6820e90495SChuanqi Xu "--precompile", 6920e90495SChuanqi Xu PrebuiltModulePath.c_str(), 7020e90495SChuanqi Xu "-working-directory", 7120e90495SChuanqi Xu TestDir.c_str(), 7220e90495SChuanqi Xu "-I", 7320e90495SChuanqi Xu TestDir.c_str(), 7420e90495SChuanqi Xu FileName.c_str(), 7520e90495SChuanqi Xu "-o", 7620e90495SChuanqi Xu CacheBMIPath.c_str()}; 7720e90495SChuanqi Xu std::shared_ptr<CompilerInvocation> Invocation = 7820e90495SChuanqi Xu createInvocation(Args, CIOpts); 7920e90495SChuanqi Xu EXPECT_TRUE(Invocation); 8020e90495SChuanqi Xu 8120e90495SChuanqi Xu CompilerInstance Instance; 8220e90495SChuanqi Xu Instance.setDiagnostics(Diags.get()); 8320e90495SChuanqi Xu Instance.setInvocation(Invocation); 8420e90495SChuanqi Xu Instance.getFrontendOpts().OutputFile = CacheBMIPath; 85*7f431201SIlya Biryukov // Avoid memory leaks. 86*7f431201SIlya Biryukov Instance.getFrontendOpts().DisableFree = false; 8720e90495SChuanqi Xu GenerateModuleInterfaceAction Action; 8820e90495SChuanqi Xu EXPECT_TRUE(Instance.ExecuteAction(Action)); 8920e90495SChuanqi Xu EXPECT_FALSE(Diags->hasErrorOccurred()); 9020e90495SChuanqi Xu 9120e90495SChuanqi Xu return CacheBMIPath; 9220e90495SChuanqi Xu } 9320e90495SChuanqi Xu }; 9420e90495SChuanqi Xu 9520e90495SChuanqi Xu enum class CheckingMode { Forbidden, Required }; 9620e90495SChuanqi Xu 9720e90495SChuanqi Xu class DeclsReaderListener : public ASTDeserializationListener { 9820e90495SChuanqi Xu StringRef SpeficiedName; 9920e90495SChuanqi Xu CheckingMode Mode; 10020e90495SChuanqi Xu 10120e90495SChuanqi Xu bool ReadedSpecifiedName = false; 10220e90495SChuanqi Xu 10320e90495SChuanqi Xu public: 10420e90495SChuanqi Xu void DeclRead(GlobalDeclID ID, const Decl *D) override { 10520e90495SChuanqi Xu auto *ND = dyn_cast<NamedDecl>(D); 10620e90495SChuanqi Xu if (!ND) 10720e90495SChuanqi Xu return; 10820e90495SChuanqi Xu 10920e90495SChuanqi Xu ReadedSpecifiedName |= ND->getName().contains(SpeficiedName); 11020e90495SChuanqi Xu if (Mode == CheckingMode::Forbidden) { 11120e90495SChuanqi Xu EXPECT_FALSE(ReadedSpecifiedName); 11220e90495SChuanqi Xu } 11320e90495SChuanqi Xu } 11420e90495SChuanqi Xu 11520e90495SChuanqi Xu DeclsReaderListener(StringRef SpeficiedName, CheckingMode Mode) 11620e90495SChuanqi Xu : SpeficiedName(SpeficiedName), Mode(Mode) {} 11720e90495SChuanqi Xu 11820e90495SChuanqi Xu ~DeclsReaderListener() { 11920e90495SChuanqi Xu if (Mode == CheckingMode::Required) { 12020e90495SChuanqi Xu EXPECT_TRUE(ReadedSpecifiedName); 12120e90495SChuanqi Xu } 12220e90495SChuanqi Xu } 12320e90495SChuanqi Xu }; 12420e90495SChuanqi Xu 12520e90495SChuanqi Xu class LoadSpecLazilyConsumer : public ASTConsumer { 12620e90495SChuanqi Xu DeclsReaderListener Listener; 12720e90495SChuanqi Xu 12820e90495SChuanqi Xu public: 12920e90495SChuanqi Xu LoadSpecLazilyConsumer(StringRef SpecifiedName, CheckingMode Mode) 13020e90495SChuanqi Xu : Listener(SpecifiedName, Mode) {} 13120e90495SChuanqi Xu 13220e90495SChuanqi Xu ASTDeserializationListener *GetASTDeserializationListener() override { 13320e90495SChuanqi Xu return &Listener; 13420e90495SChuanqi Xu } 13520e90495SChuanqi Xu }; 13620e90495SChuanqi Xu 13720e90495SChuanqi Xu class CheckLoadSpecLazilyAction : public ASTFrontendAction { 13820e90495SChuanqi Xu StringRef SpecifiedName; 13920e90495SChuanqi Xu CheckingMode Mode; 14020e90495SChuanqi Xu 14120e90495SChuanqi Xu public: 14220e90495SChuanqi Xu std::unique_ptr<ASTConsumer> 14320e90495SChuanqi Xu CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override { 14420e90495SChuanqi Xu return std::make_unique<LoadSpecLazilyConsumer>(SpecifiedName, Mode); 14520e90495SChuanqi Xu } 14620e90495SChuanqi Xu 14720e90495SChuanqi Xu CheckLoadSpecLazilyAction(StringRef SpecifiedName, CheckingMode Mode) 14820e90495SChuanqi Xu : SpecifiedName(SpecifiedName), Mode(Mode) {} 14920e90495SChuanqi Xu }; 15020e90495SChuanqi Xu 15120e90495SChuanqi Xu TEST_F(LoadSpecLazilyTest, BasicTest) { 15220e90495SChuanqi Xu GenerateModuleInterface("M", R"cpp( 15320e90495SChuanqi Xu export module M; 15420e90495SChuanqi Xu export template <class T> 15520e90495SChuanqi Xu class A {}; 15620e90495SChuanqi Xu export class ShouldNotBeLoaded {}; 15720e90495SChuanqi Xu export class Temp { 15820e90495SChuanqi Xu A<ShouldNotBeLoaded> AS; 15920e90495SChuanqi Xu }; 16020e90495SChuanqi Xu )cpp"); 16120e90495SChuanqi Xu 16220e90495SChuanqi Xu const char *test_file_contents = R"cpp( 16320e90495SChuanqi Xu import M; 16420e90495SChuanqi Xu A<int> a; 16520e90495SChuanqi Xu )cpp"; 16620e90495SChuanqi Xu std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); 16720e90495SChuanqi Xu EXPECT_TRUE( 16820e90495SChuanqi Xu runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>( 16920e90495SChuanqi Xu "ShouldNotBeLoaded", CheckingMode::Forbidden), 17020e90495SChuanqi Xu test_file_contents, 17120e90495SChuanqi Xu { 17220e90495SChuanqi Xu "-std=c++20", 17320e90495SChuanqi Xu DepArg.c_str(), 17420e90495SChuanqi Xu "-I", 17520e90495SChuanqi Xu TestDir.c_str(), 17620e90495SChuanqi Xu }, 17720e90495SChuanqi Xu "test.cpp")); 17820e90495SChuanqi Xu } 17920e90495SChuanqi Xu 18020e90495SChuanqi Xu TEST_F(LoadSpecLazilyTest, ChainedTest) { 18120e90495SChuanqi Xu GenerateModuleInterface("M", R"cpp( 18220e90495SChuanqi Xu export module M; 18320e90495SChuanqi Xu export template <class T> 18420e90495SChuanqi Xu class A {}; 18520e90495SChuanqi Xu )cpp"); 18620e90495SChuanqi Xu 18720e90495SChuanqi Xu GenerateModuleInterface("N", R"cpp( 18820e90495SChuanqi Xu export module N; 18920e90495SChuanqi Xu export import M; 19020e90495SChuanqi Xu export class ShouldNotBeLoaded {}; 19120e90495SChuanqi Xu export class Temp { 19220e90495SChuanqi Xu A<ShouldNotBeLoaded> AS; 19320e90495SChuanqi Xu }; 19420e90495SChuanqi Xu )cpp"); 19520e90495SChuanqi Xu 19620e90495SChuanqi Xu const char *test_file_contents = R"cpp( 19720e90495SChuanqi Xu import N; 19820e90495SChuanqi Xu A<int> a; 19920e90495SChuanqi Xu )cpp"; 20020e90495SChuanqi Xu std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); 20120e90495SChuanqi Xu EXPECT_TRUE( 20220e90495SChuanqi Xu runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>( 20320e90495SChuanqi Xu "ShouldNotBeLoaded", CheckingMode::Forbidden), 20420e90495SChuanqi Xu test_file_contents, 20520e90495SChuanqi Xu { 20620e90495SChuanqi Xu "-std=c++20", 20720e90495SChuanqi Xu DepArg.c_str(), 20820e90495SChuanqi Xu "-I", 20920e90495SChuanqi Xu TestDir.c_str(), 21020e90495SChuanqi Xu }, 21120e90495SChuanqi Xu "test.cpp")); 21220e90495SChuanqi Xu } 21320e90495SChuanqi Xu 21420e90495SChuanqi Xu /// Test that we won't crash due to we may invalidate the lazy specialization 21520e90495SChuanqi Xu /// lookup table during the loading process. 21620e90495SChuanqi Xu TEST_F(LoadSpecLazilyTest, ChainedTest2) { 21720e90495SChuanqi Xu GenerateModuleInterface("M", R"cpp( 21820e90495SChuanqi Xu export module M; 21920e90495SChuanqi Xu export template <class T> 22020e90495SChuanqi Xu class A {}; 22120e90495SChuanqi Xu 22220e90495SChuanqi Xu export class B {}; 22320e90495SChuanqi Xu 22420e90495SChuanqi Xu export class C { 22520e90495SChuanqi Xu A<B> D; 22620e90495SChuanqi Xu }; 22720e90495SChuanqi Xu )cpp"); 22820e90495SChuanqi Xu 22920e90495SChuanqi Xu GenerateModuleInterface("N", R"cpp( 23020e90495SChuanqi Xu export module N; 23120e90495SChuanqi Xu export import M; 23220e90495SChuanqi Xu export class MayBeLoaded {}; 23320e90495SChuanqi Xu 23420e90495SChuanqi Xu export class Temp { 23520e90495SChuanqi Xu A<MayBeLoaded> AS; 23620e90495SChuanqi Xu }; 23720e90495SChuanqi Xu 23820e90495SChuanqi Xu export class ExportedClass {}; 23920e90495SChuanqi Xu 24020e90495SChuanqi Xu export template<> class A<ExportedClass> { 24120e90495SChuanqi Xu A<MayBeLoaded> AS; 24220e90495SChuanqi Xu A<B> AB; 24320e90495SChuanqi Xu }; 24420e90495SChuanqi Xu )cpp"); 24520e90495SChuanqi Xu 24620e90495SChuanqi Xu const char *test_file_contents = R"cpp( 24720e90495SChuanqi Xu import N; 24820e90495SChuanqi Xu Temp T; 24920e90495SChuanqi Xu A<ExportedClass> a; 25020e90495SChuanqi Xu )cpp"; 25120e90495SChuanqi Xu std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); 25220e90495SChuanqi Xu EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>( 25320e90495SChuanqi Xu "MayBeLoaded", CheckingMode::Required), 25420e90495SChuanqi Xu test_file_contents, 25520e90495SChuanqi Xu { 25620e90495SChuanqi Xu "-std=c++20", 25720e90495SChuanqi Xu DepArg.c_str(), 25820e90495SChuanqi Xu "-I", 25920e90495SChuanqi Xu TestDir.c_str(), 26020e90495SChuanqi Xu }, 26120e90495SChuanqi Xu "test.cpp")); 26220e90495SChuanqi Xu } 26320e90495SChuanqi Xu 26420e90495SChuanqi Xu } // namespace 265