[clang] f79f64b - [clang][Interp] Implement inc and dec operators
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 28 09:01:42 PDT 2022
Author: Timm Bäder
Date: 2022-10-28T17:47:32+02:00
New Revision: f79f64b8d0829c404609d98f069580fa8cfb4d82
URL: https://github.com/llvm/llvm-project/commit/f79f64b8d0829c404609d98f069580fa8cfb4d82
DIFF: https://github.com/llvm/llvm-project/commit/f79f64b8d0829c404609d98f069580fa8cfb4d82.diff
LOG: [clang][Interp] Implement inc and dec operators
Differential Revision: https://reviews.llvm.org/D136423
Added:
Modified:
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
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 02d7ff2d2304..30ae7fbfc637 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1053,35 +1053,67 @@ bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) {
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;
- return this->emitInvBool(E);
+ // The Inv doesn't change anything, so skip it if we don't need the result.
+ return DiscardResult ? this->emitPop(*T, E) : 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;
+ return DiscardResult ? this->emitPop(*T, E) : this->emitNeg(*T, E);
case UO_Plus: // +x
- return this->visit(SubExpr); // noop
-
+ if (!this->visit(SubExpr)) // noop
+ return false;
+ return DiscardResult ? this->emitPop(*T, E) : 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;
+ return DiscardResult ? this->emitPop(*T, E) : true;
case UO_Deref: // *x
return dereference(
SubExpr, DerefKind::Read,
@@ -1095,9 +1127,7 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
case UO_Not: // ~x
if (!this->visit(SubExpr))
return false;
- if (Optional<PrimType> T = classify(E->getType()))
- return this->emitComp(*T, E);
- return false;
+ return DiscardResult ? this->emitPop(*T, E) : this->emitComp(*T, E);
case UO_Real: // __real x
case UO_Imag: // __imag x
case UO_Extension:
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 8e53a76d563f..81f833735258 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -281,6 +281,105 @@ bool Neg(InterpState &S, CodePtr OpPC) {
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>
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index f9d2a5997625..6523f129bc45 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -419,6 +419,11 @@ def Inv: Opcode {
let HasGroup = 1;
}
+def Inc: IntegerOpcode;
+def IncPop : IntegerOpcode;
+def Dec: IntegerOpcode;
+def DecPop: IntegerOpcode;
+
// [Real] -> [Real]
def Neg: Opcode {
let Types = [AluTypeClass];
diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp
index 17829189f85e..a13bce6fb27d 100644
--- a/clang/test/AST/Interp/arrays.cpp
+++ b/clang/test/AST/Interp/arrays.cpp
@@ -131,3 +131,18 @@ namespace DefaultInit {
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
+};
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index 3eba13cff188..739910d3bc45 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -331,3 +331,88 @@ namespace strings {
#pragma clang diagnostic pop
};
+
+#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
More information about the cfe-commits
mailing list