aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2013-05-05 23:31:59 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2013-05-05 23:31:59 +0000
commit5528ac9f40ec6cb54e7096908bf2beeed511bce4 (patch)
treefcdc39987b01a3338d6012a828133ceabffae09d
parentb476a14e9b35ef2448b42b033e1f0cceaa3f2778 (diff)
C++1y: support for increment and decrement in constant expression evaluation.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181173 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticASTKinds.td26
-rw-r--r--lib/AST/ExprConstant.cpp246
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp2
-rw-r--r--test/SemaCXX/constant-expression-cxx1y.cpp80
4 files changed, 305 insertions, 49 deletions
diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td
index aea2980e6f..c69f85f18d 100644
--- a/include/clang/Basic/DiagnosticASTKinds.td
+++ b/include/clang/Basic/DiagnosticASTKinds.td
@@ -84,19 +84,19 @@ def note_constexpr_depth_limit_exceeded : Note<
def note_constexpr_call_limit_exceeded : Note<
"constexpr evaluation hit maximum call limit">;
def note_constexpr_lifetime_ended : Note<
- "%select{read of|assignment to}0 %select{temporary|variable}1 "
- "whose lifetime has ended">;
+ "%select{read of|assignment to|increment of|decrement of}0 "
+ "%select{temporary|variable}1 whose lifetime has ended">;
def note_constexpr_access_uninit : Note<
- "%select{read of|assignment to}0 object outside its lifetime "
- "is not allowed in a constant expression">;
+ "%select{read of|assignment to|increment of|decrement of}0 "
+ "object outside its lifetime is not allowed in a constant expression">;
def note_constexpr_modify_const_type : Note<
"modification of object of const-qualified type %0 is not allowed "
"in a constant expression">;
def note_constexpr_access_volatile_type : Note<
- "%select{read of|assignment to}0 volatile-qualified type %1 "
- "is not allowed in a constant expression">;
+ "%select{read of|assignment to|increment of|decrement of}0 "
+ "volatile-qualified type %1 is not allowed in a constant expression">;
def note_constexpr_access_volatile_obj : Note<
- "%select{read of|assignment to}0 volatile "
+ "%select{read of|assignment to|increment of|decrement of}0 volatile "
"%select{temporary|object %2|member %2}1 is not allowed in "
"a constant expression">;
def note_constexpr_ltor_mutable : Note<
@@ -106,14 +106,14 @@ def note_constexpr_ltor_non_const_int : Note<
def note_constexpr_ltor_non_constexpr : Note<
"read of non-constexpr variable %0 is not allowed in a constant expression">;
def note_constexpr_access_null : Note<
- "%select{read of|assignment to}0 dereferenced null pointer "
- "is not allowed in a constant expression">;
+ "%select{read of|assignment to|increment of|decrement of}0 "
+ "dereferenced null pointer is not allowed in a constant expression">;
def note_constexpr_access_past_end : Note<
- "%select{read of|assignment to}0 dereferenced one-past-the-end pointer "
- "is not allowed in a constant expression">;
+ "%select{read of|assignment to|increment of|decrement of}0 "
+ "dereferenced one-past-the-end pointer is not allowed in a constant expression">;
def note_constexpr_access_inactive_union_member : Note<
- "%select{read of|assignment to}0 member %1 of union with "
- "%select{active member %3|no active member}2 "
+ "%select{read of|assignment to|increment of|decrement of}0 "
+ "member %1 of union with %select{active member %3|no active member}2 "
"is not allowed in a constant expression">;
def note_constexpr_modify_global : Note<
"a constant expression cannot modify an object that is visible outside "
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 13fd9df266..5d153c422f 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -1614,7 +1614,9 @@ static void expandArray(APValue &Array, unsigned Index) {
/// 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
@@ -2083,9 +2085,9 @@ CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK,
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.
@@ -2094,7 +2096,7 @@ CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK,
/// 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)
@@ -2133,7 +2135,7 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
}
/// 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;
@@ -2147,6 +2149,160 @@ static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
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) {
@@ -2534,7 +2690,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const 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);
}
@@ -2761,7 +2917,7 @@ public:
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);
}
@@ -2967,7 +3123,7 @@ public:
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);
@@ -2977,6 +3133,26 @@ public:
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);
@@ -3044,7 +3220,7 @@ public:
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);
@@ -3127,7 +3303,7 @@ public:
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); }
@@ -3142,8 +3318,12 @@ public:
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);
@@ -3296,31 +3476,32 @@ bool LValueExprEvaluator::VisitUnaryImag(const UnaryOperator *E) {
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;
@@ -3335,12 +3516,21 @@ bool LValueExprEvaluator::VisitCompoundAssignOperator(
}
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);
}
@@ -6682,7 +6872,7 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
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;
}
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
index e12011d19c..3bc639dd57 100644
--- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
@@ -277,7 +277,7 @@ namespace std_example {
constexpr int prev(int x) {
return --x;
}
-#if 1 // FIXME: !defined CXX1Y
+#ifndef CXX1Y
// expected-error@-4 {{never produces a constant expression}}
// expected-note@-4 {{subexpression}}
#endif
diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp
index 62739ee8c1..2dfded4cc3 100644
--- a/test/SemaCXX/constant-expression-cxx1y.cpp
+++ b/test/SemaCXX/constant-expression-cxx1y.cpp
@@ -161,11 +161,9 @@ namespace string_assign {
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
@@ -180,10 +178,8 @@ namespace string_assign {
}
#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
@@ -286,3 +282,73 @@ namespace null {
}
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, "");
+}