https://github.com/ChuanqiXu9 created 
https://github.com/llvm/llvm-project/pull/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.

>From 4d1fb62879f4035b2862cb6d3c68ec32355cc4bd Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Tue, 7 Apr 2026 13:43:10 +0800
Subject: [PATCH] [C++20] [Modules] Don't profiling the callee of CXXFoldExpr

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.
---
 clang/lib/AST/StmtProfile.cpp                 | 30 +++++++++++++++-
 .../callable-require-clause-merge.cppm        | 35 +++++++++++++++++++
 clang/test/Modules/polluted-operator.cppm     |  7 ----
 3 files changed, 64 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/Modules/callable-require-clause-merge.cppm

diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index e8c1f8a8ecb5f..af27a928edff6 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2390,7 +2390,35 @@ void StmtProfiler::VisitMaterializeTemporaryExpr(
 }
 
 void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) {
-  VisitExpr(S);
+  // For CXXFoldExpr, not profile the call expression as it may
+  // be affected by the context. e.g.,
+  //
+  // "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 may give different profile results to F::operator() in
+  // "c.h" vs other use cases of "a.h". This is problematic in
+  // cases where we may have expression coming from different
+  // headers, e.g., modules.
+  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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to