[clang] [C++20] [Modules] Profiling non dependent noexcept expression by pointer (PR #224528)
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 18 00:02:33 PDT 2026
https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/224528
>From f2b9c4659492edf2065038427ce62a8922d81a31 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <chuanqi.xcq at alibaba-inc.com>
Date: Fri, 18 Sep 2026 13:37:40 +0800
Subject: [PATCH 1/2] [C++20] [Modules] Profiling non dependent noexcept
expression by pointer
---
clang/lib/AST/Type.cpp | 14 ++++++-
...oncept-specialization-deserialization.cppm | 37 ++++++++++++++++
.../Serialization/LoadSpecLazilyTest.cpp | 42 +++++++++++++++++++
3 files changed, 92 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Modules/concept-specialization-deserialization.cppm
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 98901502cf37a..123028d214100 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4094,7 +4094,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 uninitlaized
+ // 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;
>From 06a5f3af32b846a161b5f49480701584fb8519b4 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <chuanqi.xcq at alibaba-inc.com>
Date: Fri, 18 Sep 2026 15:02:07 +0800
Subject: [PATCH 2/2] update
---
clang/lib/AST/Type.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 123028d214100..0a884a9afc0aa 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4100,7 +4100,7 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
// address of the expression as profiling results instead of profiling the
// expression.
//
- // This is not only an optimization but avoids an access on uninitlaized
+ // 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
More information about the cfe-commits
mailing list