From d20afcbe94f0daed15d040d98d2aa8ef8ed368e4 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 7 May 2013 04:50:00 +0000 Subject: [PATCH] C++1y constant expression evaluation: support for compound assignments on integers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181287 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 379 ++++++++++++++------- test/SemaCXX/constant-expression-cxx1y.cpp | 58 +++- 2 files changed, 303 insertions(+), 134 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index a3315e3b87..5695af21ac 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1299,6 +1299,128 @@ static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E, return false; } +/// 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) { + if (Info.getIntOverflowCheckMode()) + Info.Ctx.getDiagnostics().Report(E->getExprLoc(), + diag::warn_integer_constant_overflow) + << Result.toString(10) << E->getType(); + else + HandleOverflow(Info, E, Value, E->getType()); + } + return Result; +} + +/// Perform the given binary integer operation. +static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS, + BinaryOperatorKind Opcode, APSInt RHS, + APSInt &Result) { + switch (Opcode) { + default: + Info.Diag(E); + return false; + case BO_Mul: + Result = CheckedIntArithmetic(Info, E, LHS, RHS, LHS.getBitWidth() * 2, + std::multiplies()); + return true; + case BO_Add: + Result = CheckedIntArithmetic(Info, E, LHS, RHS, LHS.getBitWidth() + 1, + std::plus()); + return true; + case BO_Sub: + Result = CheckedIntArithmetic(Info, E, LHS, RHS, LHS.getBitWidth() + 1, + std::minus()); + return true; + case BO_And: Result = LHS & RHS; return true; + case BO_Xor: Result = LHS ^ RHS; return true; + case BO_Or: Result = LHS | RHS; return true; + case BO_Div: + case BO_Rem: + if (RHS == 0) { + Info.Diag(E, diag::note_expr_divide_by_zero); + return false; + } + // Check for overflow case: INT_MIN / -1 or INT_MIN % -1. + if (RHS.isNegative() && RHS.isAllOnesValue() && + LHS.isSigned() && LHS.isMinSignedValue()) + HandleOverflow(Info, E, -LHS.extend(LHS.getBitWidth() + 1), E->getType()); + Result = (Opcode == BO_Rem ? LHS % RHS : LHS / RHS); + return true; + case BO_Shl: { + if (Info.getLangOpts().OpenCL) + // OpenCL 6.3j: shift values are effectively % word size of LHS. + RHS &= APSInt(llvm::APInt(RHS.getBitWidth(), + static_cast(LHS.getBitWidth() - 1)), + RHS.isUnsigned()); + else if (RHS.isSigned() && RHS.isNegative()) { + // During constant-folding, a negative shift is an opposite shift. Such + // a shift is not a constant expression. + Info.CCEDiag(E, diag::note_constexpr_negative_shift) << RHS; + RHS = -RHS; + goto shift_right; + } + shift_left: + // C++11 [expr.shift]p1: Shift width must be less than the bit width of + // the shifted type. + unsigned SA = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); + if (SA != RHS) { + Info.CCEDiag(E, diag::note_constexpr_large_shift) + << RHS << E->getType() << LHS.getBitWidth(); + } else if (LHS.isSigned()) { + // C++11 [expr.shift]p2: A signed left shift must have a non-negative + // operand, and must not overflow the corresponding unsigned type. + if (LHS.isNegative()) + Info.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS; + else if (LHS.countLeadingZeros() < SA) + Info.CCEDiag(E, diag::note_constexpr_lshift_discards); + } + Result = LHS << SA; + return true; + } + case BO_Shr: { + if (Info.getLangOpts().OpenCL) + // OpenCL 6.3j: shift values are effectively % word size of LHS. + RHS &= APSInt(llvm::APInt(RHS.getBitWidth(), + static_cast(LHS.getBitWidth() - 1)), + RHS.isUnsigned()); + else if (RHS.isSigned() && RHS.isNegative()) { + // During constant-folding, a negative shift is an opposite shift. Such a + // shift is not a constant expression. + Info.CCEDiag(E, diag::note_constexpr_negative_shift) << RHS; + RHS = -RHS; + goto shift_left; + } + shift_right: + // C++11 [expr.shift]p1: Shift width must be less than the bit width of the + // shifted type. + unsigned SA = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); + if (SA != RHS) + Info.CCEDiag(E, diag::note_constexpr_large_shift) + << RHS << E->getType() << LHS.getBitWidth(); + Result = LHS >> SA; + return true; + } + + case BO_LT: Result = LHS < RHS; return true; + case BO_GT: Result = LHS > RHS; return true; + case BO_LE: Result = LHS <= RHS; return true; + case BO_GE: Result = LHS >= RHS; return true; + case BO_EQ: Result = LHS == RHS; return true; + case BO_NE: Result = LHS != RHS; return true; + } +} + /// Cast an lvalue referring to a base subobject to a derived class, by /// truncating the lvalue's path to the given length. static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, @@ -2158,6 +2280,116 @@ static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy); } +namespace { +struct CompoundAssignSubobjectHandler { + EvalInfo &Info; + const Expr *E; + QualType PromotedLHSType; + BinaryOperatorKind Opcode; + const APValue &RHS; + + static const AccessKinds AccessKind = AK_Assign; + + typedef bool result_type; + + bool checkConst(QualType QT) { + // Assigning to a const object has undefined behavior. + if (QT.isConstQualified()) { + Info.Diag(E, diag::note_constexpr_modify_const_type) << QT; + return false; + } + return true; + } + + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + switch (Subobj.getKind()) { + case APValue::Int: + return found(Subobj.getInt(), SubobjType); + case APValue::Float: + return found(Subobj.getFloat(), SubobjType); + case APValue::ComplexInt: + case APValue::ComplexFloat: + // FIXME: Implement complex compound assignment. + Info.Diag(E); + return false; + case APValue::LValue: + return foundPointer(Subobj, SubobjType); + default: + // FIXME: can this happen? + Info.Diag(E); + return false; + } + } + bool found(APSInt &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + + if (!SubobjType->isIntegerType() || !RHS.isInt()) { + // We don't support compound assignment on integer-cast-to-pointer + // values. + Info.Diag(E); + return false; + } + + APSInt LHS = HandleIntToIntCast(Info, E, PromotedLHSType, + SubobjType, Value); + if (!handleIntIntBinOp(Info, E, LHS, Opcode, RHS.getInt(), LHS)) + return false; + Value = HandleIntToIntCast(Info, E, SubobjType, PromotedLHSType, LHS); + return true; + } + bool found(APFloat &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + + // FIXME: Implement. + Info.Diag(E); + return false; + } + bool foundPointer(APValue &Subobj, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + + QualType PointeeType; + if (const PointerType *PT = SubobjType->getAs()) + PointeeType = PT->getPointeeType(); + else { + Info.Diag(E); + return false; + } + + // FIXME: Implement. + Info.Diag(E); + return false; + } + bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { + llvm_unreachable("shouldn't encounter string elements here"); + } +}; +} // end anonymous namespace + +const AccessKinds CompoundAssignSubobjectHandler::AccessKind; + +/// Perform a compound assignment of LVal = RVal. +static bool handleCompoundAssignment( + EvalInfo &Info, const Expr *E, + const LValue &LVal, QualType LValType, QualType PromotedLValType, + BinaryOperatorKind Opcode, const APValue &RVal) { + if (LVal.Designator.Invalid) + return false; + + if (!Info.getLangOpts().CPlusPlus1y) { + Info.Diag(E); + return false; + } + + CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType); + CompoundAssignSubobjectHandler Handler = { Info, E, PromotedLValType, Opcode, + RVal }; + return Obj && findSubobject(Info, E, Obj, LVal.Designator, Handler); +} + namespace { struct IncDecSubobjectHandler { EvalInfo &Info; @@ -3655,14 +3887,10 @@ bool LValueExprEvaluator::VisitCompoundAssignOperator( if (!Evaluate(RHS, this->Info, CAO->getRHS())) return false; - // FIXME: - //return handleCompoundAssignment( - // this->Info, CAO, - // Result, CAO->getLHS()->getType(), CAO->getComputationLHSType(), - // RHS, CAO->getRHS()->getType(), - // CAO->getOpForCompoundAssignment(CAO->getOpcode()), - // CAO->getComputationResultType()); - return Error(CAO); + return handleCompoundAssignment( + this->Info, CAO, + Result, CAO->getLHS()->getType(), CAO->getComputationLHSType(), + CAO->getOpForCompoundAssignment(CAO->getOpcode()), RHS); } bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { @@ -5170,29 +5398,6 @@ static bool HasSameBase(const LValue &A, const LValue &B) { A.getLValueCallIndex() == B.getLValueCallIndex(); } -/// 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) { - if (Info.getIntOverflowCheckMode()) - Info.Ctx.getDiagnostics().Report(E->getExprLoc(), - diag::warn_integer_constant_overflow) - << Result.toString(10) << E->getType(); - else - HandleOverflow(Info, E, Value, E->getType()); - } - return Result; -} - namespace { /// \brief Data recursive integer evaluator of certain binary operators. @@ -5437,108 +5642,20 @@ bool DataRecursiveIntBinOpEvaluator:: Result = APValue(LHSAddrExpr, RHSAddrExpr); return true; } - - // All the following cases expect both operands to be an integer + + // All the remaining cases expect both operands to be an integer if (!LHSVal.isInt() || !RHSVal.isInt()) return Error(E); - - const APSInt &LHS = LHSVal.getInt(); - APSInt RHS = RHSVal.getInt(); - - switch (E->getOpcode()) { - default: - return Error(E); - case BO_Mul: - return Success(CheckedIntArithmetic(Info, E, LHS, RHS, - LHS.getBitWidth() * 2, - std::multiplies()), E, - Result); - case BO_Add: - return Success(CheckedIntArithmetic(Info, E, LHS, RHS, - LHS.getBitWidth() + 1, - std::plus()), E, Result); - case BO_Sub: - return Success(CheckedIntArithmetic(Info, E, LHS, RHS, - LHS.getBitWidth() + 1, - std::minus()), E, Result); - case BO_And: return Success(LHS & RHS, E, Result); - case BO_Xor: return Success(LHS ^ RHS, E, Result); - case BO_Or: return Success(LHS | RHS, E, Result); - case BO_Div: - case BO_Rem: - if (RHS == 0) - return Error(E, diag::note_expr_divide_by_zero); - // Check for overflow case: INT_MIN / -1 or INT_MIN % -1. The latter is - // not actually undefined behavior in C++11 due to a language defect. - if (RHS.isNegative() && RHS.isAllOnesValue() && - LHS.isSigned() && LHS.isMinSignedValue()) - HandleOverflow(Info, E, -LHS.extend(LHS.getBitWidth() + 1), E->getType()); - return Success(E->getOpcode() == BO_Rem ? LHS % RHS : LHS / RHS, E, - Result); - case BO_Shl: { - if (Info.getLangOpts().OpenCL) - // OpenCL 6.3j: shift values are effectively % word size of LHS. - RHS &= APSInt(llvm::APInt(RHS.getBitWidth(), - static_cast(LHS.getBitWidth() - 1)), - RHS.isUnsigned()); - else if (RHS.isSigned() && RHS.isNegative()) { - // During constant-folding, a negative shift is an opposite shift. Such - // a shift is not a constant expression. - CCEDiag(E, diag::note_constexpr_negative_shift) << RHS; - RHS = -RHS; - goto shift_right; - } - - shift_left: - // C++11 [expr.shift]p1: Shift width must be less than the bit width of - // the shifted type. - unsigned SA = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); - if (SA != RHS) { - CCEDiag(E, diag::note_constexpr_large_shift) - << RHS << E->getType() << LHS.getBitWidth(); - } else if (LHS.isSigned()) { - // C++11 [expr.shift]p2: A signed left shift must have a non-negative - // operand, and must not overflow the corresponding unsigned type. - if (LHS.isNegative()) - CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS; - else if (LHS.countLeadingZeros() < SA) - CCEDiag(E, diag::note_constexpr_lshift_discards); - } - - return Success(LHS << SA, E, Result); - } - case BO_Shr: { - if (Info.getLangOpts().OpenCL) - // OpenCL 6.3j: shift values are effectively % word size of LHS. - RHS &= APSInt(llvm::APInt(RHS.getBitWidth(), - static_cast(LHS.getBitWidth() - 1)), - RHS.isUnsigned()); - else if (RHS.isSigned() && RHS.isNegative()) { - // During constant-folding, a negative shift is an opposite shift. Such a - // shift is not a constant expression. - CCEDiag(E, diag::note_constexpr_negative_shift) << RHS; - RHS = -RHS; - goto shift_left; - } - - shift_right: - // C++11 [expr.shift]p1: Shift width must be less than the bit width of the - // shifted type. - unsigned SA = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); - if (SA != RHS) - CCEDiag(E, diag::note_constexpr_large_shift) - << RHS << E->getType() << LHS.getBitWidth(); - - return Success(LHS >> SA, E, Result); - } - - case BO_LT: return Success(LHS < RHS, E, Result); - case BO_GT: return Success(LHS > RHS, E, Result); - case BO_LE: return Success(LHS <= RHS, E, Result); - case BO_GE: return Success(LHS >= RHS, E, Result); - case BO_EQ: return Success(LHS == RHS, E, Result); - case BO_NE: return Success(LHS != RHS, E, Result); - } + + // Set up the width and signedness manually, in case it can't be deduced + // from the operation we're performing. + // FIXME: Don't do this in the cases where we can deduce it. + APSInt Value(Info.Ctx.getIntWidth(E->getType()), + E->getType()->isUnsignedIntegerOrEnumerationType()); + if (!handleIntIntBinOp(Info, E, LHSVal.getInt(), E->getOpcode(), + RHSVal.getInt(), Value)) + return false; + return Success(Value, E, Result); } void DataRecursiveIntBinOpEvaluator::process(EvalResult &Result) { diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index 198b9945da..0d134e3cc8 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -334,6 +334,58 @@ namespace incdec { static_assert(incr(0) == 101, ""); } +namespace compound_assign { + constexpr bool test_int() { + int a = 3; + a += 6; + if (a != 9) throw 0; + a -= 2; + if (a != 7) throw 0; + a *= 3; + if (a != 21) throw 0; + a /= 10; + if (a != 2) throw 0; + a <<= 3; + if (a != 16) throw 0; + a %= 6; + if (a != 4) throw 0; + a >>= 1; + if (a != 2) throw 0; + a ^= 10; + if (a != 8) throw 0; + a |= 5; + if (a != 13) throw 0; + a &= 14; + if (a != 12) throw 0; + return true; + } + static_assert(test_int(), ""); + + template + constexpr bool test_overflow() { + T a = 1; + while (a) + a *= 2; // expected-note {{value 2147483648 is outside the range}} expected-note {{ 9223372036854775808 }} + return true; + } + + static_assert(test_overflow(), ""); // expected-error {{constant}} expected-note {{call}} + static_assert(test_overflow(), ""); // ok, unsigned overflow is defined + static_assert(test_overflow(), ""); // ok, short is promoted to int before multiplication + static_assert(test_overflow(), ""); // ok + static_assert(test_overflow(), ""); // ok + static_assert(test_overflow(), ""); // expected-error {{constant}} expected-note {{call}} + + constexpr short test_promotion(short k) { + short s = k; + s *= s; + return s; + } + static_assert(test_promotion(100) == 10000, ""); + static_assert(test_promotion(200) == -25536, ""); + static_assert(test_promotion(256) == 0, ""); +} + namespace loops { constexpr int fib_loop(int a) { int f_k = 0, f_k_plus_one = 1; @@ -407,7 +459,7 @@ namespace loops { int arr[] = { 1, 2, 3, 4, 5 }; int sum = 0; for (int x : arr) - sum = sum + x; + sum += x; return sum; } static_assert(range_for() == 15, ""); @@ -450,7 +502,7 @@ namespace loops { array arr { 1, 2, 3, 4, 5 }; int sum = 0; for (int k : arr) { - sum = sum + k; + sum += k; if (sum > 8) break; } return sum; @@ -458,7 +510,7 @@ namespace loops { static_assert(range_for_2() == 10, ""); } -namespace assignment { +namespace assignment_op { struct A { constexpr A() : n(5) {} int n; -- 2.40.0