[clang] Reland [C++20] [Modules] Don't profiling the callee of CXXFoldExpr (#190732) (PR #195983)
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Thu May 7 18:51:10 PDT 2026
https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/195983
>From c54b8fcb7d7760929a92599a1e08d7342c24373e Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Tue, 7 Apr 2026 15:04:49 +0800
Subject: [PATCH 1/2] Reland [C++20] [Modules] Don't profiling the callee of
CXXFoldExpr (#190732)
Close https://github.com/llvm/llvm-project/issues/190333
For the test case, the root cause of the problem is, the compiler
thought the declaration of `operator &&` in consumer.cpp may change the
meaning of '&&' in the requrie clause of `F::operator()`. But it doesn't
make sense. Here we skip profiling the callee to solve the problem. Note
that we've already record the kind of the operator. So '&&' and '||'
won't be confused.
---
See the discussion in https://github.com/llvm/llvm-project/pull/194283
For the new found pattern that we may have other binary operator (e.g.,
operator +) in the require clause, e.g.,
```C++
template <typename T, typename U>
requires requires(T t, U u) { t + u; }
void operator()(T, U) {}
```
This is a new problem and we need to solve it in other PR.
---
clang/lib/AST/StmtProfile.cpp | 32 ++++++++++++++++-
.../callable-require-clause-merge.cppm | 35 +++++++++++++++++++
clang/test/Modules/polluted-operator.cppm | 7 ----
.../diagnose-redefinition-fold-expr.cpp | 6 ++++
4 files changed, 72 insertions(+), 8 deletions(-)
create mode 100644 clang/test/Modules/callable-require-clause-merge.cppm
create mode 100644 clang/test/SemaCXX/diagnose-redefinition-fold-expr.cpp
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 8219e57644be6..c3bdcb9a2e60d 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2400,7 +2400,37 @@ void StmtProfiler::VisitMaterializeTemporaryExpr(
}
void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) {
- VisitExpr(S);
+ VisitStmtNoChildren(S);
+ // We intentionally not profile the callee sub-expression
+ // to keep the profiling result stable across different
+ // context.
+ //
+ // "a.h"
+ //
+ // struct F {
+ // template <typename... T> requires ((sizeof(T) > 0) && ...)
+ // void operator()(T...) {}
+ // } f;
+ //
+ // and
+ //
+ // "c.h"
+ //
+ // void operator&&(struct X, struct X);
+ // #include "a.h"
+ //
+ // Here we might give different profiling results if we profile
+ // the callee sub-expression, which is nullptr in the first case
+ // an UnresolvedLookupExpr in the second case where there is a
+ // global operator&& operator that pollutes the fold expression.
+ if (S->getLHS())
+ Visit(S->getLHS());
+ else
+ ID.AddInteger(0);
+ if (S->getRHS())
+ Visit(S->getRHS());
+ else
+ ID.AddInteger(0);
ID.AddInteger(S->getOperator());
}
diff --git a/clang/test/Modules/callable-require-clause-merge.cppm b/clang/test/Modules/callable-require-clause-merge.cppm
new file mode 100644
index 0000000000000..ae49dd7a58542
--- /dev/null
+++ b/clang/test/Modules/callable-require-clause-merge.cppm
@@ -0,0 +1,35 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/mymod.cppm -emit-module-interface -o %t/mymod.pcm
+// RUN: %clang_cc1 -std=c++20 %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/mymod.cppm -emit-reduced-module-interface -o %t/mymod.pcm
+// RUN: %clang_cc1 -std=c++20 %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/mymod.cppm -emit-module-interface -o %t/mymod.pcm
+// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/mymod.cppm -emit-reduced-module-interface -o %t/mymod.pcm
+// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/consumer.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
+//--- r.h
+struct F {
+ template <typename... T> requires ((sizeof(T) > 0) && ...)
+ void operator()(T...) {}
+} f;
+
+//--- mymod.cppm
+module;
+#include "r.h"
+export module mymod;
+export using ::f;
+
+//--- consumer.cpp
+// expected-no-diagnostics
+void operator&&(struct X, struct X);
+#include "r.h"
+import mymod;
+
+void g() { f(); }
diff --git a/clang/test/Modules/polluted-operator.cppm b/clang/test/Modules/polluted-operator.cppm
index 45cc5e37d6a64..e81a63c3e03de 100644
--- a/clang/test/Modules/polluted-operator.cppm
+++ b/clang/test/Modules/polluted-operator.cppm
@@ -49,8 +49,6 @@ namespace std
//--- a.cppm
module;
-// The operator&& defined in 'foo.h' will pollute the
-// expression '__is_trivial(_Types) && ...' in bar.h
#include "foo.h"
#include "bar.h"
export module a;
@@ -71,9 +69,4 @@ export namespace std {
using std::operator&&;
}
-#ifdef SKIP_ODR_CHECK_IN_GMF
// expected-no-diagnostics
-#else
-// expected-error@* {{has different definitions in different modules; first difference is defined here found data member '_S_copy_ctor' with an initializer}}
-// expected-note@* {{but in 'a.<global>' found data member '_S_copy_ctor' with a different initializer}}
-#endif
diff --git a/clang/test/SemaCXX/diagnose-redefinition-fold-expr.cpp b/clang/test/SemaCXX/diagnose-redefinition-fold-expr.cpp
new file mode 100644
index 0000000000000..2c43eb7a16d8a
--- /dev/null
+++ b/clang/test/SemaCXX/diagnose-redefinition-fold-expr.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+template <typename... T> requires ((sizeof(T) > 0) && ...) void f() {} // expected-note{{previous definition is here}}
+class A;
+void operator&&(A, A);
+template <typename... T> requires ((sizeof(T) > 0) && ...) void f() {} // expected-error{{redefinition of 'f'}}
>From 43088c3d1c70c59db3eded3f29640b26c6284c14 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Fri, 8 May 2026 09:50:44 +0800
Subject: [PATCH 2/2] update
---
clang/lib/AST/StmtProfile.cpp | 29 +++++++++--------------------
1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index c3bdcb9a2e60d..eb25e5260fd1a 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2401,28 +2401,17 @@ void StmtProfiler::VisitMaterializeTemporaryExpr(
void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) {
VisitStmtNoChildren(S);
- // We intentionally not profile the callee sub-expression
- // to keep the profiling result stable across different
- // context.
+ // The callee sub-expression is not part of how the expression is written,
+ // so it's not added to the profile.
//
- // "a.h"
+ // Example:
+ // template <typename... T> requires ((sizeof(T) > 0) && ...) void f() {}
+ // class A;
+ // void operator&&(A, A);
+ // template <typename... T> requires ((sizeof(T) > 0) && ...) void f() {}
//
- // struct F {
- // template <typename... T> requires ((sizeof(T) > 0) && ...)
- // void operator()(T...) {}
- // } f;
- //
- // and
- //
- // "c.h"
- //
- // void operator&&(struct X, struct X);
- // #include "a.h"
- //
- // Here we might give different profiling results if we profile
- // the callee sub-expression, which is nullptr in the first case
- // an UnresolvedLookupExpr in the second case where there is a
- // global operator&& operator that pollutes the fold expression.
+ // Both definitions have identically written fold expressions, but semantic
+ // analysis adds the overloaded operator to the second one.
if (S->getLHS())
Visit(S->getLHS());
else
More information about the cfe-commits
mailing list