|  | //===- unittests/Serialization/VarDeclConstantInitTest.cpp - CI tests -----===// | 
|  | // | 
|  | // 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 "clang/ASTMatchers/ASTMatchFinder.h" | 
|  | #include "clang/ASTMatchers/ASTMatchers.h" | 
|  | #include "clang/Basic/FileManager.h" | 
|  | #include "clang/Frontend/CompilerInstance.h" | 
|  | #include "clang/Frontend/CompilerInvocation.h" | 
|  | #include "clang/Frontend/FrontendActions.h" | 
|  | #include "clang/Frontend/Utils.h" | 
|  | #include "clang/Lex/HeaderSearch.h" | 
|  | #include "clang/Tooling/Tooling.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/Support/FileSystem.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  |  | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace clang; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class VarDeclConstantInitTest : public ::testing::Test { | 
|  | void SetUp() override { | 
|  | ASSERT_FALSE(sys::fs::createUniqueDirectory("modules-test", TestDir)); | 
|  | } | 
|  |  | 
|  | void TearDown() override { sys::fs::remove_directories(TestDir); } | 
|  |  | 
|  | public: | 
|  | SmallString<256> TestDir; | 
|  |  | 
|  | void addFile(StringRef Path, StringRef Contents) { | 
|  | ASSERT_FALSE(sys::path::is_absolute(Path)); | 
|  |  | 
|  | SmallString<256> AbsPath(TestDir); | 
|  | sys::path::append(AbsPath, Path); | 
|  |  | 
|  | ASSERT_FALSE( | 
|  | sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); | 
|  |  | 
|  | std::error_code EC; | 
|  | llvm::raw_fd_ostream OS(AbsPath, EC); | 
|  | ASSERT_FALSE(EC); | 
|  | OS << Contents; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(VarDeclConstantInitTest, CachedConstantInit) { | 
|  | addFile("Cached.cppm", R"cpp( | 
|  | export module Fibonacci.Cache; | 
|  |  | 
|  | export namespace Fibonacci | 
|  | { | 
|  | constexpr unsigned long Recursive(unsigned long n) | 
|  | { | 
|  | if (n == 0) | 
|  | return 0; | 
|  | if (n == 1) | 
|  | return 1; | 
|  | return Recursive(n - 2) + Recursive(n - 1); | 
|  | } | 
|  |  | 
|  | template<unsigned long N> | 
|  | struct Number{}; | 
|  |  | 
|  | struct DefaultStrategy | 
|  | { | 
|  | constexpr unsigned long operator()(unsigned long n, auto... other) const | 
|  | { | 
|  | return (n + ... + other); | 
|  | } | 
|  | }; | 
|  |  | 
|  | constexpr unsigned long Compute(Number<10ul>, auto strategy) | 
|  | { | 
|  | return strategy(Recursive(10ul)); | 
|  | } | 
|  |  | 
|  | template<unsigned long N, typename Strategy = DefaultStrategy> | 
|  | constexpr unsigned long Cache = Compute(Number<N>{}, Strategy{}); | 
|  |  | 
|  | template constexpr unsigned long Cache<10ul>; | 
|  | } | 
|  | )cpp"); | 
|  |  | 
|  | CreateInvocationOptions CIOpts; | 
|  | CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); | 
|  | IntrusiveRefCntPtr<DiagnosticsEngine> Diags = | 
|  | CompilerInstance::createDiagnostics(*CIOpts.VFS, new DiagnosticOptions()); | 
|  | CIOpts.Diags = Diags; | 
|  |  | 
|  | const char *Args[] = {"clang++",       "-std=c++20", | 
|  | "--precompile",  "-working-directory", | 
|  | TestDir.c_str(), "Cached.cppm"}; | 
|  | std::shared_ptr<CompilerInvocation> Invocation = | 
|  | createInvocation(Args, CIOpts); | 
|  | ASSERT_TRUE(Invocation); | 
|  | Invocation->getFrontendOpts().DisableFree = false; | 
|  |  | 
|  | CompilerInstance Instance(std::move(Invocation)); | 
|  | Instance.setDiagnostics(Diags.get()); | 
|  |  | 
|  | std::string CacheBMIPath = llvm::Twine(TestDir + "/Cached.pcm").str(); | 
|  | Instance.getFrontendOpts().OutputFile = CacheBMIPath; | 
|  |  | 
|  | GenerateReducedModuleInterfaceAction Action; | 
|  | ASSERT_TRUE(Instance.ExecuteAction(Action)); | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  |  | 
|  | std::string DepArg = | 
|  | llvm::Twine("-fmodule-file=Fibonacci.Cache=" + CacheBMIPath).str(); | 
|  | std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs( | 
|  | R"cpp( | 
|  | import Fibonacci.Cache; | 
|  | )cpp", | 
|  | /*Args=*/{"-std=c++20", DepArg.c_str()}); | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  | const auto *cached = selectFirst<VarDecl>( | 
|  | "Cache", | 
|  | match(varDecl(isTemplateInstantiation(), hasName("Cache")).bind("Cache"), | 
|  | Ctx)); | 
|  | EXPECT_TRUE(cached); | 
|  | EXPECT_TRUE(cached->getEvaluatedStmt()); | 
|  | EXPECT_TRUE(cached->getEvaluatedStmt()->WasEvaluated); | 
|  | EXPECT_TRUE(cached->getEvaluatedValue()); | 
|  | EXPECT_TRUE(cached->getEvaluatedValue()->isInt()); | 
|  | EXPECT_EQ(cached->getEvaluatedValue()->getInt(), 55); | 
|  | } | 
|  |  | 
|  | } // anonymous namespace |