diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1333,24 +1333,44 @@ const Expr *SubExpr = E->getSubExpr(); std::optional T = classify(SubExpr->getType()); - // TODO: Support pointers for inc/dec operators. switch (E->getOpcode()) { case UO_PostInc: { // x++ if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + if (!this->emitIncPtr(E)) + return false; + + return DiscardResult ? this->emitPopPtr(E) : true; + } + return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E); } case UO_PostDec: { // x-- if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + if (!this->emitDecPtr(E)) + return false; + + return DiscardResult ? this->emitPopPtr(E) : true; + } + return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E); } case UO_PreInc: { // ++x if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + this->emitLoadPtr(E); + this->emitConstUint8(1, E); + this->emitAddOffsetUint8(E); + return DiscardResult ? this->emitStorePopPtr(E) : this->emitStorePtr(E); + } + // Post-inc and pre-inc are the same if the value is to be discarded. if (DiscardResult) return this->emitIncPop(*T, E); @@ -1364,6 +1384,13 @@ if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + this->emitLoadPtr(E); + this->emitConstUint8(1, E); + this->emitSubOffsetUint8(E); + return DiscardResult ? this->emitStorePopPtr(E) : this->emitStorePtr(E); + } + // Post-dec and pre-dec are the same if the value is to be discarded. if (DiscardResult) return this->emitDecPop(*T, E); diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -140,6 +140,8 @@ /// Interpreter entry point. bool Interpret(InterpState &S, APValue &Result); +enum class ArithOp { Add, Sub }; + //===----------------------------------------------------------------------===// // Add, Sub, Mul //===----------------------------------------------------------------------===// @@ -1112,6 +1114,34 @@ return OffsetHelper(S, OpPC); } +template +static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC) { + using OneT = Integral<8, false>; + const Pointer &Ptr = S.Stk.pop(); + + // Get the current value on the stack. + S.Stk.push(Ptr.deref()); + + // Now the current Ptr again and a constant 1. + // FIXME: We shouldn't have to push these two on the stack. + S.Stk.push(Ptr.deref()); + S.Stk.push(OneT::from(1)); + if (!OffsetHelper(S, OpPC)) + return false; + + // Store the new value. + Ptr.deref() = S.Stk.pop(); + return true; +} + +static inline bool IncPtr(InterpState &S, CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + +static inline bool DecPtr(InterpState &S, CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + /// 1) Pops a Pointer from the stack. /// 2) Pops another Pointer from the stack. /// 3) Pushes the different of the indices of the two pointers on the stack. diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -393,12 +393,21 @@ // [Pointer, Integral] -> [Pointer] def SubOffset : AluOpcode; -// Pointer, Pointer] - [Integral] +// [Pointer, Pointer] -> [Integral] def SubPtr : Opcode { let Types = [IntegerTypeClass]; let HasGroup = 1; } +// [Pointer] -> [Pointer] +def IncPtr : Opcode { + let HasGroup = 0; +} +// [Pointer] -> [Pointer] +def DecPtr : Opcode { + let HasGroup = 0; +} + //===----------------------------------------------------------------------===// // Binary operators. //===----------------------------------------------------------------------===// diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp --- a/clang/test/AST/Interp/arrays.cpp +++ b/clang/test/AST/Interp/arrays.cpp @@ -235,9 +235,6 @@ // ref-note {{in call to 'BU()'}} 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; @@ -245,6 +242,91 @@ } constexpr int E[] = {1,2,3,4}; - static_assert(getNextElem(E, 1) == 3); -#endif + static_assert(getNextElem(E, 1) == 3, ""); + + constexpr int getFirst() { + const int *e = E; + return *(e++); + } + static_assert(getFirst() == 1, ""); + + constexpr int getFirst2() { + const int *e = E; + e++; + return *e; + } + static_assert(getFirst2() == 2, ""); + + constexpr int getSecond() { + const int *e = E; + return *(++e); + } + static_assert(getSecond() == 2, ""); + + constexpr int getSecond2() { + const int *e = E; + ++e; + return *e; + } + static_assert(getSecond2() == 2, ""); + + constexpr int getLast() { + const int *e = E + 3; + return *(e--); + } + static_assert(getLast() == 4, ""); + + constexpr int getLast2() { + const int *e = E + 3; + e--; + return *e; + } + static_assert(getLast2() == 3, ""); + + constexpr int getSecondToLast() { + const int *e = E + 3; + return *(--e); + } + static_assert(getSecondToLast() == 3, ""); + + constexpr int getSecondToLast2() { + const int *e = E + 3; + --e; + return *e; + } + static_assert(getSecondToLast2() == 3, ""); + + constexpr int bad1() { // ref-error {{never produces a constant expression}} + const int *e = E + 3; + e++; // This is fine because it's a one-past-the-end pointer + return *e; // expected-note {{read of dereferenced one-past-the-end pointer}} \ + // ref-note 2{{read of dereferenced one-past-the-end pointer}} + } + static_assert(bad1() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} + + constexpr int bad2() { // ref-error {{never produces a constant expression}} + const int *e = E + 4; + e++; // expected-note {{cannot refer to element 5 of array of 4 elements}} \ + // ref-note 2{{cannot refer to element 5 of array of 4 elements}} + return *e; // This is UB as well + } + static_assert(bad2() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} + + + constexpr int bad3() { // ref-error {{never produces a constant expression}} + const int *e = E; + e--; // expected-note {{cannot refer to element -1 of array of 4 elements}} \ + // ref-note 2{{cannot refer to element -1 of array of 4 elements}} + return *e; // This is UB as well + } + static_assert(bad3() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} };