Skip to content

Commit 8d70f4b

Browse files
committed
[Clang] Warning as error for fold expressions over comparison operators
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 #129570
1 parent 4cb9a37 commit 8d70f4b

File tree

5 files changed

+40
-1
lines changed

5 files changed

+40
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ Improvements to Clang's diagnostics
318318
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
319319
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
320320
``-Wno-error=parentheses``.
321+
- Similarly, fold expressions over a comparison operator are now an error by default.
321322
- Clang now better preserves the sugared types of pointers to member.
322323
- Clang now better preserves the presence of the template keyword with dependent
323324
prefixes.

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7138,6 +7138,11 @@ def warn_consecutive_comparison : Warning<
71387138
"chained comparison 'X %0 Y %1 Z' does not behave the same as a mathematical expression">,
71397139
InGroup<Parentheses>, DefaultError;
71407140

7141+
def warn_comparison_in_fold_expression : Warning<
7142+
"comparison in a fold expression would evaluate to '(X %0 Y) %0 Z' "
7143+
"which does not behave the same as a mathematical expression">,
7144+
InGroup<Parentheses>, DefaultError;
7145+
71417146
def warn_enum_constant_in_bool_context : Warning<
71427147
"converting the enum constant to a boolean">,
71437148
InGroup<IntInBoolContext>, DefaultIgnore;

clang/lib/Sema/TreeTransform.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16411,6 +16411,7 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
1641116411
return true;
1641216412
}
1641316413

16414+
bool WarnedOnComparison = false;
1641416415
for (unsigned I = 0; I != *NumExpansions; ++I) {
1641516416
Sema::ArgPackSubstIndexRAII SubstIndex(
1641616417
getSema(), LeftFold ? I : *NumExpansions - I - 1);
@@ -16439,6 +16440,13 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
1643916440
} else {
1644016441
Result = getDerived().RebuildBinaryOperator(E->getEllipsisLoc(),
1644116442
E->getOperator(), LHS, RHS);
16443+
if(!WarnedOnComparison && Result.isUsable()) {
16444+
if(auto * BO = dyn_cast<BinaryOperator>(Result.get()); BO && BO->isComparisonOp()) {
16445+
WarnedOnComparison = true;
16446+
SemaRef.Diag(BO->getBeginLoc(), diag::warn_comparison_in_fold_expression)
16447+
<< BO->getOpcodeStr();
16448+
}
16449+
}
1644216450
}
1644316451
} else
1644416452
Result = Out;

clang/test/Parser/cxx1z-fold-expressions.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ template<typename ...T> void as_operand_of_cast(int a, T ...t) {
5252

5353
// fold-operator can be '>' or '>>'.
5454
template <int... N> constexpr bool greaterThan() { return (N > ...); }
55+
// expected-error@-1 {{comparison in a fold expression}}
56+
5557
template <int... N> constexpr int rightShift() { return (N >> ...); }
5658

57-
static_assert(greaterThan<2, 1>());
59+
static_assert(greaterThan<2, 1>()); // expected-note {{in instantiation}}
5860
static_assert(rightShift<10, 1>() == 5);
5961

6062
template <auto V> constexpr auto Identity = V;

clang/test/SemaTemplate/cxx1z-fold-expressions.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,26 @@ bool f();
132132
template <typename... T>
133133
void g(bool = (f<T>() || ...));
134134
}
135+
136+
137+
namespace comparison_warning {
138+
struct S {
139+
bool operator<(const S&) const;
140+
bool operator==(const S&) const;
141+
};
142+
143+
template <typename...T>
144+
void f(T... ts) {
145+
(void)(ts == ...);
146+
// expected-error@-1{{comparison in a fold expression would evaluate to '(X == Y) == Z'}}
147+
(void)(ts < ...);
148+
// expected-error@-1{{comparison in a fold expression would evaluate to '(X < Y) < Z'}}
149+
}
150+
151+
void test() {
152+
f(0, 1, 2); // expected-note{{in instantiation}}
153+
f(S{}, S{});
154+
f(0);
155+
}
156+
157+
};

0 commit comments

Comments
 (0)