[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