/// Kinds of access we can perform on an object.
enum AccessKinds {
AK_Read,
- AK_Assign
+ AK_Assign,
+ AK_Increment,
+ AK_Decrement
};
/// A handle to a complete object (an object that is not a subobject of
return CompleteObject(BaseVal, BaseType);
}
-/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on
-/// the given glvalue. This can also be used for 'lvalue-to-lvalue' conversions
-/// for looking up the glvalue referred to by an entity of reference type.
+/// \brief Perform an lvalue-to-rvalue conversion on the given glvalue. This
+/// can also be used for 'lvalue-to-lvalue' conversions for looking up the
+/// glvalue referred to by an entity of reference type.
///
/// \param Info - Information about the ongoing evaluation.
/// \param Conv - The expression for which we are performing the conversion.
/// case of a non-class type).
/// \param LVal - The glvalue on which we are attempting to perform this action.
/// \param RVal - The produced value will be placed here.
-static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
+static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
QualType Type,
const LValue &LVal, APValue &RVal) {
if (LVal.Designator.Invalid)
}
/// Perform an assignment of Val to LVal. Takes ownership of Val.
-static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
+static bool handleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
QualType LValType, APValue &Val) {
if (LVal.Designator.Invalid)
return false;
return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val);
}
+static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) {
+ return T->isSignedIntegerType() &&
+ Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy);
+}
+
+namespace {
+struct IncDecSubobjectHandler {
+ EvalInfo &Info;
+ const Expr *E;
+ AccessKinds AccessKind;
+ APValue *Old;
+
+ 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) {
+ // Stash the old value. Also clear Old, so we don't clobber it later
+ // if we're post-incrementing a complex.
+ if (Old) {
+ *Old = Subobj;
+ Old = 0;
+ }
+
+ switch (Subobj.getKind()) {
+ case APValue::Int:
+ return found(Subobj.getInt(), SubobjType);
+ case APValue::Float:
+ return found(Subobj.getFloat(), SubobjType);
+ case APValue::ComplexInt:
+ return found(Subobj.getComplexIntReal(),
+ SubobjType->castAs<ComplexType>()->getElementType()
+ .withCVRQualifiers(SubobjType.getCVRQualifiers()));
+ case APValue::ComplexFloat:
+ return found(Subobj.getComplexFloatReal(),
+ SubobjType->castAs<ComplexType>()->getElementType()
+ .withCVRQualifiers(SubobjType.getCVRQualifiers()));
+ 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()) {
+ // We don't support increment / decrement on integer-cast-to-pointer
+ // values.
+ Info.Diag(E);
+ return false;
+ }
+
+ if (Old) *Old = APValue(Value);
+
+ // bool arithmetic promotes to int, and the conversion back to bool
+ // doesn't reduce mod 2^n, so special-case it.
+ if (SubobjType->isBooleanType()) {
+ if (AccessKind == AK_Increment)
+ Value = 1;
+ else
+ Value = !Value;
+ return true;
+ }
+
+ bool WasNegative = Value.isNegative();
+ if (AccessKind == AK_Increment) {
+ ++Value;
+
+ if (!WasNegative && Value.isNegative() &&
+ isOverflowingIntegerType(Info.Ctx, SubobjType)) {
+ APSInt ActualValue(Value, /*IsUnsigned*/true);
+ HandleOverflow(Info, E, ActualValue, SubobjType);
+ }
+ } else {
+ --Value;
+
+ if (WasNegative && !Value.isNegative() &&
+ isOverflowingIntegerType(Info.Ctx, SubobjType)) {
+ unsigned BitWidth = Value.getBitWidth();
+ APSInt ActualValue(Value.sext(BitWidth + 1), /*IsUnsigned*/false);
+ ActualValue.setBit(BitWidth);
+ HandleOverflow(Info, E, ActualValue, SubobjType);
+ }
+ }
+ return true;
+ }
+ bool found(APFloat &Value, QualType SubobjType) {
+ if (!checkConst(SubobjType))
+ return false;
+
+ if (Old) *Old = APValue(Value);
+
+ APFloat One(Value.getSemantics(), 1);
+ if (AccessKind == AK_Increment)
+ Value.add(One, APFloat::rmNearestTiesToEven);
+ else
+ Value.subtract(One, APFloat::rmNearestTiesToEven);
+ return true;
+ }
+ 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;
+ }
+
+ LValue LVal;
+ LVal.setFrom(Info.Ctx, Subobj);
+ if (!HandleLValueArrayAdjustment(Info, E, LVal, PointeeType,
+ AccessKind == AK_Increment ? 1 : -1))
+ return false;
+ LVal.moveInto(Subobj);
+ return true;
+ }
+ bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
+ llvm_unreachable("shouldn't encounter string elements here");
+ }
+};
+} // end anonymous namespace
+
+/// Perform an increment or decrement on LVal.
+static bool handleIncDec(EvalInfo &Info, const Expr *E, const LValue &LVal,
+ QualType LValType, bool IsIncrement, APValue *Old) {
+ if (LVal.Designator.Invalid)
+ return false;
+
+ if (!Info.getLangOpts().CPlusPlus1y) {
+ Info.Diag(E);
+ return false;
+ }
+
+ AccessKinds AK = IsIncrement ? AK_Increment : AK_Decrement;
+ CompleteObject Obj = findCompleteObject(Info, E, AK, LVal, LValType);
+ IncDecSubobjectHandler Handler = { Info, E, AK, Old };
+ return Obj && findSubobject(Info, E, Obj, LVal.Designator, Handler);
+}
+
/// Build an lvalue for the object argument of a member function call.
static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object,
LValue &This) {
(Definition->isMoveConstructor() && Definition->isTrivial()))) {
LValue RHS;
RHS.setFrom(Info.Ctx, ArgValues[0]);
- return HandleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
+ return handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
RHS, Result);
}
if (!HandleMemberPointerAccess(Info, E, Obj))
return false;
APValue Result;
- if (!HandleLValueToRValueConversion(Info, E, E->getType(), Obj, Result))
+ if (!handleLValueToRValueConversion(Info, E, E->getType(), Obj, Result))
return false;
return DerivedSuccess(Result, E);
}
return false;
APValue RVal;
// Note, we use the subexpression's type in order to retain cv-qualifiers.
- if (!HandleLValueToRValueConversion(Info, E, E->getSubExpr()->getType(),
+ if (!handleLValueToRValueConversion(Info, E, E->getSubExpr()->getType(),
LVal, RVal))
return false;
return DerivedSuccess(RVal, E);
return Error(E);
}
+ RetTy VisitUnaryPostInc(const UnaryOperator *UO) {
+ return VisitUnaryPostIncDec(UO);
+ }
+ RetTy VisitUnaryPostDec(const UnaryOperator *UO) {
+ return VisitUnaryPostIncDec(UO);
+ }
+ RetTy VisitUnaryPostIncDec(const UnaryOperator *UO) {
+ if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure())
+ return Error(UO);
+
+ LValue LVal;
+ if (!EvaluateLValue(UO->getSubExpr(), LVal, Info))
+ return false;
+ APValue RVal;
+ if (!handleIncDec(this->Info, UO, LVal, UO->getSubExpr()->getType(),
+ UO->isIncrementOp(), &RVal))
+ return false;
+ return DerivedSuccess(RVal, UO);
+ }
+
/// Visit a value which is evaluated, but whose value is ignored.
void VisitIgnoredValue(const Expr *E) {
EvaluateIgnoredValue(Info, E);
if (MD->getType()->isReferenceType()) {
APValue RefValue;
- if (!HandleLValueToRValueConversion(this->Info, E, MD->getType(), Result,
+ if (!handleLValueToRValueConversion(this->Info, E, MD->getType(), Result,
RefValue))
return false;
return Success(RefValue, E);
LValueExprEvaluatorBaseTy(Info, Result) {}
bool VisitVarDecl(const Expr *E, const VarDecl *VD);
- bool VisitIncDec(const UnaryOperator *UO);
+ bool VisitUnaryPreIncDec(const UnaryOperator *UO);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); }
bool VisitUnaryDeref(const UnaryOperator *E);
bool VisitUnaryReal(const UnaryOperator *E);
bool VisitUnaryImag(const UnaryOperator *E);
- bool VisitUnaryPreInc(const UnaryOperator *UO) { return VisitIncDec(UO); }
- bool VisitUnaryPreDec(const UnaryOperator *UO) { return VisitIncDec(UO); }
+ bool VisitUnaryPreInc(const UnaryOperator *UO) {
+ return VisitUnaryPreIncDec(UO);
+ }
+ bool VisitUnaryPreDec(const UnaryOperator *UO) {
+ return VisitUnaryPreIncDec(UO);
+ }
bool VisitBinAssign(const BinaryOperator *BO);
bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO);
return true;
}
-bool LValueExprEvaluator::VisitIncDec(const UnaryOperator *UO) {
- if (!Info.getLangOpts().CPlusPlus1y)
+bool LValueExprEvaluator::VisitUnaryPreIncDec(const UnaryOperator *UO) {
+ if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure())
return Error(UO);
if (!this->Visit(UO->getSubExpr()))
return false;
- // FIXME:
- //return handleIncDec(
- // this->Info, CAO, Result, UO->getSubExpr()->getType(),
- // UO->isIncrementOp());
- // (Watch out for promotions: ++short can't overflow, ++bool is always true).
- return Error(UO);
+ return handleIncDec(
+ this->Info, UO, Result, UO->getSubExpr()->getType(),
+ UO->isIncrementOp(), 0);
}
bool LValueExprEvaluator::VisitCompoundAssignOperator(
const CompoundAssignOperator *CAO) {
- if (!Info.getLangOpts().CPlusPlus1y)
+ if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure())
return Error(CAO);
+ APValue RHS;
+
// The overall lvalue result is the result of evaluating the LHS.
- if (!this->Visit(CAO->getLHS()))
+ if (!this->Visit(CAO->getLHS())) {
+ if (Info.keepEvaluatingAfterFailure())
+ Evaluate(RHS, this->Info, CAO->getRHS());
return false;
+ }
- APValue RHS;
if (!Evaluate(RHS, this->Info, CAO->getRHS()))
return false;
}
bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) {
- if (!this->Visit(E->getLHS()))
- return false;
+ if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure())
+ return Error(E);
+
APValue NewVal;
+
+ if (!this->Visit(E->getLHS())) {
+ if (Info.keepEvaluatingAfterFailure())
+ Evaluate(NewVal, this->Info, E->getRHS());
+ return false;
+ }
+
if (!Evaluate(NewVal, this->Info, E->getRHS()))
return false;
- return HandleAssignment(this->Info, E, Result, E->getLHS()->getType(),
+
+ return handleAssignment(this->Info, E, Result, E->getLHS()->getType(),
NewVal);
}
if (E->isGLValue()) {
LValue LV;
LV.setFrom(Info.Ctx, Result);
- if (!HandleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
+ if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
return false;
}
swap(*begin++, *end);
#else
if (begin != end) {
- end = end - 1;
- if (begin == end)
+ if (begin == --end)
return;
- swap(*begin, *end);
- begin = begin + 1;
+ swap(*begin++, *end);
reverse(begin, end);
}
#endif
}
#else
if (a != ae && b != be) {
- if (*a != *b)
+ if (*a++ != *b++)
return false;
- a = a + 1;
- b = b + 1;
return equal(a, ae, b, be);
}
#endif
}
static_assert(test(0), ""); // expected-error {{constant expression}} expected-note {{in call}}
}
+
+namespace incdec {
+ template<typename T> constexpr T &ref(T &&r) { return r; }
+ template<typename T> constexpr T postinc(T &&r) { return (r++, r); }
+ template<typename T> constexpr T postdec(T &&r) { return (r--, r); }
+
+ static_assert(++ref(0) == 1, "");
+ static_assert(ref(0)++ == 0, "");
+ static_assert(postinc(0) == 1, "");
+ static_assert(--ref(0) == -1, "");
+ static_assert(ref(0)-- == 0, "");
+ static_assert(postdec(0) == -1, "");
+
+ constexpr int overflow_int_inc_1 = ref(0x7fffffff)++; // expected-error {{constant}} expected-note {{2147483648}}
+ constexpr int overflow_int_inc_1_ok = ref(0x7ffffffe)++;
+ constexpr int overflow_int_inc_2 = ++ref(0x7fffffff); // expected-error {{constant}} expected-note {{2147483648}}
+ constexpr int overflow_int_inc_2_ok = ++ref(0x7ffffffe);
+
+ // inc/dec on short can't overflow because we promote to int first
+ static_assert(++ref<short>(0x7fff) == (int)0xffff8000u, "");
+ static_assert(--ref<short>(0x8000) == 0x7fff, "");
+
+ // inc on bool sets to true
+ static_assert(++ref(false), ""); // expected-warning {{deprecated}}
+ static_assert(++ref(true), ""); // expected-warning {{deprecated}}
+
+ int arr[10];
+ static_assert(++ref(&arr[0]) == &arr[1], "");
+ static_assert(++ref(&arr[9]) == &arr[10], "");
+ static_assert(++ref(&arr[10]) == &arr[11], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
+ static_assert(ref(&arr[0])++ == &arr[0], "");
+ static_assert(ref(&arr[10])++ == &arr[10], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
+ static_assert(postinc(&arr[0]) == &arr[1], "");
+ static_assert(--ref(&arr[10]) == &arr[9], "");
+ static_assert(--ref(&arr[1]) == &arr[0], "");
+ static_assert(--ref(&arr[0]) != &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
+ static_assert(ref(&arr[1])-- == &arr[1], "");
+ static_assert(ref(&arr[0])-- == &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
+ static_assert(postdec(&arr[1]) == &arr[0], "");
+
+ int x;
+ static_assert(++ref(&x) == &x + 1, "");
+
+ static_assert(++ref(0.0) == 1.0, "");
+ static_assert(ref(0.0)++ == 0.0, "");
+ static_assert(postinc(0.0) == 1.0, "");
+ static_assert(--ref(0.0) == -1.0, "");
+ static_assert(ref(0.0)-- == 0.0, "");
+ static_assert(postdec(0.0) == -1.0, "");
+
+ static_assert(++ref(1e100) == 1e100, "");
+ static_assert(--ref(1e100) == 1e100, "");
+
+ union U {
+ int a, b;
+ };
+ constexpr int f(U u) {
+ return ++u.b; // expected-note {{increment of member 'b' of union with active member 'a'}}
+ }
+ constexpr int wrong_member = f({0}); // expected-error {{constant}} expected-note {{in call to 'f({.a = 0})'}}
+ constexpr int vol = --ref<volatile int>(0); // expected-error {{constant}} expected-note {{decrement of volatile-qualified}}
+
+ constexpr int incr(int k) {
+ int x = k;
+ if (x++ == 100)
+ return x;
+ return incr(x);
+ }
+ static_assert(incr(0) == 101, "");
+}