diff options
author | John McCall <rjmccall@apple.com> | 2010-01-04 23:31:57 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2010-01-04 23:31:57 +0000 |
commit | 51313c39c84407dd6a323be99a8c322bf8d052a9 (patch) | |
tree | e8f61318c082993401dc60c42057af5b5a375de1 /lib/Sema/SemaChecking.cpp | |
parent | ba26e58c64b4f6233dfc4bcd3ef6ce83aab47ffc (diff) |
Move the -Wconversion logic into SemaChecking.cpp. There's a fair amount of
overlap between this and -Wsign-compare, which is why I want them in the same
place.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@92543 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaChecking.cpp')
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 4812a64093..7d2e38cc15 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -1578,6 +1578,219 @@ static bool IsSignBitProvablyZero(ASTContext &Context, Expr *E) { return false; } +/// Retrieves the width and signedness of the given integer type, +/// or returns false if it is not an integer type. +/// +/// \param T must be canonical +static bool getIntProperties(ASTContext &C, const Type *T, + unsigned &BitWidth, bool &Signed) { + assert(T->isCanonicalUnqualified()); + + if (const VectorType *VT = dyn_cast<VectorType>(T)) + T = VT->getElementType().getTypePtr(); + if (const ComplexType *CT = dyn_cast<ComplexType>(T)) + T = CT->getElementType().getTypePtr(); + + if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) { + if (!BT->isInteger()) return false; + + BitWidth = C.getIntWidth(QualType(T, 0)); + Signed = BT->isSignedInteger(); + return true; + } + + return false; +} + +/// Checks whether the given value will have the same value if it it +/// is truncated to the given width, then extended back to the +/// original width. +static bool IsSameIntAfterCast(const llvm::APSInt &value, + unsigned TargetWidth) { + unsigned SourceWidth = value.getBitWidth(); + llvm::APSInt truncated = value; + truncated.trunc(TargetWidth); + truncated.extend(SourceWidth); + return (truncated == value); +} + +/// Checks whether the given value will have the same value if it +/// is truncated to the given width, then extended back to the original +/// width. +/// +/// The value might be a vector or a complex. +static bool IsSameIntAfterCast(const APValue &value, unsigned TargetWidth) { + if (value.isInt()) + return IsSameIntAfterCast(value.getInt(), TargetWidth); + + if (value.isVector()) { + for (unsigned i = 0, e = value.getVectorLength(); i != e; ++i) + if (!IsSameIntAfterCast(value.getVectorElt(i), TargetWidth)) + return false; + return true; + } + + if (value.isComplexInt()) { + return IsSameIntAfterCast(value.getComplexIntReal(), TargetWidth) && + IsSameIntAfterCast(value.getComplexIntImag(), TargetWidth); + } + + // This can happen with lossless casts to intptr_t of "based" lvalues. + // Assume it might use arbitrary bits. + assert(value.isLValue()); + return false; +} + + +/// Checks whether the given value, which currently has the given +/// source semantics, has the same value when coerced through the +/// target semantics. +static bool IsSameFloatAfterCast(const llvm::APFloat &value, + const llvm::fltSemantics &Src, + const llvm::fltSemantics &Tgt) { + llvm::APFloat truncated = value; + + bool ignored; + truncated.convert(Src, llvm::APFloat::rmNearestTiesToEven, &ignored); + truncated.convert(Tgt, llvm::APFloat::rmNearestTiesToEven, &ignored); + + return truncated.bitwiseIsEqual(value); +} + +/// Checks whether the given value, which currently has the given +/// source semantics, has the same value when coerced through the +/// target semantics. +/// +/// The value might be a vector of floats (or a complex number). +static bool IsSameFloatAfterCast(const APValue &value, + const llvm::fltSemantics &Src, + const llvm::fltSemantics &Tgt) { + if (value.isFloat()) + return IsSameFloatAfterCast(value.getFloat(), Src, Tgt); + + if (value.isVector()) { + for (unsigned i = 0, e = value.getVectorLength(); i != e; ++i) + if (!IsSameFloatAfterCast(value.getVectorElt(i), Src, Tgt)) + return false; + return true; + } + + assert(value.isComplexFloat()); + return (IsSameFloatAfterCast(value.getComplexFloatReal(), Src, Tgt) && + IsSameFloatAfterCast(value.getComplexFloatImag(), Src, Tgt)); +} + +/// Determines if it's reasonable for the given expression to be truncated +/// down to the given integer width. +/// * Boolean expressions are automatically white-listed. +/// * Arithmetic operations on implicitly-promoted operands of the +/// target width or less are okay --- not because the results are +/// actually guaranteed to fit within the width, but because the +/// user is effectively pretending that the operations are closed +/// within the implicitly-promoted type. +static bool IsExprValueWithinWidth(ASTContext &C, Expr *E, unsigned Width) { + E = E->IgnoreParens(); + +#ifndef NDEBUG + { + const Type *ETy = E->getType()->getCanonicalTypeInternal().getTypePtr(); + unsigned EWidth; + bool ESigned; + + if (!getIntProperties(C, ETy, EWidth, ESigned)) + assert(0 && "expression not of integer type"); + + // The caller should never let this happen. + assert(EWidth > Width && "called on expr whose type is too small"); + } +#endif + + // Strip implicit casts off. + while (isa<ImplicitCastExpr>(E)) { + E = cast<ImplicitCastExpr>(E)->getSubExpr(); + + const Type *ETy = E->getType()->getCanonicalTypeInternal().getTypePtr(); + + unsigned EWidth; + bool ESigned; + if (!getIntProperties(C, ETy, EWidth, ESigned)) + return false; + + if (EWidth <= Width) + return true; + } + + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) { + switch (BO->getOpcode()) { + + // Boolean-valued operations are white-listed. + case BinaryOperator::LAnd: + case BinaryOperator::LOr: + case BinaryOperator::LT: + case BinaryOperator::GT: + case BinaryOperator::LE: + case BinaryOperator::GE: + case BinaryOperator::EQ: + case BinaryOperator::NE: + return true; + + // Operations with opaque sources are black-listed. + case BinaryOperator::PtrMemD: + case BinaryOperator::PtrMemI: + return false; + + // Left shift gets black-listed based on a judgement call. + case BinaryOperator::Shl: + return false; + + // Various special cases. + case BinaryOperator::Shr: + return IsExprValueWithinWidth(C, BO->getLHS(), Width); + case BinaryOperator::Comma: + return IsExprValueWithinWidth(C, BO->getRHS(), Width); + case BinaryOperator::Sub: + if (BO->getLHS()->getType()->isPointerType()) + return false; + // fallthrough + + // Any other operator is okay if the operands are + // promoted from expressions of appropriate size. + default: + return IsExprValueWithinWidth(C, BO->getLHS(), Width) && + IsExprValueWithinWidth(C, BO->getRHS(), Width); + } + } + + if (UnaryOperator *UO = dyn_cast<UnaryOperator>(E)) { + switch (UO->getOpcode()) { + // Boolean-valued operations are white-listed. + case UnaryOperator::LNot: + return true; + + // Operations with opaque sources are black-listed. + case UnaryOperator::Deref: + case UnaryOperator::AddrOf: // should be impossible + return false; + + case UnaryOperator::OffsetOf: + return false; + + default: + return IsExprValueWithinWidth(C, UO->getSubExpr(), Width); + } + } + + // Don't diagnose if the expression is an integer constant + // whose value in the target type is the same as it was + // in the original type. + Expr::EvalResult result; + if (E->Evaluate(result, C)) + if (IsSameIntAfterCast(result.Val, Width)) + return true; + + return false; +} + /// \brief Implements -Wsign-compare. /// /// \param lex the left-hand expression @@ -1640,3 +1853,99 @@ void Sema::CheckSignCompare(Expr *lex, Expr *rex, SourceLocation OpLoc, << lex->getSourceRange() << rex->getSourceRange(); } +/// Diagnose an implicit cast; purely a helper for CheckImplicitConversion. +static void DiagnoseImpCast(Sema &S, Expr *E, QualType T, unsigned diag) { + S.Diag(E->getExprLoc(), diag) << E->getType() << T << E->getSourceRange(); +} + +/// Implements -Wconversion. +void Sema::CheckImplicitConversion(Expr *E, QualType T) { + // Don't diagnose in unevaluated contexts. + if (ExprEvalContexts.back().Context == Sema::Unevaluated) + return; + + // Don't diagnose for value-dependent expressions. + if (E->isValueDependent()) + return; + + const Type *Source = Context.getCanonicalType(E->getType()).getTypePtr(); + const Type *Target = Context.getCanonicalType(T).getTypePtr(); + + // Never diagnose implicit casts to bool. + if (Target->isSpecificBuiltinType(BuiltinType::Bool)) + return; + + // Strip vector types. + if (isa<VectorType>(Source)) { + if (!isa<VectorType>(Target)) + return DiagnoseImpCast(*this, E, T, diag::warn_impcast_vector_scalar); + + Source = cast<VectorType>(Source)->getElementType().getTypePtr(); + Target = cast<VectorType>(Target)->getElementType().getTypePtr(); + } + + // Strip complex types. + if (isa<ComplexType>(Source)) { + if (!isa<ComplexType>(Target)) + return DiagnoseImpCast(*this, E, T, diag::warn_impcast_complex_scalar); + + Source = cast<ComplexType>(Source)->getElementType().getTypePtr(); + Target = cast<ComplexType>(Target)->getElementType().getTypePtr(); + } + + const BuiltinType *SourceBT = dyn_cast<BuiltinType>(Source); + const BuiltinType *TargetBT = dyn_cast<BuiltinType>(Target); + + // If the source is floating point... + if (SourceBT && SourceBT->isFloatingPoint()) { + // ...and the target is floating point... + if (TargetBT && TargetBT->isFloatingPoint()) { + // ...then warn if we're dropping FP rank. + + // Builtin FP kinds are ordered by increasing FP rank. + if (SourceBT->getKind() > TargetBT->getKind()) { + // Don't warn about float constants that are precisely + // representable in the target type. + Expr::EvalResult result; + if (E->Evaluate(result, Context)) { + // Value might be a float, a float vector, or a float complex. + if (IsSameFloatAfterCast(result.Val, + Context.getFloatTypeSemantics(QualType(TargetBT, 0)), + Context.getFloatTypeSemantics(QualType(SourceBT, 0)))) + return; + } + + DiagnoseImpCast(*this, E, T, diag::warn_impcast_float_precision); + } + return; + } + + // If the target is integral, always warn. + if ((TargetBT && TargetBT->isInteger())) + // TODO: don't warn for integer values? + return DiagnoseImpCast(*this, E, T, diag::warn_impcast_float_integer); + + return; + } + + unsigned SourceWidth, TargetWidth; + bool SourceSigned, TargetSigned; + + if (!getIntProperties(Context, Source, SourceWidth, SourceSigned) || + !getIntProperties(Context, Target, TargetWidth, TargetSigned)) + return; + + if (SourceWidth > TargetWidth) { + if (IsExprValueWithinWidth(Context, E, TargetWidth)) + return; + + // People want to build with -Wshorten-64-to-32 and not -Wconversion + // and by god we'll let them. + if (SourceWidth == 64 && TargetWidth == 32) + return DiagnoseImpCast(*this, E, T, diag::warn_impcast_integer_64_32); + return DiagnoseImpCast(*this, E, T, diag::warn_impcast_integer_precision); + } + + return; +} + |