From: Richard Smith Date: Wed, 1 Feb 2012 05:53:12 +0000 (+0000) Subject: constexpr: overflow checking for integral and floating-point arithmetic. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7b48a2986345480241f3b8209f71bb21b0530b4f;p=clang constexpr: overflow checking for integral and floating-point arithmetic. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149473 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 7bca36de0b..ba8820a3b0 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -36,6 +36,8 @@ def note_constexpr_non_global : Note< "cannot be used to initialize a member in a constant expression}3">; def note_constexpr_array_index : Note<"cannot refer to element %0 of " "%select{array of %2 elements|non-array object}1 in a constant expression">; +def note_constexpr_float_arithmetic : Note< + "floating point arithmetic produces %select{an infinity|a NaN}0">; def note_constexpr_pointer_arithmetic : Note< "cannot refer to element %0 of non-array object in a constant " "expression">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 0f8de63bba..731bddd6b8 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -45,6 +45,7 @@ #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallString.h" #include +#include using namespace clang; using llvm::APSInt; @@ -4138,6 +4139,23 @@ static bool HasSameBase(const LValue &A, const LValue &B) { A.getLValueFrame() == B.getLValueFrame(); } +/// Perform the given integer operation, which is known to need at most BitWidth +/// bits, and check for overflow in the original type (if that type was not an +/// unsigned type). +template +static APSInt CheckedIntArithmetic(EvalInfo &Info, const Expr *E, + const APSInt &LHS, const APSInt &RHS, + unsigned BitWidth, Operation Op) { + if (LHS.isUnsigned()) + return Op(LHS, RHS); + + APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false); + APSInt Result = Value.trunc(LHS.getBitWidth()); + if (Result.extend(BitWidth) != Value) + HandleOverflow(Info, E, Value, E->getType()); + return Result; +} + bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (E->isAssignmentOp()) return Error(E); @@ -4476,9 +4494,18 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { switch (E->getOpcode()) { default: return Error(E); - case BO_Mul: return Success(LHS * RHS, E); - case BO_Add: return Success(LHS + RHS, E); - case BO_Sub: return Success(LHS - RHS, E); + case BO_Mul: + return Success(CheckedIntArithmetic(Info, E, LHS, RHS, + LHS.getBitWidth() * 2, + std::multiplies()), E); + case BO_Add: + return Success(CheckedIntArithmetic(Info, E, LHS, RHS, + LHS.getBitWidth() + 1, + std::plus()), E); + case BO_Sub: + return Success(CheckedIntArithmetic(Info, E, LHS, RHS, + LHS.getBitWidth() + 1, + std::minus()), E); case BO_And: return Success(LHS & RHS, E); case BO_Xor: return Success(LHS ^ RHS, E); case BO_Or: return Success(LHS | RHS, E); @@ -5078,17 +5105,21 @@ bool FloatExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { default: return Error(E); case BO_Mul: Result.multiply(RHS, APFloat::rmNearestTiesToEven); - return true; + break; case BO_Add: Result.add(RHS, APFloat::rmNearestTiesToEven); - return true; + break; case BO_Sub: Result.subtract(RHS, APFloat::rmNearestTiesToEven); - return true; + break; case BO_Div: Result.divide(RHS, APFloat::rmNearestTiesToEven); - return true; + break; } + + if (Result.isInfinity() || Result.isNaN()) + CCEDiag(E, diag::note_constexpr_float_arithmetic) << Result.isNaN(); + return true; } bool FloatExprEvaluator::VisitFloatingLiteral(const FloatingLiteral *E) { diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index 1355bbb7c2..857f02960f 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -196,6 +196,47 @@ namespace UndefinedBehavior { C c2; constexpr int k4 = c2.f(); // ok! } + + namespace Overflow { + // Signed int overflow. + constexpr int n1 = 2 * 3 * 3 * 7 * 11 * 31 * 151 * 331; // ok + constexpr int n2 = 65536 * 32768; // expected-error {{constant expression}} expected-note {{value 2147483648 is outside the range of }} + constexpr int n3 = n1 + 1; // ok + constexpr int n4 = n3 + 1; // expected-error {{constant expression}} expected-note {{value 2147483648 is outside the range of }} + constexpr int n5 = -65536 * 32768; // ok + constexpr int n6 = 3 * -715827883; // expected-error {{constant expression}} expected-note {{value -2147483649 is outside the range of }} + constexpr int n7 = -n3 + -1; // ok + constexpr int n8 = -1 + n7; // expected-error {{constant expression}} expected-note {{value -2147483649 is outside the range of }} + constexpr int n9 = n3 - 0; // ok + constexpr int n10 = n3 - -1; // expected-error {{constant expression}} expected-note {{value 2147483648 is outside the range of }} + constexpr int n11 = -1 - n3; // ok + constexpr int n12 = -2 - n3; // expected-error {{constant expression}} expected-note {{value -2147483649 is outside the range of }} + constexpr int n13 = n5 + n5; // expected-error {{constant expression}} expected-note {{value -4294967296 is outside the range of }} + constexpr int n14 = n3 - n5; // expected-error {{constant expression}} expected-note {{value 4294967295 is outside the range of }} + constexpr int n15 = n5 * n5; // expected-error {{constant expression}} expected-note {{value 4611686018427387904 is outside the range of }} + constexpr signed char c1 = 100 * 2; // ok + constexpr signed char c2 = '\x64' * '\2'; // also ok + constexpr long long ll1 = 0x7fffffffffffffff; // ok + constexpr long long ll2 = ll1 + 1; // expected-error {{constant}} expected-note {{ 9223372036854775808 }} + constexpr long long ll3 = -ll1 - 1; // ok + constexpr long long ll4 = ll3 - 1; // expected-error {{constant}} expected-note {{ -9223372036854775809 }} + constexpr long long ll5 = ll3 * ll3; // expected-error {{constant}} expected-note {{ 85070591730234615865843651857942052864 }} + + // Unsigned int overflow. + static_assert(65536u * 65536u == 0u, ""); // ok + static_assert(4294967295u + 1u == 0u, ""); // ok + static_assert(0u - 1u == 4294967295u, ""); // ok + static_assert(~0u * ~0u == 1u, ""); // ok + + // Floating-point overflow and NaN. + constexpr float f1 = 1e38f * 3.4028f; // ok + constexpr float f2 = 1e38f * 3.4029f; // expected-error {{constant expression}} expected-note {{floating point arithmetic produces an infinity}} + constexpr float f3 = 1e38f / -.2939f; // ok + constexpr float f4 = 1e38f / -.2938f; // expected-error {{constant expression}} expected-note {{floating point arithmetic produces an infinity}} + constexpr float f5 = 2e38f + 2e38f; // expected-error {{constant expression}} expected-note {{floating point arithmetic produces an infinity}} + constexpr float f6 = -2e38f - 2e38f; // expected-error {{constant expression}} expected-note {{floating point arithmetic produces an infinity}} + constexpr float f7 = 0.f / 0.f; // expected-error {{constant expression}} expected-note {{floating point arithmetic produces a NaN}} + } } // - a lambda-expression (5.1.2);