-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[Clang] Warning as error for fold expressions over comparison operators #136836
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
Conversation
We made chained comparisons an error. Fold exprerssions over a comparison operators produce chained comparison, so we should be consistent there too. We only emit the warning when instantiating the fold expression as to not warn on types with user-defined comparisons. Partially addresses llvm#129570
|
@llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) ChangesWe made chained comparisons an error. We only emit the warning when instantiating the fold expression so as not to warn on types with user-defined comparisons. Partially addresses #129570 Full diff: https://github.com/llvm/llvm-project/pull/136836.diff 5 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f5cd1fbeabcfe..3905aef111394 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -318,6 +318,7 @@ Improvements to Clang's diagnostics
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
``-Wno-error=parentheses``.
+- Similarly, fold expressions over a comparison operator are now an error by default.
- Clang now better preserves the sugared types of pointers to member.
- Clang now better preserves the presence of the template keyword with dependent
prefixes.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c29a3422acd26..b1d261193763a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7138,6 +7138,11 @@ def warn_consecutive_comparison : Warning<
"chained comparison 'X %0 Y %1 Z' does not behave the same as a mathematical expression">,
InGroup<Parentheses>, DefaultError;
+def warn_comparison_in_fold_expression : Warning<
+ "comparison in a fold expression would evaluate to '(X %0 Y) %0 Z' "
+ "which does not behave the same as a mathematical expression">,
+ InGroup<Parentheses>, DefaultError;
+
def warn_enum_constant_in_bool_context : Warning<
"converting the enum constant to a boolean">,
InGroup<IntInBoolContext>, DefaultIgnore;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 2469991bf2ce8..ba107da82d683 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16411,6 +16411,7 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
return true;
}
+ bool WarnedOnComparison = false;
for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgPackSubstIndexRAII SubstIndex(
getSema(), LeftFold ? I : *NumExpansions - I - 1);
@@ -16439,6 +16440,13 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
} else {
Result = getDerived().RebuildBinaryOperator(E->getEllipsisLoc(),
E->getOperator(), LHS, RHS);
+ if(!WarnedOnComparison && Result.isUsable()) {
+ if(auto * BO = dyn_cast<BinaryOperator>(Result.get()); BO && BO->isComparisonOp()) {
+ WarnedOnComparison = true;
+ SemaRef.Diag(BO->getBeginLoc(), diag::warn_comparison_in_fold_expression)
+ << BO->getOpcodeStr();
+ }
+ }
}
} else
Result = Out;
diff --git a/clang/test/Parser/cxx1z-fold-expressions.cpp b/clang/test/Parser/cxx1z-fold-expressions.cpp
index ac27111697737..3ba9a0932ccf1 100644
--- a/clang/test/Parser/cxx1z-fold-expressions.cpp
+++ b/clang/test/Parser/cxx1z-fold-expressions.cpp
@@ -52,9 +52,11 @@ template<typename ...T> void as_operand_of_cast(int a, T ...t) {
// fold-operator can be '>' or '>>'.
template <int... N> constexpr bool greaterThan() { return (N > ...); }
+// expected-error@-1 {{comparison in a fold expression}}
+
template <int... N> constexpr int rightShift() { return (N >> ...); }
-static_assert(greaterThan<2, 1>());
+static_assert(greaterThan<2, 1>()); // expected-note {{in instantiation}}
static_assert(rightShift<10, 1>() == 5);
template <auto V> constexpr auto Identity = V;
diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
index 47a252eb335f6..7dc67fe8d6bf7 100644
--- a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
+++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
@@ -132,3 +132,26 @@ bool f();
template <typename... T>
void g(bool = (f<T>() || ...));
}
+
+
+namespace comparison_warning {
+ struct S {
+ bool operator<(const S&) const;
+ bool operator==(const S&) const;
+ };
+
+ template <typename...T>
+ void f(T... ts) {
+ (void)(ts == ...);
+ // expected-error@-1{{comparison in a fold expression would evaluate to '(X == Y) == Z'}}
+ (void)(ts < ...);
+ // expected-error@-1{{comparison in a fold expression would evaluate to '(X < Y) < Z'}}
+ }
+
+ void test() {
+ f(0, 1, 2); // expected-note{{in instantiation}}
+ f(S{}, S{});
+ f(0);
+ }
+
+};
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
AaronBallman
left a comment
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.
LGTM with a minor rewording and reformatting of the diagnostic message.
| def warn_comparison_in_fold_expression | ||
| : Warning< | ||
| "comparison in a fold expression would evaluate to '(X %0 Y) %0 Z' " | ||
| "which does not behave the same as a mathematical expression">, | ||
| InGroup<Parentheses>, | ||
| DefaultError; |
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.
| def warn_comparison_in_fold_expression | |
| : Warning< | |
| "comparison in a fold expression would evaluate to '(X %0 Y) %0 Z' " | |
| "which does not behave the same as a mathematical expression">, | |
| InGroup<Parentheses>, | |
| DefaultError; | |
| def warn_comparison_in_fold_expression : Warning< | |
| "comparison in fold expression would evaluate to '(X %0 Y) %0 Z' " | |
| "which does not behave the same as a mathematical expression">, | |
| InGroup<Parentheses>, DefaultError; |
zyn0217
left a comment
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
| InGroup<Parentheses>, DefaultError; | ||
|
|
||
| def warn_comparison_in_fold_expression : Warning< | ||
| "comparison in fold expression would evaluate to '(X %0 Y) %0 Z' " |
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.
It might be better to print out the expression than X, Y, Z
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.
And I'm thinking if there's a case where it actually evaluates to X %0 (Y %0 Z)
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.
We added that discussion for the previous PR, we concluded it was really not useful!
| void test() { | ||
| f(0, 1, 2); // expected-note{{in instantiation}} | ||
| f(S{}, S{}); | ||
| f(0); |
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.
can you test f(0, 0)?
|
https://compiler-explorer.com/z/zoze5br4d it turns out that we already produce a warning for right fold... hum, this will require more work |
|
|
||
| void test() { | ||
| f(0, 1, 2); // expected-note{{in instantiation}} | ||
| f(0, 1); // expected-note{{in instantiation}} |
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.
Hmmm. It looks strange to me if we warn for '0 < 1'
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.
This is intended -, ideally, we would even warn when n <= 1 (ie, the size of the pack should not affect the warning) - but that would be annoying because there could be user-defined operators, and we only discover that for n >=2.
|
|
||
| if (const auto *BI = dyn_cast<BinaryOperator>(LHSExpr); | ||
| BI && BI->isComparisonOp()) | ||
| !ForFoldExpression && BI && BI->isComparisonOp()) |
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.
The right fold cases are handled by this branch, right? So shall we rename ForFoldExpression to ForLeftFold or something?
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.
That's just a side effect of looking at the LHS first - I don't think the distinction matters here
zyn0217
left a comment
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.
Okay, let's go ahead as is. Though I feel it's unfortunate that the warnings for left fold and right fold are scattered - maybe we could clean them up in future
Thanks for working on this.
This is not the case, there is a warning for the fold case (regardless of direction) in TreeTransform, and a different warning for the non-fold case in CreateBinOp |
…rs (llvm#136836) We made chained comparisons an error. Fold-expressions over a comparison operator produce chained comparisons, so we should be consistent there too. We only emit the warning when instantiating the fold expression so as not to warn on types with user-defined comparisons. Partially addresses llvm#129570
…rs (llvm#136836) We made chained comparisons an error. Fold-expressions over a comparison operator produce chained comparisons, so we should be consistent there too. We only emit the warning when instantiating the fold expression so as not to warn on types with user-defined comparisons. Partially addresses llvm#129570
We made chained comparisons an error.
Fold-expressions over a comparison operator produce chained comparisons, so we should be consistent there too.
We only emit the warning when instantiating the fold expression so as not to warn on types with user-defined comparisons.
Partially addresses #129570