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 <[email protected]> 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 <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
