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/5] 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/5] 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 >From 9c511e2b401bb9245d4bfa8cc31de11b131d4376 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <[email protected]> Date: Fri, 8 May 2026 10:30:51 +0800 Subject: [PATCH 3/5] remove modules test --- .../callable-require-clause-merge.cppm | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 clang/test/Modules/callable-require-clause-merge.cppm diff --git a/clang/test/Modules/callable-require-clause-merge.cppm b/clang/test/Modules/callable-require-clause-merge.cppm deleted file mode 100644 index ae49dd7a58542..0000000000000 --- a/clang/test/Modules/callable-require-clause-merge.cppm +++ /dev/null @@ -1,35 +0,0 @@ -// 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(); } >From 5ffea11bc11f452e29718cc341583f3bb553003d Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <[email protected]> Date: Fri, 8 May 2026 10:44:04 +0800 Subject: [PATCH 4/5] Rename newly introduced test --- .../SemaCXX/{diagnose-redefinition-fold-expr.cpp => GH190333.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clang/test/SemaCXX/{diagnose-redefinition-fold-expr.cpp => GH190333.cpp} (100%) diff --git a/clang/test/SemaCXX/diagnose-redefinition-fold-expr.cpp b/clang/test/SemaCXX/GH190333.cpp similarity index 100% rename from clang/test/SemaCXX/diagnose-redefinition-fold-expr.cpp rename to clang/test/SemaCXX/GH190333.cpp >From 3fb97439b475d29d5bf34bffb935ada9f7852e02 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <[email protected]> Date: Fri, 8 May 2026 11:14:09 +0800 Subject: [PATCH 5/5] remove polluted-operator.cppm test --- clang/test/Modules/polluted-operator.cppm | 72 ----------------------- 1 file changed, 72 deletions(-) delete mode 100644 clang/test/Modules/polluted-operator.cppm diff --git a/clang/test/Modules/polluted-operator.cppm b/clang/test/Modules/polluted-operator.cppm deleted file mode 100644 index e81a63c3e03de..0000000000000 --- a/clang/test/Modules/polluted-operator.cppm +++ /dev/null @@ -1,72 +0,0 @@ -// RUN: rm -rf %t -// 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 %t/b.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/b.pcm -verify -// -// Testing the behavior of `-fskip-odr-check-in-gmf` -// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf -emit-module-interface %t/a.cppm -o \ -// RUN: %t/a.pcm -// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/b.cppm -fprebuilt-module-path=%t \ -// RUN: -emit-module-interface -DSKIP_ODR_CHECK_IN_GMF -o %t/b.pcm -verify - -// RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/a.cppm -o %t/a.pcm -// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -emit-reduced-module-interface \ -// RUN: -o %t/b.pcm -verify -DREDUCED - -//--- foo.h - -namespace std -{ - template<class _Dom1> - void operator &&(_Dom1 __v, _Dom1 __w) - { - return; - } -} - -//--- bar.h -namespace std -{ - template<typename... _Types> - struct _Traits - { - static constexpr bool _S_copy_ctor = - (__is_trivial(_Types) && ...); - }; - - template<typename... _Types> - struct variant - { - void - swap(variant& __rhs) - noexcept((__is_trivial(_Types) && ...)) - { - } - }; -} - -//--- a.cppm -module; -#include "foo.h" -#include "bar.h" -export module a; -export namespace std { - using std::variant; - using std::_Traits; - using std::operator&&; -} - -//--- b.cppm -module; -#include "bar.h" -export module b; -import a; -export namespace std { - using std::variant; - using std::_Traits; - using std::operator&&; -} - -// expected-no-diagnostics _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
