[clang] [clang] Implement CWG2611 (PR #133747)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 31 09:44:04 PDT 2025
https://github.com/offsetof created https://github.com/llvm/llvm-project/pull/133747
CWG2611 "Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation"
Fixes #57396
>From 175e2531de9e2fceb72b25aed5073b225e2495ac Mon Sep 17 00:00:00 2001
From: offsetof <offsetof at mailo.com>
Date: Mon, 31 Mar 2025 16:35:03 +0000
Subject: [PATCH] [clang] Implement CWG2611
CWG2611 "Missing parentheses in expansion of fold-expression could cause
syntactic reinterpretation"
---
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/Sema/TreeTransform.h | 15 +++++++++---
clang/test/CXX/drs/cwg26xx.cpp | 23 +++++++++++++++++++
clang/test/CXX/temp/temp.param/p10-2a.cpp | 8 +++----
.../instantiate-requires-expr.cpp | 20 ++++++++--------
clang/www/cxx_dr_status.html | 2 +-
6 files changed, 51 insertions(+), 18 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index daad01919ecd4..51b575f1b15d1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -110,6 +110,7 @@ Resolutions to C++ Defect Reports
two releases. The improvements to template template parameter matching implemented
in the previous release, as described in P3310 and P3579, made this flag unnecessary.
+- Implemented `CWG2611 Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation <https://cplusplus.github.io/CWG/issues/2611>`_
- Implemented `CWG2918 Consideration of constraints for address of overloaded `
`function <https://cplusplus.github.io/CWG/issues/2918.html>`_
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8fdb2cf6dce6c..aa23da298864f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16441,14 +16441,23 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
return true;
}
- if (ParenExpr *PE = dyn_cast_or_null<ParenExpr>(Result.get()))
- PE->setIsProducedByFoldExpansion();
-
// If we had no init and an empty pack, and we're not retaining an expansion,
// then produce a fallback value or error.
if (Result.isUnset())
return getDerived().RebuildEmptyCXXFoldExpr(E->getEllipsisLoc(),
E->getOperator());
+
+ // Wrap the result in a ParenExpr if it isn't already one (see CWG2611).
+ ParenExpr *PE = dyn_cast<ParenExpr>(Result.get());
+ if (!PE) {
+ Result = getDerived().RebuildParenExpr(Result.get(), E->getLParenLoc(),
+ E->getRParenLoc());
+ if (Result.isInvalid())
+ return true;
+ PE = cast<ParenExpr>(Result.get());
+ }
+ PE->setIsProducedByFoldExpansion();
+
return Result;
}
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index 3eb70583b6026..5bb955dedd766 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -35,6 +35,29 @@ namespace std {
template<typename T> T declval();
} // namespace std
+namespace cwg2611 { // cwg2611: 21
+#if __cpp_fold_expressions >= 201603
+template<class> class R {};
+template<class... Ts> auto a(Ts... xs) -> R<decltype((xs + ...))>;
+template<class... Ts> auto b(Ts... xs) -> R<decltype((..., xs))>;
+template<class... Ts> auto c(Ts... xs, int i = 0) -> R<decltype((xs * ... * i))>;
+template<class... Ts> auto d(Ts... xs, int i = 0) -> R<decltype((i ^ ... ^ xs))>;
+R<int&> i = a(1);
+R<int&> j = b(1);
+R<int&> k = c();
+R<int&> l = d();
+
+template<int... Is> constexpr int w = 0 * (Is + ...);
+template<int... Is> constexpr int x = 0 * (... + Is);
+template<int... Is> constexpr int y = 0 * (Is + ... + 1);
+template<int... Is> constexpr int z = 0 * (1 + ... + Is);
+static_assert(w<1, 2> == 0);
+static_assert(x<1, 2> == 0);
+static_assert(y<1, 2> == 0);
+static_assert(z<1, 2> == 0);
+#endif
+} // namespace cwg2611
+
namespace cwg2621 { // cwg2621: sup 2877
#if __cplusplus >= 202002L
enum class E { a };
diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp
index 4f5fdd3b4809a..281c66bf9da90 100644
--- a/clang/test/CXX/temp/temp.param/p10-2a.cpp
+++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp
@@ -27,10 +27,10 @@ using b4 = B<short, char>; // expected-error {{constraints not satisfied for ali
template<typename... T>
concept C3 = (sizeof(T) + ...) == 12;
-// expected-note at -1 {{because 'sizeof(char[11]) == 12' (11 == 12) evaluated to false}}
-// expected-note at -2 {{because 'sizeof(char[10]) == 12' (10 == 12) evaluated to false}}
-// expected-note at -3 3{{because 'sizeof(int) == 12' (4 == 12) evaluated to false}}
-// expected-note at -4 6{{because 'sizeof(short) == 12' (2 == 12) evaluated to false}}
+// expected-note at -1 {{because '(sizeof(char[11])) == 12' (11 == 12) evaluated to false}}
+// expected-note at -2 {{because '(sizeof(char[10])) == 12' (10 == 12) evaluated to false}}
+// expected-note at -3 3{{because '(sizeof(int)) == 12' (4 == 12) evaluated to false}}
+// expected-note at -4 6{{because '(sizeof(short)) == 12' (2 == 12) evaluated to false}}
template<C3 T1, C3 T2, C3 T3>
// expected-note at -1 {{because 'char[11]' does not satisfy 'C3'}}
diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
index a1f5456156a06..2930c5a2eb05d 100644
--- a/clang/test/SemaTemplate/instantiate-requires-expr.cpp
+++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
@@ -35,7 +35,7 @@ using r1i2 = r1<char>; // expected-error {{constraints not satisfied for class t
template<typename... Ts> requires
false_v<requires (Ts... ts) {requires ((sizeof(ts) == 2) && ...);}>
-// expected-note at -1 {{because 'false_v<requires (short ts, unsigned short ts) { requires (sizeof (ts) == 2) && (sizeof (ts) == 2); }>'}}
+// expected-note at -1 {{because 'false_v<requires (short ts, unsigned short ts) { requires ((sizeof (ts) == 2) && (sizeof (ts) == 2)); }>'}}
// expected-note at -2 {{because 'false_v<requires (short ts) { requires (sizeof (ts) == 2); }>' evaluated to false}}
struct r2 {};
@@ -44,8 +44,8 @@ using r2i2 = r2<short>; // expected-error {{constraints not satisfied for class
template<typename... Ts> requires
false_v<(requires (Ts ts) {requires sizeof(ts) != 0;} && ...)>
-// expected-note at -1 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; } && requires (unsigned short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
-// expected-note at -2 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
+// expected-note at -1 {{because 'false_v<(requires (short ts) { requires sizeof (ts) != 0; } && requires (unsigned short ts) { requires sizeof (ts) != 0; })>' evaluated to false}}
+// expected-note at -2 {{because 'false_v<(requires (short ts) { requires sizeof (ts) != 0; })>' evaluated to false}}
struct r3 {};
using r3i1 = r3<short, unsigned short>; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = <short, unsigned short>]}}
@@ -93,19 +93,19 @@ namespace type_requirement {
// Parameter pack inside expr
template<typename... Ts> requires
false_v<(requires { typename Ts::type; } && ...)>
- // expected-note at -1 {{because 'false_v<requires { typename identity<short>::type; } && requires { typename identity<int>::type; } && requires { <<error-type>>; }>' evaluated to false}}
+ // expected-note at -1 {{because 'false_v<(requires { typename identity<short>::type; } && requires { typename identity<int>::type; } && requires { <<error-type>>; })>' evaluated to false}}
struct r5 {};
using r5i = r5<identity<short>, identity<int>, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = <identity<short>, identity<int>, short>]}}
template<typename... Ts> requires
- false_v<(requires { typename void_t<Ts>; } && ...)> // expected-note{{because 'false_v<requires { typename void_t<int>; } && requires { typename void_t<short>; }>' evaluated to false}}
+ false_v<(requires { typename void_t<Ts>; } && ...)> // expected-note{{because 'false_v<(requires { typename void_t<int>; } && requires { typename void_t<short>; })>' evaluated to false}}
struct r6 {};
using r6i = r6<int, short>; // expected-error{{constraints not satisfied for class template 'r6' [with Ts = <int, short>]}}
template<typename... Ts> requires
false_v<(requires { typename Ts::template aaa<Ts>; } && ...)>
- // expected-note at -1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}}
+ // expected-note at -1 {{because 'false_v<(requires { <<error-type>>; } && requires { <<error-type>>; })>' evaluated to false}}
struct r7 {};
using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, type_requirement::A>]}}
@@ -160,7 +160,7 @@ namespace expr_requirement {
template<typename... Ts> requires
false_v<(requires { { 0 } noexcept -> C1<Ts>; } && ...)>
- // expected-note at -1 {{because 'false_v<requires { { 0 } noexcept -> C1<int>; } && requires { { 0 } noexcept -> C1<unsigned int>; }>' evaluated to false}}
+ // expected-note at -1 {{because 'false_v<(requires { { 0 } noexcept -> C1<int>; } && requires { { 0 } noexcept -> C1<unsigned int>; })>' evaluated to false}}
struct r3 {};
using r3i = r3<int, unsigned int>; // expected-error{{constraints not satisfied for class template 'r3' [with Ts = <int, unsigned int>]}}
@@ -199,7 +199,7 @@ namespace nested_requirement {
// Parameter pack inside expr
template<typename... Ts> requires
false_v<(requires { requires sizeof(Ts) == 0; } && ...)>
- // expected-note at -1 {{because 'false_v<requires { requires sizeof(int) == 0; } && requires { requires sizeof(short) == 0; }>' evaluated to false}}
+ // expected-note at -1 {{because 'false_v<(requires { requires sizeof(int) == 0; } && requires { requires sizeof(short) == 0; })>' evaluated to false}}
struct r2 {};
using r2i = r2<int, short>; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = <int, short>]}}
@@ -208,14 +208,14 @@ namespace nested_requirement {
// Parameter pack inside multiple requirements
template<typename... Ts> requires
false_v<(requires { requires sizeof(Ts) == 0; sizeof(Ts); } && ...)>
-// expected-note at -1 {{because 'false_v<requires { requires sizeof(int) == 0; sizeof(Ts); } && requires { requires sizeof(short) == 0; sizeof(Ts); }>' evaluated to false}}
+// expected-note at -1 {{because 'false_v<(requires { requires sizeof(int) == 0; sizeof(Ts); } && requires { requires sizeof(short) == 0; sizeof(Ts); })>' evaluated to false}}
struct r4 {};
using r4i = r4<int, short>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int, short>]}}
template<typename... Ts> requires
false_v<(requires(Ts t) { requires sizeof(t) == 0; t++; } && ...)>
-// expected-note at -1 {{because 'false_v<requires (int t) { requires sizeof (t) == 0; t++; } && requires (short t) { requires sizeof (t) == 0; t++; }>' evaluated to false}}
+// expected-note at -1 {{because 'false_v<(requires (int t) { requires sizeof (t) == 0; t++; } && requires (short t) { requires sizeof (t) == 0; t++; })>' evaluated to false}}
struct r5 {};
using r5i = r5<int, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = <int, short>]}}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 138f12facf0ad..55d16a5da0174 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -15513,7 +15513,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2611.html">2611</a></td>
<td>C++23</td>
<td>Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 21</td>
</tr>
<tr id="2612">
<td><a href="https://cplusplus.github.io/CWG/issues/2612.html">2612</a></td>
More information about the cfe-commits
mailing list