Author: Chuanqi Xu Date: 2026-09-21T02:54:59Z New Revision: f970f59a9d4e6b8738547d01bc33e8a12df5244b
URL: https://github.com/llvm/llvm-project/commit/f970f59a9d4e6b8738547d01bc33e8a12df5244b DIFF: https://github.com/llvm/llvm-project/commit/f970f59a9d4e6b8738547d01bc33e8a12df5244b.diff LOG: [C++20] [Modules] Profiling non dependent noexcept expression by pointer (#224528) Another approach to solve https://github.com/llvm/llvm-project/issues/191361 and https://github.com/llvm/llvm-project/issues/224180 than https://github.com/llvm/llvm-project/pull/222148 I want to avoid competing PR but I do feel the original one's approach seems too workaround. And I want to emphasize that, in my experience, finding the root cause is much more harder than find the solution to me. Many thanks to @ispeters For the approach itself, I described them in the comments of the PR. Note that the real problem is we were profiling something is not completely deserialized, so that even if it is not crash, the logic is not correct. Added: clang/test/Modules/concept-specialization-deserialization.cppm Modified: clang/lib/AST/Type.cpp clang/unittests/Serialization/LoadSpecLazilyTest.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 439625504657d..5fdac154725c3 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4177,7 +4177,19 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, if (epi.ExceptionSpec.Type == EST_Dynamic) { for (QualType Ex : epi.ExceptionSpec.Exceptions) ID.AddPointer(Ex.getAsOpaquePtr()); - } else if (isComputedNoexcept(epi.ExceptionSpec.Type)) { + } else if (epi.ExceptionSpec.Type == EST_NoexceptTrue || + epi.ExceptionSpec.Type == EST_NoexceptFalse) { + // If the exception type has already been determined, we can use the + // address of the expression as profiling results instead of profiling the + // expression. + // + // This is not only an optimization but avoids an access on uninitialized + // fields during the profiling. + // + // See clang/test/Modules/concept-specialization-deserialization.cppm for + // an example. + ID.AddPointer(epi.ExceptionSpec.NoexceptExpr); + } else if (epi.ExceptionSpec.Type == EST_DependentNoexcept) { // getFunctionTypeInternal compares noexcept expressions after the lookup, // so the key only needs their canonical form. epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, /*Canonical=*/true); diff --git a/clang/test/Modules/concept-specialization-deserialization.cppm b/clang/test/Modules/concept-specialization-deserialization.cppm new file mode 100644 index 0000000000000..876008e1e5ece --- /dev/null +++ b/clang/test/Modules/concept-specialization-deserialization.cppm @@ -0,0 +1,37 @@ +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 -fmodule-file=a=%t/a.pcm -fsyntax-only %t/use.cpp -verify +// +// RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/a.cppm -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 -fmodule-file=a=%t/a.pcm -fsyntax-only %t/use.cpp -verify + +//--- a.cppm +export module a; +template <class> concept C = true; + +template <class T> int fn() noexcept(C<T>); +export using t = decltype(fn<int>()); + +//--- use.cpp +// expected-no-diagnostics +import a; + +// During the deserialization process of fn<int>, the C<int> in noexcept expression +// may be not completely deserialized. This test makes sure that we can handle the case. +// +// The ordering is: +// +// Deserialize C<int> +// +// Deserializing C<int>'s template argument +// +// Read SubstTemplateTypeParmType::AssociatedDecl in readSubstTemplateTypeParmType +// +// Deserialize fn<int> +// +// FunctionProtoType::Profile +// +// but C<int>'s template arguments is not deserialized yet. +t x; diff --git a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp index f55925aeae1f2..4ae85f4f910e6 100644 --- a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp +++ b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" #include "clang/Driver/CreateInvocationFromArgs.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -95,6 +97,46 @@ class LoadSpecLazilyTest : public ::testing::Test { enum class CheckingMode { Forbidden, Required }; +TEST_F(LoadSpecLazilyTest, ConceptSpecializationInExceptionSpecification) { + GenerateModuleInterface("M", R"cpp( +export module M; +template <class> concept C = true; +template <class T> int fn() noexcept(C<T>); +template <class T> int gn() noexcept(!C<T>); +export using t = decltype(fn<int>()); +export using u = decltype(gn<int>()); + )cpp"); + + auto AST = buildASTFromCodeWithArgs( + "import M;\nt x;\nu y;", + {"-std=c++20", "-fprebuilt-module-path=" + TestDir.str().str()}); + ASSERT_TRUE(AST); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + ASTContext &Ctx = AST->getASTContext(); + + // Reading C<int>'s substituted argument loads fn<int>, including its + // exception specification, before the argument has been populated. If + // constructing the function type profiles C<int> at that point, its + // FoldingSet key changes once deserialization completes. + SmallVector<const FunctionProtoType *, 4> Types; + bool SawNoexceptTrue = false, SawNoexceptFalse = false; + for (const auto *T : Ctx.getTypes()) { + const auto *F = dyn_cast<FunctionProtoType>(T); + if (F && (F->getExceptionSpecType() == EST_NoexceptTrue || + F->getExceptionSpecType() == EST_NoexceptFalse)) { + SawNoexceptTrue |= F->getExceptionSpecType() == EST_NoexceptTrue; + SawNoexceptFalse |= F->getExceptionSpecType() == EST_NoexceptFalse; + Types.push_back(F); + } + } + EXPECT_TRUE(SawNoexceptTrue); + EXPECT_TRUE(SawNoexceptFalse); + for (const FunctionProtoType *F : Types) + EXPECT_EQ(F, Ctx.getFunctionType(F->getReturnType(), F->getParamTypes(), + F->getExtProtoInfo()) + .getTypePtr()); +} + class DeclsReaderListener : public ASTDeserializationListener { StringRef SpeficiedName; CheckingMode Mode; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
