https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/136836
>From 8d70f4bb100ad0dec57947ce0999a7f837d2646e Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Wed, 23 Apr 2025 11:50:10 +0200 Subject: [PATCH 1/5] [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 --- clang/docs/ReleaseNotes.rst | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 5 ++++ clang/lib/Sema/TreeTransform.h | 8 +++++++ clang/test/Parser/cxx1z-fold-expressions.cpp | 4 +++- .../SemaTemplate/cxx1z-fold-expressions.cpp | 23 +++++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) 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); + } + +}; >From 7e53392a9c7cee9c09b107c1447fd904e63fdaf2 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Wed, 23 Apr 2025 12:30:11 +0200 Subject: [PATCH 2/5] format --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 10 ++++++---- clang/lib/Sema/TreeTransform.h | 14 ++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b1d261193763a..5f636a713e34c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7138,10 +7138,12 @@ 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_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">, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index ba107da82d683..7ecf30dd481ce 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -16440,12 +16440,14 @@ 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(); - } + 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 >From 7c61d91b5d6cef29bac947abd14270e8232c8461 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Wed, 23 Apr 2025 14:18:19 +0200 Subject: [PATCH 3/5] Address aaron's feedback --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 10 ++++------ clang/test/Parser/cxx1z-fold-expressions.cpp | 2 +- clang/test/SemaTemplate/cxx1z-fold-expressions.cpp | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5f636a713e34c..361ee3e4a737d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7138,12 +7138,10 @@ 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_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; def warn_enum_constant_in_bool_context : Warning< "converting the enum constant to a boolean">, diff --git a/clang/test/Parser/cxx1z-fold-expressions.cpp b/clang/test/Parser/cxx1z-fold-expressions.cpp index 3ba9a0932ccf1..4a329646b799f 100644 --- a/clang/test/Parser/cxx1z-fold-expressions.cpp +++ b/clang/test/Parser/cxx1z-fold-expressions.cpp @@ -52,7 +52,7 @@ 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}} +// expected-error@-1 {{comparison in fold expression}} template <int... N> constexpr int rightShift() { return (N >> ...); } diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp index 7dc67fe8d6bf7..61cf710aeba52 100644 --- a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp +++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp @@ -143,9 +143,9 @@ namespace comparison_warning { template <typename...T> void f(T... ts) { (void)(ts == ...); - // expected-error@-1{{comparison in a fold expression would evaluate to '(X == Y) == Z'}} + // expected-error@-1{{comparison in fold expression would evaluate to '(X == Y) == Z'}} (void)(ts < ...); - // expected-error@-1{{comparison in a fold expression would evaluate to '(X < Y) < Z'}} + // expected-error@-1{{comparison in fold expression would evaluate to '(X < Y) < Z'}} } void test() { >From 5a2297bbce7be13f5eeacc15b462bb3d5f7f7d98 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Wed, 23 Apr 2025 17:02:02 +0200 Subject: [PATCH 4/5] add tests --- clang/test/SemaTemplate/cxx1z-fold-expressions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp index 61cf710aeba52..34987758abfd4 100644 --- a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp +++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp @@ -143,13 +143,14 @@ namespace comparison_warning { template <typename...T> void f(T... ts) { (void)(ts == ...); - // expected-error@-1{{comparison in fold expression would evaluate to '(X == Y) == Z'}} + // expected-error@-1 2{{comparison in fold expression would evaluate to '(X == Y) == Z'}} (void)(ts < ...); - // expected-error@-1{{comparison in fold expression would evaluate to '(X < Y) < Z'}} + // expected-error@-1 2{{comparison in fold expression would evaluate to '(X < Y) < Z'}} } void test() { f(0, 1, 2); // expected-note{{in instantiation}} + f(0, 1); // expected-note{{in instantiation}} f(S{}, S{}); f(0); } >From 2b8e3b0843d73f67175e1d5a9c38f377165a4b10 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Thu, 1 May 2025 11:12:26 +0200 Subject: [PATCH 5/5] add dpplicasted wordings for right folds --- clang/include/clang/Sema/Sema.h | 6 ++++-- clang/lib/Sema/SemaExpr.cpp | 15 ++++++++------- clang/lib/Sema/TreeTransform.h | 12 +++++++----- .../test/SemaTemplate/cxx1z-fold-expressions.cpp | 3 +++ 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a757f4c6430ae..9c69ac0b09fd8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7182,13 +7182,15 @@ class Sema final : public SemaBase { ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, tok::TokenKind Kind, Expr *LHSExpr, Expr *RHSExpr); ExprResult BuildBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr); + Expr *LHSExpr, Expr *RHSExpr, + bool ForFoldExpression = false); /// CreateBuiltinBinOp - Creates a new built-in binary operation with /// operator @p Opc at location @c TokLoc. This routine only supports /// built-in operations; ActOnBinOp handles overloaded operators. ExprResult CreateBuiltinBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr); + Expr *LHSExpr, Expr *RHSExpr, + bool ForFoldExpression = false); void LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, UnresolvedSetImpl &Functions); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9cd1a7e45fb64..7b62281712cf6 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14918,8 +14918,8 @@ static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx, } ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr) { + BinaryOperatorKind Opc, Expr *LHSExpr, + Expr *RHSExpr, bool ForFoldExpression) { if (getLangOpts().CPlusPlus11 && isa<InitListExpr>(RHSExpr)) { // The syntax only allows initializer lists on the RHS of assignment, // so we don't need to worry about accepting invalid code for @@ -15050,7 +15050,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); if (const auto *BI = dyn_cast<BinaryOperator>(LHSExpr); - BI && BI->isComparisonOp()) + !ForFoldExpression && BI && BI->isComparisonOp()) Diag(OpLoc, diag::warn_consecutive_comparison) << BI->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc); @@ -15460,8 +15460,8 @@ static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc, } ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr) { + BinaryOperatorKind Opc, Expr *LHSExpr, + Expr *RHSExpr, bool ForFoldExpression) { ExprResult LHS, RHS; std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); if (!LHS.isUsable() || !RHS.isUsable()) @@ -15535,7 +15535,8 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, LHSExpr->getType()->isOverloadableType())) return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); - return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr, + ForFoldExpression); } // Don't resolve overloads if the other type is overloadable. @@ -15599,7 +15600,7 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, } // Build a built-in binary operation. - return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr, ForFoldExpression); } static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 7ecf30dd481ce..f83bd1707be82 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2967,10 +2967,11 @@ class TreeTransform { /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildBinaryOperator(SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHS, Expr *RHS) { - return getSema().BuildBinOp(/*Scope=*/nullptr, OpLoc, Opc, LHS, RHS); + ExprResult RebuildBinaryOperator(SourceLocation OpLoc, BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS, + bool ForFoldExpression = false) { + return getSema().BuildBinOp(/*Scope=*/nullptr, OpLoc, Opc, LHS, RHS, + ForFoldExpression); } /// Build a new rewritten operator expression. @@ -16439,7 +16440,8 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) { Functions, LHS, RHS); } else { Result = getDerived().RebuildBinaryOperator(E->getEllipsisLoc(), - E->getOperator(), LHS, RHS); + E->getOperator(), LHS, RHS, + /*ForFoldExpresion=*/true); if (!WarnedOnComparison && Result.isUsable()) { if (auto *BO = dyn_cast<BinaryOperator>(Result.get()); BO && BO->isComparisonOp()) { diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp index 34987758abfd4..1ac821002f95c 100644 --- a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp +++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp @@ -137,6 +137,7 @@ void g(bool = (f<T>() || ...)); namespace comparison_warning { struct S { bool operator<(const S&) const; + bool operator<(int) const; bool operator==(const S&) const; }; @@ -146,6 +147,8 @@ namespace comparison_warning { // expected-error@-1 2{{comparison in fold expression would evaluate to '(X == Y) == Z'}} (void)(ts < ...); // expected-error@-1 2{{comparison in fold expression would evaluate to '(X < Y) < Z'}} + (void)(... < ts); + // expected-error@-1 2{{comparison in fold expression would evaluate to '(X < Y) < Z'}} } void test() { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits