forked from OSchip/llvm-project
C++1y constant expression evaluation: support for compound assignments on integers.
llvm-svn: 181287
This commit is contained in:
parent
e78b76fbed
commit
43e77733c2
|
@ -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<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,
|
||||
|
@ -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<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;
|
||||
|
@ -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<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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue