r181287 - C++1y constant expression evaluation: support for compound assignments on integers.
Richard Smith
richard-llvm at metafoo.co.uk
Mon May 6 21:50:00 PDT 2013
Author: rsmith
Date: Mon May 6 23:50:00 2013
New Revision: 181287
URL: http://llvm.org/viewvc/llvm-project?rev=181287&view=rev
Log:
C++1y constant expression evaluation: support for compound assignments on integers.
Modified:
cfe/trunk/lib/AST/ExprConstant.cpp
cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=181287&r1=181286&r2=181287&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon May 6 23:50:00 2013
@@ -1299,6 +1299,128 @@ static bool EvalAndBitcastToAPInt(EvalIn
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<typename Operation>
+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<APSInt>());
+ return true;
+ case BO_Add:
+ Result = CheckedIntArithmetic(Info, E, LHS, RHS, LHS.getBitWidth() + 1,
+ std::plus<APSInt>());
+ return true;
+ case BO_Sub:
+ Result = CheckedIntArithmetic(Info, E, LHS, RHS, LHS.getBitWidth() + 1,
+ std::minus<APSInt>());
+ 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<uint64_t>(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<uint64_t>(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,
@@ -2159,6 +2281,116 @@ static bool isOverflowingIntegerType(AST
}
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<PointerType>())
+ 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 <op>= 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;
const Expr *E;
@@ -3655,14 +3887,10 @@ bool LValueExprEvaluator::VisitCompoundA
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,
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<typename Operation>
-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<APSInt>()), E,
- Result);
- case BO_Add:
- return Success(CheckedIntArithmetic(Info, E, LHS, RHS,
- LHS.getBitWidth() + 1,
- std::plus<APSInt>()), E, Result);
- case BO_Sub:
- return Success(CheckedIntArithmetic(Info, E, LHS, RHS,
- LHS.getBitWidth() + 1,
- std::minus<APSInt>()), 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<uint64_t>(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<uint64_t>(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) {
Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp?rev=181287&r1=181286&r2=181287&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp Mon May 6 23:50:00 2013
@@ -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<typename T>
+ 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<int>(), ""); // expected-error {{constant}} expected-note {{call}}
+ static_assert(test_overflow<unsigned>(), ""); // ok, unsigned overflow is defined
+ static_assert(test_overflow<short>(), ""); // ok, short is promoted to int before multiplication
+ static_assert(test_overflow<unsigned short>(), ""); // ok
+ static_assert(test_overflow<unsigned long long>(), ""); // ok
+ static_assert(test_overflow<long long>(), ""); // 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<int, 5> 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;
More information about the cfe-commits
mailing list