Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -118,6 +118,9 @@ def warn_float_underflow : Warning< "magnitude of floating-point constant too small for type %0; minimum is %1">, InGroup; +def warn_tautological_float_compare : Warning< + "floating-point comparison is always false: constant cannot be represented exactly in type %0">, + InGroup; def warn_double_const_requires_fp64 : Warning< "double precision constant requires %select{cl_khr_fp64|cl_khr_fp64 and __opencl_c_fp64}0, " "casting to single precision">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -12922,7 +12922,8 @@ const FunctionDecl *FD = nullptr); public: - void CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS); + void CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS, + BinaryOperatorKind Opcode); private: void CheckImplicitConversions(Expr *E, SourceLocation CC = SourceLocation()); Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -11436,12 +11436,43 @@ CheckPPCMMAType(RetValExp->getType(), ReturnLoc); } -//===--- CHECK: Floating-Point comparisons (-Wfloat-equal) ---------------===// +/// Check for comparisons of floating-point values using == and !=. Issue a +/// warning if the comparison is not likely to do what the programmer intended. +void Sema::CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS, + BinaryOperatorKind Opcode) { + // Match and capture subexpressions such as "(float) X == 0.1". + FloatingLiteral *FPLiteral; + CastExpr *FPCast; + auto getCastAndLiteral = [&FPLiteral, &FPCast](Expr *L, Expr *R) { + FPLiteral = dyn_cast(L->IgnoreParens()); + FPCast = dyn_cast(R->IgnoreParens()); + return FPLiteral && FPCast; + }; + + if (getCastAndLiteral(LHS, RHS) || getCastAndLiteral(RHS, LHS)) { + auto *SourceBT = FPCast->getSubExpr()->getType()->getAs(); + auto *TargetBT = FPLiteral->getType()->getAs(); + if (SourceBT && TargetBT && SourceBT->isFloatingPoint() && + TargetBT->isFloatingPoint()) { + bool Lossy; + llvm::APFloat TargetC = FPLiteral->getValue(); + TargetC.convert(Context.getFloatTypeSemantics(QualType(SourceBT, 0)), + llvm::APFloat::rmNearestTiesToEven, &Lossy); + if (Lossy) { + // If the literal cannot be represented in the source type, then a + // check for equality is always false. + if (Opcode == BO_EQ) + Diag(Loc, diag::warn_tautological_float_compare) + << FPCast->getSubExpr()->getType() << LHS->getSourceRange() + << RHS->getSourceRange(); + + // TODO: Add warning for != comparison. It's an !isnan() in disguise. + return; + } + } + } -/// Check for comparisons of floating point operands using != and ==. -/// Issue a warning if these are no self-comparisons, as they are not likely -/// to do what the programmer intended. -void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) { + // Match a more general floating-point equality comparison (-Wfloat-equal). Expr* LeftExprSansParen = LHS->IgnoreParenImpCasts(); Expr* RightExprSansParen = RHS->IgnoreParenImpCasts(); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -11989,7 +11989,7 @@ // Check for comparisons of floating point operands using != and ==. if (Type->hasFloatingRepresentation() && BinaryOperator::isEqualityOp(Opc)) - S.CheckFloatComparison(Loc, LHS.get(), RHS.get()); + S.CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); // The result of comparisons is 'bool' in C++, 'int' in C. return S.Context.getLogicalOperationType(); @@ -12580,7 +12580,7 @@ if (BinaryOperator::isEqualityOp(Opc) && LHSType->hasFloatingRepresentation()) { assert(RHS.get()->getType()->hasFloatingRepresentation()); - CheckFloatComparison(Loc, LHS.get(), RHS.get()); + CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); } // Return a signed type for the vector. Index: clang/test/Sema/floating-point-compare.c =================================================================== --- clang/test/Sema/floating-point-compare.c +++ clang/test/Sema/floating-point-compare.c @@ -12,6 +12,7 @@ return x == x; // no-warning } +// 0.0 can be represented exactly, so don't warn. int f4(float x) { return x == 0.0; // no-warning {{comparing}} } @@ -20,6 +21,29 @@ return x == __builtin_inf(); // no-warning } +// The literal is a double that can't be represented losslessly as a float. int f7(float x) { - return x == 3.14159; // expected-warning {{comparing}} + return x == 3.14159; // expected-warning {{floating-point comparison is always false}} +} + +// The literal is a double that can be represented losslessly as a long double, +// but this might not be the comparison what was intended. +int not_tautological_FP_compare(long double f) { + return f == 0.1; // expected-warning {{comparing floating point with ==}} +} + +int tautological_FP_compare_commute(float f) { + return 0.3 == f; // expected-warning {{floating-point comparison is always false}} +} + +int tautological_FP_compare_explicit_upcast(float f) { + return 0.3 == (double) f; // expected-warning {{floating-point comparison is always false}} +} + +int tautological_FP_compare_explicit_downcast(double d) { + return 0.3 == (float) d; // expected-warning {{floating-point comparison is always false}} +} + +int tautological_FP_compare_ignore_parens(float f) { + return f == (0.3); // expected-warning {{floating-point comparison is always false}} }