diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 5ce7007d8acef..7ce2640c279f4 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1659,6 +1659,10 @@ void ASTDeclReader::ReadVarDeclInit(VarDecl *VD) { EvaluatedStmt *Eval = VD->ensureEvaluatedStmt(); Eval->HasConstantInitialization = (Val & 2) != 0; Eval->HasConstantDestruction = (Val & 4) != 0; + Eval->WasEvaluated = (Val & 8) != 0; + if (Eval->WasEvaluated) + Eval->Evaluated = Record.readAPValue(); + // Store the offset of the initializer. Don't deserialize it yet: it might // not be needed, and might refer back to the variable, for example if it // contains a lambda. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 749aaa4cd6e19..4efc27b3d2434 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5987,13 +5987,20 @@ void ASTRecordWriter::AddVarDeclInit(const VarDecl *VD) { return; } - unsigned Val = 1; + uint64_t Val = 1; if (EvaluatedStmt *ES = VD->getEvaluatedStmt()) { Val |= (ES->HasConstantInitialization ? 2 : 0); Val |= (ES->HasConstantDestruction ? 4 : 0); - // FIXME: Also emit the constant initializer value. + APValue *Evaluated = VD->getEvaluatedValue(); + // If the evaluted result is constant, emit it. + if (Evaluated && (Evaluated->isInt() || Evaluated->isFloat())) + Val |= 8; } push_back(Val); + if (Val & 8) { + AddAPValue(*VD->getEvaluatedValue()); + } + writeStmtRef(Init); } diff --git a/clang/unittests/Serialization/CMakeLists.txt b/clang/unittests/Serialization/CMakeLists.txt index cfb4089167aad..88aca2e135200 100644 --- a/clang/unittests/Serialization/CMakeLists.txt +++ b/clang/unittests/Serialization/CMakeLists.txt @@ -8,6 +8,7 @@ add_clang_unittest(SerializationTests InMemoryModuleCacheTest.cpp ModuleCacheTest.cpp SourceLocationEncodingTest.cpp + VarDeclConstantInitTest.cpp ) clang_target_link_libraries(SerializationTests @@ -18,4 +19,5 @@ clang_target_link_libraries(SerializationTests clangLex clangSema clangSerialization + clangTooling ) diff --git a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp new file mode 100644 index 0000000000000..4445030122d57 --- /dev/null +++ b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp @@ -0,0 +1,139 @@ +//===- 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 + 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 + constexpr unsigned long Cache = Compute(Number{}, Strategy{}); + + template constexpr unsigned long Cache<10ul>; +} + )cpp"); + + IntrusiveRefCntPtr Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + CreateInvocationOptions CIOpts; + CIOpts.Diags = Diags; + + llvm::Twine CacheBMIPath = TestDir + "/Cached.pcm"; + const char *Args[] = {"clang++", + "-std=c++20", + "--precompile", + "-working-directory", + TestDir.c_str(), + "Cached.cppm", + "-o", + CacheBMIPath.str().c_str()}; + std::shared_ptr Invocation = + createInvocation(Args, CIOpts); + ASSERT_TRUE(Invocation); + + CompilerInstance Instance; + Instance.setDiagnostics(Diags.get()); + Instance.setInvocation(Invocation); + GenerateModuleInterfaceAction Action; + ASSERT_TRUE(Instance.ExecuteAction(Action)); + ASSERT_FALSE(Diags->hasErrorOccurred()); + + llvm::Twine DepArg = "-fmodule-file=Fibonacci.Cache=" + CacheBMIPath; + std::unique_ptr AST = tooling::buildASTFromCodeWithArgs( + R"cpp( +import Fibonacci.Cache; + )cpp", + /*Args=*/{"-std=c++20", DepArg.str().c_str()}); + + using namespace clang::ast_matchers; + ASTContext &Ctx = AST->getASTContext(); + const auto *cached = selectFirst( + "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