tbaeder updated this revision to Diff 470163.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D136423/new/

https://reviews.llvm.org/D136423

Files:
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/Interp.h
  clang/lib/AST/Interp/Opcodes.td
  clang/test/AST/Interp/arrays.cpp
  clang/test/AST/Interp/literals.cpp

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
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/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/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 <typename T, IncDecOp Op, PushVal DoPush>
+bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+  T Value = Ptr.deref<T>();
+  T Result;
+
+  if constexpr (DoPush == PushVal::Yes)
+    S.Stk.push<T>(Result);
+
+  if constexpr (Op == IncDecOp::Inc) {
+    if (!T::increment(Value, &Result)) {
+      Ptr.deref<T>() = Result;
+      return true;
+    }
+  } else {
+    if (!T::decrement(Value, &Result)) {
+      Ptr.deref<T>() = 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 <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inc(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(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 <PrimType Name, class T = typename PrimConv<Name>::T>
+bool IncPop(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(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 <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dec(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(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 <PrimType Name, class T = typename PrimConv<Name>::T>
+bool DecPop(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
+}
+
 /// 1) Pops the value from the stack.
 /// 2) Pushes the bitwise complemented value on the stack (~V).
 template <PrimType Name, class T = typename PrimConv<Name>::T>
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1225,35 +1225,75 @@
 
 template <class Emitter>
 bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
-  if (DiscardResult)
-    return true;
-
   const Expr *SubExpr = E->getSubExpr();
+  Optional<PrimType> 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;
+
+    return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E);
+  }
+  case UO_PostDec: { // x--
+    if (!this->visit(SubExpr))
+      return false;
+
+    return DiscardResult ? this->emitDecPop(*T, E) : 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<PrimType> 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 +1307,9 @@
   case UO_Not:    // ~x
     if (!this->visit(SubExpr))
       return false;
-    if (Optional<PrimType> 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:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to