Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1225,35 +1225,81 @@ template bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { - if (DiscardResult) - return true; - const Expr *SubExpr = E->getSubExpr(); + Optional T = classify(SubExpr->getType()); + // TODO: Support pointers for inc/dec operators. switch (E->getOpcode()) { - case UO_PostInc: // x++ - case UO_PostDec: // x-- - case UO_PreInc: // --x - case UO_PreDec: // ++x - return false; + case UO_PostInc: { // x++ + if (!this->visit(SubExpr)) + return false; + + if (DiscardResult) + return this->emitIncPop(*T, E); + + return this->emitInc(*T, E); + } + case UO_PostDec: { // x-- + if (!this->visit(SubExpr)) + return false; + + if (DiscardResult) + return this->emitDecPop(*T, E); + + return this->emitDec(*T, E); + } + case UO_PreInc: { // ++x + if (!this->visit(SubExpr)) + return false; + + // Post-inc and pre-inc are the same if the value is to be discarded. + if (DiscardResult) + return this->emitIncPop(*T, E); + this->emitLoad(*T, E); + this->emitConst(E, 1); + this->emitAdd(*T, E); + return this->emitStore(*T, E); + } + case UO_PreDec: { // --x + if (!this->visit(SubExpr)) + return false; + + // Post-dec and pre-dec are the same if the value is to be discarded. + if (DiscardResult) + return this->emitDecPop(*T, E); + + this->emitLoad(*T, E); + this->emitConst(E, 1); + this->emitSub(*T, E); + return this->emitStore(*T, E); + } case UO_LNot: // !x if (!this->visit(SubExpr)) return false; + // The Inv doesn't change anything, so skip it if we don't need the result. + if (DiscardResult) + return this->emitPop(*T, E); return this->emitInvBool(E); case UO_Minus: // -x if (!this->visit(SubExpr)) return false; - if (Optional T = classify(E->getType())) - return this->emitNeg(*T, E); - return false; + if (DiscardResult) + return this->emitPop(*T, E); + return this->emitNeg(*T, E); case UO_Plus: // +x - return this->visit(SubExpr); // noop - + if (!this->visit(SubExpr)) // noop + return false; + if (DiscardResult) + return this->emitPop(*T, E); + return true; case UO_AddrOf: // &x // We should already have a pointer when we get here. - return this->visit(SubExpr); - + if (!this->visit(SubExpr)) + return false; + if (DiscardResult) + return this->emitPop(*T, E); + return true; case UO_Deref: // *x return dereference( SubExpr, DerefKind::Read, @@ -1267,9 +1313,9 @@ case UO_Not: // ~x if (!this->visit(SubExpr)) return false; - if (Optional T = classify(E->getType())) - return this->emitComp(*T, E); - return false; + if (DiscardResult) + return this->emitPop(*T, E); + return this->emitComp(*T, E); case UO_Real: // __real x case UO_Imag: // __imag x case UO_Extension: Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -328,6 +328,105 @@ return true; } +enum class PushVal : bool { + No, + Yes, +}; +enum class IncDecOp { + Inc, + Dec, +}; + +template +bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + T Value = Ptr.deref(); + T Result; + + if constexpr (DoPush == PushVal::Yes) + S.Stk.push(Result); + + if constexpr (Op == IncDecOp::Inc) { + if (!T::increment(Value, &Result)) { + Ptr.deref() = Result; + return true; + } + } else { + if (!T::decrement(Value, &Result)) { + Ptr.deref() = Result; + return true; + } + } + + // Something went wrong with the previous operation. Compute the + // result with another bit of precision. + unsigned Bits = Value.bitWidth() + 1; + APSInt APResult; + if constexpr (Op == IncDecOp::Inc) + APResult = ++Value.toAPSInt(Bits); + else + APResult = --Value.toAPSInt(Bits); + + // Report undefined behaviour, stopping if required. + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + if (S.checkingForUndefinedBehavior()) { + SmallString<32> Trunc; + APResult.trunc(Result.bitWidth()).toString(Trunc, 10); + auto Loc = E->getExprLoc(); + S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; + return true; + } + + S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type; + return S.noteUndefinedBehavior(); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value increased by one back to the pointer +/// 4) Pushes the original (pre-inc) value on the stack. +template ::T> +bool Inc(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + + return IncDecHelper(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value increased by one back to the pointer +template ::T> +bool IncPop(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + + return IncDecHelper(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value decreased by one back to the pointer +/// 4) Pushes the original (pre-dec) value on the stack. +template ::T> +bool Dec(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + + return IncDecHelper(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value decreased by one back to the pointer +template ::T> +bool DecPop(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop(); + + return IncDecHelper(S, OpPC, Ptr); +} + /// 1) Pops the value from the stack. /// 2) Pushes the bitwise complemented value on the stack (~V). template ::T> Index: clang/lib/AST/Interp/Opcodes.td =================================================================== --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -446,6 +446,11 @@ let HasGroup = 1; } +def Inc: IntegerOpcode; +def IncPop : IntegerOpcode; +def Dec: IntegerOpcode; +def DecPop: IntegerOpcode; + // [Real] -> [Real] def Neg: Opcode { let Types = [NonPtrTypeClass]; Index: clang/test/AST/Interp/arrays.cpp =================================================================== --- clang/test/AST/Interp/arrays.cpp +++ clang/test/AST/Interp/arrays.cpp @@ -180,3 +180,18 @@ constexpr int x = arr.a[0]; } }; + +namespace IncDec { + // FIXME: Pointer arithmethic needs to be supported in inc/dec + // unary operators +#if 0 + constexpr int getNextElem(const int *A, int I) { + const int *B = (A + I); + ++B; + return *B; + } + constexpr int E[] = {1,2,3,4}; + + static_assert(getNextElem(E, 1) == 3); +#endif +}; Index: clang/test/AST/Interp/literals.cpp =================================================================== --- clang/test/AST/Interp/literals.cpp +++ clang/test/AST/Interp/literals.cpp @@ -361,3 +361,88 @@ constexpr double db = true; static_assert(db == 1.0, ""); }; + +#if __cplusplus > 201402L +namespace IncDec { + constexpr int zero() { + int a = 0; + a++; + ++a; + a--; + --a; + return a; + } + static_assert(zero() == 0, ""); + + constexpr int preInc() { + int a = 0; + return ++a; + } + static_assert(preInc() == 1, ""); + + constexpr int postInc() { + int a = 0; + return a++; + } + static_assert(postInc() == 0, ""); + + constexpr int preDec() { + int a = 0; + return --a; + } + static_assert(preDec() == -1, ""); + + constexpr int postDec() { + int a = 0; + return a--; + } + static_assert(postDec() == 0, ""); + + constexpr int three() { + int a = 0; + return ++a + ++a; // expected-warning {{multiple unsequenced modifications to 'a'}} \ + // ref-warning {{multiple unsequenced modifications to 'a'}} \ + + } + static_assert(three() == 3, ""); + + constexpr bool incBool() { + bool b = false; + return ++b; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}} \ + // ref-error {{ISO C++17 does not allow incrementing expression of type bool}} + } + static_assert(incBool(), ""); + + constexpr int uninit() { + int a; + ++a; // ref-note {{increment of uninitialized}} \ + // FIXME: Should also be rejected by new interpreter + return 1; + } + static_assert(uninit(), ""); // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'uninit()'}} + + constexpr int OverFlow() { // ref-error {{never produces a constant expression}} + int a = INT_MAX; + ++a; // ref-note 2{{is outside the range}} \ + // expected-note {{is outside the range}} + return -1; + } + static_assert(OverFlow() == -1, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'OverFlow()'}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'OverFlow()'}} + + + constexpr int UnderFlow() { // ref-error {{never produces a constant expression}} + int a = INT_MIN; + --a; // ref-note 2{{is outside the range}} \ + // expected-note {{is outside the range}} + return -1; + } + static_assert(UnderFlow() == -1, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'UnderFlow()'}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'UnderFlow()'}} +}; +#endif