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 @@ -12918,7 +12918,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 @@ -11430,7 +11430,45 @@ /// 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) { +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); + FPCast = dyn_cast(R); + return FPLiteral && FPCast; + }; + + if (getCastAndLiteral(LHS, RHS) || getCastAndLiteral(RHS, LHS)) { + const Type *SourceTy = + Context.getCanonicalType(FPCast->getSubExpr()->getType()).getTypePtr(); + const Type *TargetTy = + Context.getCanonicalType(FPLiteral->getType()).getTypePtr(); + auto *SourceBT = dyn_cast(SourceTy); + auto *TargetBT = dyn_cast(TargetTy); + + // If the literal cannot be represented in the casted source type, then + // this probably is not the intended comparison. + 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 (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; + } + } + } + Expr* LeftExprSansParen = LHS->IgnoreParenImpCasts(); Expr* RightExprSansParen = RHS->IgnoreParenImpCasts(); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -11889,7 +11889,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(); @@ -12480,7 +12480,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,17 @@ 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}} }