From: Richard Trieu Date: Thu, 21 Apr 2016 21:04:55 +0000 (+0000) Subject: Split interesting warnings off from -Wfloat-conversion X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c188bce6b37bf75d728943bb208415b018134db;p=clang Split interesting warnings off from -Wfloat-conversion Restructure the implict floating point to integer conversions so that interesting sub-groups are under different flags. Breakdown of warnings: No warning: Exact conversions from floating point to integer: int x = 10.0; int x = 1e10; -Wliteral-conversion - Floating point literal to integer with rounding: int x = 5.5; int x = -3.4; -Wfloat-conversion - All conversions not covered by the above two: int x = GetFloat(); int x = 5.5 + 3.5; -Wfloat-zero-conversion - The expression converted has a non-zero floating point value that gets converted to a zero integer value, excluded the cases falling under -Wliteral-conversion. Subset of -Wfloat-conversion. int x = 1.0 / 2.0; -Wfloat-overflow-conversion - The floating point value is outside the range of the integer type, exluding cases from -Wliteral conversion. Subset of -Wfloat-conversion. char x = 500; char x = -1000; -Wfloat-bool-conversion - Any conversion of a floating point type to bool. Subset of -Wfloat-conversion. if (GetFloat()) {} bool x = 5.0; -Wfloat-bool-constant-conversion - Conversion of a compile time evaluatable floating point value to bool. Subset of -Wfloat-bool-conversion. bool x = 1.0; bool x = 4.0 / 20.0; Also add EvaluateAsFloat to Sema, which is similar to EvaluateAsInt, but for floating point values. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267054 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 83b6e2693f..bdfefc8a4f 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -594,6 +594,13 @@ public: bool EvaluateAsInt(llvm::APSInt &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /// EvaluateAsFloat - Return true if this is a constant which we can fold and + /// convert to a floating point value, using any crazy technique that we + /// want to. + bool + EvaluateAsFloat(llvm::APFloat &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be /// constant folded without side-effects, but discard the result. bool isEvaluatable(const ASTContext &Ctx, diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index d6d21d71dd..487308293a 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -46,7 +46,17 @@ def BoolConversion : DiagGroup<"bool-conversion", [PointerBoolConversion, UndefinedBoolConversion]>; def IntConversion : DiagGroup<"int-conversion">; def EnumConversion : DiagGroup<"enum-conversion">; -def FloatConversion : DiagGroup<"float-conversion">; + +def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">; +def FloatZeroConversion : DiagGroup<"float-zero-conversion">; +def FloatBoolConstantConversion : DiagGroup<"float-bool-constant-conversion">; +def FloatBoolConversion : + DiagGroup<"float-bool-conversion", [FloatBoolConstantConversion]>; +def FloatConversion : + DiagGroup<"float-conversion", [FloatBoolConversion, + FloatOverflowConversion, + FloatZeroConversion]>; + def DoublePromotion : DiagGroup<"double-promotion">; def EnumTooLarge : DiagGroup<"enum-too-large">; def UnsupportedNan : DiagGroup<"unsupported-nan">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1cb21f2423..355892062b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2742,9 +2742,6 @@ def warn_impcast_float_precision : Warning< def warn_impcast_double_promotion : Warning< "implicit conversion increases floating-point precision: %0 to %1">, InGroup, DefaultIgnore; -def warn_impcast_float_integer : Warning< - "implicit conversion turns floating-point number into integer: %0 to %1">, - InGroup, DefaultIgnore; def warn_impcast_integer_sign : Warning< "implicit conversion changes signedness: %0 to %1">, InGroup, DefaultIgnore; @@ -2763,9 +2760,29 @@ def warn_impcast_integer_precision_constant : Warning< def warn_impcast_bitfield_precision_constant : Warning< "implicit truncation from %2 to bitfield changes value from %0 to %1">, InGroup; + def warn_impcast_literal_float_to_integer : Warning< "implicit conversion from %0 to %1 changes value from %2 to %3">, InGroup; +def warn_impcast_float_integer : Warning< + "implicit conversion turns floating-point number into integer: %0 to %1">, + InGroup, DefaultIgnore; + +def warn_impcast_float_bool : Warning< + "implicit conversion turns floating-point number into boolean: %0 to %1">, + InGroup, DefaultIgnore; +def warn_impcast_float_to_bool : Warning< + "implicit conversion from %0 to %1 changes value from %2 to %3">, + InGroup; + +def warn_impcast_float_to_integer : Warning< + "implicit conversion of out of range value from %0 to %1 changes value " + "from %2 to %3">, + InGroup, DefaultIgnore; +def warn_impcast_float_to_integer_zero : Warning< + "implicit conversion from %0 to %1 changes non-zero value from %2 to %3">, + InGroup, DefaultIgnore; + def warn_impcast_string_literal_to_bool : Warning< "implicit conversion turns string literal into bool: %0 to %1">, InGroup, DefaultIgnore; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index b38aeff8a6..c19d66917a 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -9027,6 +9027,20 @@ bool Expr::EvaluateAsInt(APSInt &Result, const ASTContext &Ctx, return true; } +bool Expr::EvaluateAsFloat(APFloat &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects) const { + if (!getType()->isRealFloatingType()) + return false; + + EvalResult ExprResult; + if (!EvaluateAsRValue(ExprResult, Ctx) || !ExprResult.Val.isFloat() || + hasUnacceptableSideEffect(ExprResult, AllowSideEffects)) + return false; + + Result = ExprResult.Val.getFloat(); + return true; +} + bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const { EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index b1e6b99ce4..0592a83011 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -7382,19 +7382,78 @@ void DiagnoseImpCast(Sema &S, Expr *E, QualType T, SourceLocation CContext, DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, pruneControlFlow); } -/// Diagnose an implicit cast from a literal expression. Does not warn when the -/// cast wouldn't lose information. -void DiagnoseFloatingLiteralImpCast(Sema &S, FloatingLiteral *FL, QualType T, - SourceLocation CContext) { - // Try to convert the literal exactly to an integer. If we can, don't warn. + +/// Diagnose an implicit cast from a floating point value to an integer value. +void DiagnoseFloatingImpCast(Sema &S, Expr *E, QualType T, + + SourceLocation CContext) { + const bool IsBool = T->isSpecificBuiltinType(BuiltinType::Bool); + const bool PruneWarnings = !S.ActiveTemplateInstantiations.empty(); + + Expr *InnerE = E->IgnoreParenImpCasts(); + // We also want to warn on, e.g., "int i = -1.234" + if (UnaryOperator *UOp = dyn_cast(InnerE)) + if (UOp->getOpcode() == UO_Minus || UOp->getOpcode() == UO_Plus) + InnerE = UOp->getSubExpr()->IgnoreParenImpCasts(); + + const bool IsLiteral = + isa(E) || isa(InnerE); + + llvm::APFloat Value(0.0); + bool IsConstant = + E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects); + if (!IsConstant) { + if (IsBool) { + return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_bool, + PruneWarnings); + } else { + return DiagnoseImpCast(S, E, T, CContext, + diag::warn_impcast_float_integer, PruneWarnings); + } + } + bool isExact = false; - const llvm::APFloat &Value = FL->getValue(); + llvm::APSInt IntegerValue(S.Context.getIntWidth(T), T->hasUnsignedIntegerRepresentation()); - if (Value.convertToInteger(IntegerValue, - llvm::APFloat::rmTowardZero, &isExact) - == llvm::APFloat::opOK && isExact) - return; + if (Value.convertToInteger(IntegerValue, llvm::APFloat::rmTowardZero, + &isExact) == llvm::APFloat::opOK && + isExact && !IsBool) { + if (IsLiteral) return; + return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer, + PruneWarnings); + } + + unsigned DiagID = 0; + if (IsBool) { + // Warn on all floating point to bool conversions + DiagID = diag::warn_impcast_float_to_bool; + } else if (IsLiteral) { + // Warn on floating point literal to integer. + DiagID = diag::warn_impcast_literal_float_to_integer; + } else if (IntegerValue == 0) { + if (Value.isZero()) { // Skip -0.0 to 0 conversion. + return DiagnoseImpCast(S, E, T, CContext, + diag::warn_impcast_float_integer, PruneWarnings); + } + // Warn on non-zero to zero conversion. + DiagID = diag::warn_impcast_float_to_integer_zero; + } else { + if (IntegerValue.isUnsigned()) { + if (!IntegerValue.isMaxValue()) { + return DiagnoseImpCast(S, E, T, CContext, + diag::warn_impcast_float_integer, PruneWarnings); + } + } else { // IntegerValue.isSigned() + if (!IntegerValue.isMaxSignedValue() && + !IntegerValue.isMinSignedValue()) { + return DiagnoseImpCast(S, E, T, CContext, + diag::warn_impcast_float_integer, PruneWarnings); + } + } + // Warn on evaluatable floating point expression to integer conversion. + DiagID = diag::warn_impcast_float_to_integer; + } // FIXME: Force the precision of the source value down so we don't print // digits which are usually useless (we don't really care here if we @@ -7407,14 +7466,22 @@ void DiagnoseFloatingLiteralImpCast(Sema &S, FloatingLiteral *FL, QualType T, Value.toString(PrettySourceValue, precision); SmallString<16> PrettyTargetValue; - if (T->isSpecificBuiltinType(BuiltinType::Bool)) + if (IsBool) PrettyTargetValue = Value.isZero() ? "false" : "true"; else IntegerValue.toString(PrettyTargetValue); - S.Diag(FL->getExprLoc(), diag::warn_impcast_literal_float_to_integer) - << FL->getType() << T.getUnqualifiedType() << PrettySourceValue - << PrettyTargetValue << FL->getSourceRange() << SourceRange(CContext); + if (PruneWarnings) { + S.DiagRuntimeBehavior(E->getExprLoc(), E, + S.PDiag(DiagID) + << E->getType() << T.getUnqualifiedType() + << PrettySourceValue << PrettyTargetValue + << E->getSourceRange() << SourceRange(CContext)); + } else { + S.Diag(E->getExprLoc(), DiagID) + << E->getType() << T.getUnqualifiedType() << PrettySourceValue + << PrettyTargetValue << E->getSourceRange() << SourceRange(CContext); + } } std::string PrettyPrintInRange(const llvm::APSInt &Value, IntRange Range) { @@ -7748,22 +7815,12 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, return; } - // If the target is integral, always warn. + // If the target is integral, always warn. if (TargetBT && TargetBT->isInteger()) { if (S.SourceMgr.isInSystemMacro(CC)) return; - - Expr *InnerE = E->IgnoreParenImpCasts(); - // We also want to warn on, e.g., "int i = -1.234" - if (UnaryOperator *UOp = dyn_cast(InnerE)) - if (UOp->getOpcode() == UO_Minus || UOp->getOpcode() == UO_Plus) - InnerE = UOp->getSubExpr()->IgnoreParenImpCasts(); - - if (FloatingLiteral *FL = dyn_cast(InnerE)) { - DiagnoseFloatingLiteralImpCast(S, FL, T, CC); - } else { - DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_float_integer); - } + + DiagnoseFloatingImpCast(S, E, T, CC); } // Detect the case where a call result is converted from floating-point to diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp index 48c5b23207..022b3111b6 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp @@ -58,8 +58,8 @@ void float_to_int() { Agg ce1 = { Convert(1.0) }; // expected-error {{type 'float' cannot be narrowed to 'char'}} expected-note {{silence}} Agg ce2 = { ConvertVar() }; // expected-error {{type 'double' cannot be narrowed to 'char'}} expected-note {{silence}} - bool b{1.0}; // expected-error {{type 'double' cannot be narrowed to 'bool'}} expected-note {{silence}} - Agg ab = {0.0}; // expected-error {{type 'double' cannot be narrowed to 'bool'}} expected-note {{silence}} + bool b{1.0}; // expected-error {{type 'double' cannot be narrowed to 'bool'}} expected-note {{silence}} expected-warning {{changes value}} + Agg ab = {0.0}; // expected-error {{type 'double' cannot be narrowed to 'bool'}} expected-note {{silence}} expected-warning {{changes value}} } // * from long double to double or float, or from double to float, except where diff --git a/test/SemaCXX/warn-float-conversion.cpp b/test/SemaCXX/warn-float-conversion.cpp index 22c33040b2..5a5532572a 100644 --- a/test/SemaCXX/warn-float-conversion.cpp +++ b/test/SemaCXX/warn-float-conversion.cpp @@ -1,5 +1,12 @@ -// RUN: %clang_cc1 -verify -fsyntax-only %s -Wfloat-conversion +// RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-pc-linux-gnu %s -Wno-literal-conversion -Wfloat-conversion -DFLOAT_CONVERSION -DZERO -DBOOL -DCONSTANT_BOOL -DOVERFLOW +// RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-pc-linux-gnu %s -Wno-conversion -Wfloat-overflow-conversion -DOVERFLOW +// RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-pc-linux-gnu %s -Wno-conversion -Wfloat-zero-conversion -DZERO +// RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-pc-linux-gnu %s -Wno-conversion -Wfloat-bool-constant-conversion -DCONSTANT_BOOL +// RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-pc-linux-gnu %s -Wno-conversion -Wfloat-bool-conversion -DCONSTANT_BOOL -DBOOL +float ReturnFloat(); + +#ifdef FLOAT_CONVERSION bool ReturnBool(float f) { return f; //expected-warning{{conversion}} } @@ -36,3 +43,80 @@ void Convert(float f, double d, long double ld) { l = ld; //expected-warning{{conversion}} } +void Test() { + int a1 = 10.0/2.0; //expected-warning{{conversion}} + int a2 = 1.0/2.0; //expected-warning{{conversion}} + bool a3 = ReturnFloat(); //expected-warning{{conversion}} + int a4 = 1e30 + 1; //expected-warning{{conversion}} +} + +void TestConstantFloat() { + // Don't warn on exact floating literals. + int a1 = 5.0; + int a2 = 1e3; + + int a3 = 5.5; // caught by -Wliteral-conversion + int a4 = 500.44; // caught by -Wliteral-convserion + + int b1 = 5.0 / 1.0; //expected-warning{{conversion}} + int b2 = 5.0 / 2.0; //expected-warning{{conversion}} + + const float five = 5.0; + + int b3 = five / 1.0; //expected-warning{{conversion}} + int b4 = five / 2.0; //expected-warning{{conversion}} +} +#endif // FLOAT_CONVERSION + +#ifdef CONSTANT_BOOL +const float pi = 3.1415; + +void TestConstantBool() { + bool b1 = 0.99f; // expected-warning {{implicit conversion from 'float' to 'bool' changes value from 0.99 to true}} + bool b2 = 0.99; // expected-warning {{implicit conversion from 'double' to 'bool' changes value from 0.99 to true}} + bool b3 = 0.0f; // expected-warning {{implicit conversion from 'float' to 'bool' changes value from 0 to false}} + bool b4 = 0.0; // expected-warning {{implicit conversion from 'double' to 'bool' changes value from 0 to false}} + bool b5 = 1.0f; // expected-warning {{implicit conversion from 'float' to 'bool' changes value from 1 to true}} + bool b6 = 1.0; // expected-warning {{implicit conversion from 'double' to 'bool' changes value from 1 to true}} + bool b7 = pi; // expected-warning {{implicit conversion from 'const float' to 'bool' changes value from 3.1415 to true}} + bool b8 = pi - pi; // expected-warning {{implicit conversion from 'float' to 'bool' changes value from 0 to false}} +} +#endif // CONSTANT_BOOL + +#ifdef BOOL +const float E = 2.718; + +float GetFloat(); +double GetDouble(); + +void TestBool() { + bool b1 = GetFloat(); // expected-warning {{implicit conversion turns floating-point number into boolean: 'float' to 'bool'}} + bool b2 = GetDouble(); // expected-warning {{implicit conversion turns floating-point number into boolean: 'double' to 'bool'}} + bool b3 = 0.0 * GetDouble(); // expected-warning {{implicit conversion turns floating-point number into boolean: 'double' to 'bool'}} + bool b4 = GetFloat() + GetDouble(); // expected-warning {{implicit conversion turns floating-point number into boolean: 'double' to 'bool'}} + bool b5 = E + GetFloat(); // expected-warning {{implicit conversion turns floating-point number into boolean: 'float' to 'bool'}} +} + +#endif // BOOL + +#ifdef ZERO +void TestZero() { + const float half = .5; + int a1 = half; // expected-warning{{implicit conversion from 'const float' to 'int' changes non-zero value from 0.5 to 0}} + int a2 = 1.0 / 2.0; // expected-warning{{implicit conversion from 'double' to 'int' changes non-zero value from 0.5 to 0}} + int a3 = 5; +} +#endif // ZERO + +#ifdef OVERFLOW +void TestOverflow() { + char a = 500.0; // caught by -Wliteral-conversion + char b = -500.0; // caught by -Wliteral-conversion + + const float LargeNumber = 1024; + char c = LargeNumber; // expected-warning{{implicit conversion of out of range value from 'const float' to 'char' changes value from 1024 to 127}} + char d = 400.0 + 400.0; // expected-warning{{implicit conversion of out of range value from 'double' to 'char' changes value from 800 to 127}} + + char e = 1.0 / 0.0; // expected-warning{{implicit conversion of out of range value from 'double' to 'char' changes value from +Inf to 127}} +} +#endif // OVERFLOW diff --git a/test/SemaCXX/warn-literal-conversion.cpp b/test/SemaCXX/warn-literal-conversion.cpp index 5d4b6f7f5b..8a74166a01 100644 --- a/test/SemaCXX/warn-literal-conversion.cpp +++ b/test/SemaCXX/warn-literal-conversion.cpp @@ -25,7 +25,7 @@ void test0() { // Test passing a literal floating-point value to a function that takes an integer. foo(1.2F); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2 to 1}} - int y10 = -1.2F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from 1.2 to 1}} + int y10 = -1.2F; // expected-warning {{implicit conversion from 'float' to 'int' changes value from -1.2 to -1}} // -Wliteral-conversion does NOT catch const values. // (-Wconversion DOES catch them.) @@ -38,14 +38,3 @@ void test0() { int y = (24*60*60) * 0.25; int pennies = 123.45 * 100; } - -// Similarly, test floating point conversion to bool. Only float values of zero -// are converted to false; everything else is converted to true. -void test1() { - bool b1 = 0.99f; // expected-warning {{implicit conversion from 'float' to 'bool' changes value from 0.99 to true}} - bool b2 = 0.99; // expected-warning {{implicit conversion from 'double' to 'bool' changes value from 0.99 to true}} - // These do not warn because they can be directly converted to integral - // values. - bool b3 = 0.0f; - bool b4 = 0.0; -}