-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[clang] Implement CWG2611 #133747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[clang] Implement CWG2611 #133747
Conversation
CWG2611 "Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation"
@llvm/pr-subscribers-clang Author: None (offsetof) ChangesCWG2611 "Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation" Fixes #57396 Full diff: https://github.com/llvm/llvm-project/pull/133747.diff 6 Files Affected:
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@-1 {{because 'sizeof(char[11]) == 12' (11 == 12) evaluated to false}}
-// expected-note@-2 {{because 'sizeof(char[10]) == 12' (10 == 12) evaluated to false}}
-// expected-note@-3 3{{because 'sizeof(int) == 12' (4 == 12) evaluated to false}}
-// expected-note@-4 6{{because 'sizeof(short) == 12' (2 == 12) evaluated to false}}
+// expected-note@-1 {{because '(sizeof(char[11])) == 12' (11 == 12) evaluated to false}}
+// expected-note@-2 {{because '(sizeof(char[10])) == 12' (10 == 12) evaluated to false}}
+// expected-note@-3 3{{because '(sizeof(int)) == 12' (4 == 12) evaluated to false}}
+// expected-note@-4 6{{because '(sizeof(short)) == 12' (2 == 12) evaluated to false}}
template<C3 T1, C3 T2, C3 T3>
// expected-note@-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@-1 {{because 'false_v<requires (short ts, unsigned short ts) { requires (sizeof (ts) == 2) && (sizeof (ts) == 2); }>'}}
+// expected-note@-1 {{because 'false_v<requires (short ts, unsigned short ts) { requires ((sizeof (ts) == 2) && (sizeof (ts) == 2)); }>'}}
// expected-note@-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@-1 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; } && requires (unsigned short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
-// expected-note@-2 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
+// expected-note@-1 {{because 'false_v<(requires (short ts) { requires sizeof (ts) != 0; } && requires (unsigned short ts) { requires sizeof (ts) != 0; })>' evaluated to false}}
+// expected-note@-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@-1 {{because 'false_v<requires { typename identity<short>::type; } && requires { typename identity<int>::type; } && requires { <<error-type>>; }>' evaluated to false}}
+ // expected-note@-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@-1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}}
+ // expected-note@-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@-1 {{because 'false_v<requires { { 0 } noexcept -> C1<int>; } && requires { { 0 } noexcept -> C1<unsigned int>; }>' evaluated to false}}
+ // expected-note@-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@-1 {{because 'false_v<requires { requires sizeof(int) == 0; } && requires { requires sizeof(short) == 0; }>' evaluated to false}}
+ // expected-note@-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@-1 {{because 'false_v<requires { requires sizeof(int) == 0; sizeof(Ts); } && requires { requires sizeof(short) == 0; sizeof(Ts); }>' evaluated to false}}
+// expected-note@-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@-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@-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>
|
clang/test/CXX/drs/cwg26xx.cpp
Outdated
@@ -35,6 +35,29 @@ namespace std { | |||
template<typename T> T declval(); | |||
} // namespace std | |||
|
|||
namespace cwg2611 { // cwg2611: 21 | |||
#if __cpp_fold_expressions >= 201603 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#if __cpp_fold_expressions >= 201603 | |
#if __cplusplus >= 201703L |
Replace feature-test macro with `__cplusplus`
clang/lib/Sema/TreeTransform.h
Outdated
// 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(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would create the Paren Expr directly here, we don't need to do all of the work RebuildParenExpr is doing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks; updated.
#if __cplusplus >= 201703L | ||
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(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add __is_same_as
tests as in the issue, that's the (only) tests we get wrong
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These testcases are checking the same thing as the one in the issue (if I understand correctly what you're referring to). Could you clarify what you think is missing?
CWG2611 "Missing parentheses in expansion of fold-expression could cause syntactic reinterpretation"
Fixes #57396