Skip to content

Commit f79f64b

Browse files
committed
[clang][Interp] Implement inc and dec operators
Differential Revision: https://reviews.llvm.org/D136423
1 parent ddec896 commit f79f64b

File tree

5 files changed

+253
-19
lines changed

5 files changed

+253
-19
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,35 +1053,67 @@ bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) {
10531053

10541054
template <class Emitter>
10551055
bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
1056-
if (DiscardResult)
1057-
return true;
1058-
10591056
const Expr *SubExpr = E->getSubExpr();
1057+
Optional<PrimType> T = classify(SubExpr->getType());
10601058

1059+
// TODO: Support pointers for inc/dec operators.
10611060
switch (E->getOpcode()) {
1062-
case UO_PostInc: // x++
1063-
case UO_PostDec: // x--
1064-
case UO_PreInc: // --x
1065-
case UO_PreDec: // ++x
1066-
return false;
1061+
case UO_PostInc: { // x++
1062+
if (!this->visit(SubExpr))
1063+
return false;
1064+
1065+
return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E);
1066+
}
1067+
case UO_PostDec: { // x--
1068+
if (!this->visit(SubExpr))
1069+
return false;
1070+
1071+
return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E);
1072+
}
1073+
case UO_PreInc: { // ++x
1074+
if (!this->visit(SubExpr))
1075+
return false;
1076+
1077+
// Post-inc and pre-inc are the same if the value is to be discarded.
1078+
if (DiscardResult)
1079+
return this->emitIncPop(*T, E);
10671080

1081+
this->emitLoad(*T, E);
1082+
this->emitConst(E, 1);
1083+
this->emitAdd(*T, E);
1084+
return this->emitStore(*T, E);
1085+
}
1086+
case UO_PreDec: { // --x
1087+
if (!this->visit(SubExpr))
1088+
return false;
1089+
1090+
// Post-dec and pre-dec are the same if the value is to be discarded.
1091+
if (DiscardResult)
1092+
return this->emitDecPop(*T, E);
1093+
1094+
this->emitLoad(*T, E);
1095+
this->emitConst(E, 1);
1096+
this->emitSub(*T, E);
1097+
return this->emitStore(*T, E);
1098+
}
10681099
case UO_LNot: // !x
10691100
if (!this->visit(SubExpr))
10701101
return false;
1071-
return this->emitInvBool(E);
1102+
// The Inv doesn't change anything, so skip it if we don't need the result.
1103+
return DiscardResult ? this->emitPop(*T, E) : this->emitInvBool(E);
10721104
case UO_Minus: // -x
10731105
if (!this->visit(SubExpr))
10741106
return false;
1075-
if (Optional<PrimType> T = classify(E->getType()))
1076-
return this->emitNeg(*T, E);
1077-
return false;
1107+
return DiscardResult ? this->emitPop(*T, E) : this->emitNeg(*T, E);
10781108
case UO_Plus: // +x
1079-
return this->visit(SubExpr); // noop
1080-
1109+
if (!this->visit(SubExpr)) // noop
1110+
return false;
1111+
return DiscardResult ? this->emitPop(*T, E) : true;
10811112
case UO_AddrOf: // &x
10821113
// We should already have a pointer when we get here.
1083-
return this->visit(SubExpr);
1084-
1114+
if (!this->visit(SubExpr))
1115+
return false;
1116+
return DiscardResult ? this->emitPop(*T, E) : true;
10851117
case UO_Deref: // *x
10861118
return dereference(
10871119
SubExpr, DerefKind::Read,
@@ -1095,9 +1127,7 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
10951127
case UO_Not: // ~x
10961128
if (!this->visit(SubExpr))
10971129
return false;
1098-
if (Optional<PrimType> T = classify(E->getType()))
1099-
return this->emitComp(*T, E);
1100-
return false;
1130+
return DiscardResult ? this->emitPop(*T, E) : this->emitComp(*T, E);
11011131
case UO_Real: // __real x
11021132
case UO_Imag: // __imag x
11031133
case UO_Extension:

clang/lib/AST/Interp/Interp.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,105 @@ bool Neg(InterpState &S, CodePtr OpPC) {
281281
return true;
282282
}
283283

284+
enum class PushVal : bool {
285+
No,
286+
Yes,
287+
};
288+
enum class IncDecOp {
289+
Inc,
290+
Dec,
291+
};
292+
293+
template <typename T, IncDecOp Op, PushVal DoPush>
294+
bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
295+
T Value = Ptr.deref<T>();
296+
T Result;
297+
298+
if constexpr (DoPush == PushVal::Yes)
299+
S.Stk.push<T>(Result);
300+
301+
if constexpr (Op == IncDecOp::Inc) {
302+
if (!T::increment(Value, &Result)) {
303+
Ptr.deref<T>() = Result;
304+
return true;
305+
}
306+
} else {
307+
if (!T::decrement(Value, &Result)) {
308+
Ptr.deref<T>() = Result;
309+
return true;
310+
}
311+
}
312+
313+
// Something went wrong with the previous operation. Compute the
314+
// result with another bit of precision.
315+
unsigned Bits = Value.bitWidth() + 1;
316+
APSInt APResult;
317+
if constexpr (Op == IncDecOp::Inc)
318+
APResult = ++Value.toAPSInt(Bits);
319+
else
320+
APResult = --Value.toAPSInt(Bits);
321+
322+
// Report undefined behaviour, stopping if required.
323+
const Expr *E = S.Current->getExpr(OpPC);
324+
QualType Type = E->getType();
325+
if (S.checkingForUndefinedBehavior()) {
326+
SmallString<32> Trunc;
327+
APResult.trunc(Result.bitWidth()).toString(Trunc, 10);
328+
auto Loc = E->getExprLoc();
329+
S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
330+
return true;
331+
}
332+
333+
S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type;
334+
return S.noteUndefinedBehavior();
335+
}
336+
337+
/// 1) Pops a pointer from the stack
338+
/// 2) Load the value from the pointer
339+
/// 3) Writes the value increased by one back to the pointer
340+
/// 4) Pushes the original (pre-inc) value on the stack.
341+
template <PrimType Name, class T = typename PrimConv<Name>::T>
342+
bool Inc(InterpState &S, CodePtr OpPC) {
343+
// FIXME: Check initialization of Ptr
344+
const Pointer &Ptr = S.Stk.pop<Pointer>();
345+
346+
return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr);
347+
}
348+
349+
/// 1) Pops a pointer from the stack
350+
/// 2) Load the value from the pointer
351+
/// 3) Writes the value increased by one back to the pointer
352+
template <PrimType Name, class T = typename PrimConv<Name>::T>
353+
bool IncPop(InterpState &S, CodePtr OpPC) {
354+
// FIXME: Check initialization of Ptr
355+
const Pointer &Ptr = S.Stk.pop<Pointer>();
356+
357+
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr);
358+
}
359+
360+
/// 1) Pops a pointer from the stack
361+
/// 2) Load the value from the pointer
362+
/// 3) Writes the value decreased by one back to the pointer
363+
/// 4) Pushes the original (pre-dec) value on the stack.
364+
template <PrimType Name, class T = typename PrimConv<Name>::T>
365+
bool Dec(InterpState &S, CodePtr OpPC) {
366+
// FIXME: Check initialization of Ptr
367+
const Pointer &Ptr = S.Stk.pop<Pointer>();
368+
369+
return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr);
370+
}
371+
372+
/// 1) Pops a pointer from the stack
373+
/// 2) Load the value from the pointer
374+
/// 3) Writes the value decreased by one back to the pointer
375+
template <PrimType Name, class T = typename PrimConv<Name>::T>
376+
bool DecPop(InterpState &S, CodePtr OpPC) {
377+
// FIXME: Check initialization of Ptr
378+
const Pointer &Ptr = S.Stk.pop<Pointer>();
379+
380+
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
381+
}
382+
284383
/// 1) Pops the value from the stack.
285384
/// 2) Pushes the bitwise complemented value on the stack (~V).
286385
template <PrimType Name, class T = typename PrimConv<Name>::T>

clang/lib/AST/Interp/Opcodes.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,11 @@ def Inv: Opcode {
419419
let HasGroup = 1;
420420
}
421421

422+
def Inc: IntegerOpcode;
423+
def IncPop : IntegerOpcode;
424+
def Dec: IntegerOpcode;
425+
def DecPop: IntegerOpcode;
426+
422427
// [Real] -> [Real]
423428
def Neg: Opcode {
424429
let Types = [AluTypeClass];

clang/test/AST/Interp/arrays.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,18 @@ namespace DefaultInit {
131131
constexpr int x = arr.a[0];
132132
}
133133
};
134+
135+
namespace IncDec {
136+
// FIXME: Pointer arithmethic needs to be supported in inc/dec
137+
// unary operators
138+
#if 0
139+
constexpr int getNextElem(const int *A, int I) {
140+
const int *B = (A + I);
141+
++B;
142+
return *B;
143+
}
144+
constexpr int E[] = {1,2,3,4};
145+
146+
static_assert(getNextElem(E, 1) == 3);
147+
#endif
148+
};

clang/test/AST/Interp/literals.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,88 @@ namespace strings {
331331

332332
#pragma clang diagnostic pop
333333
};
334+
335+
#if __cplusplus > 201402L
336+
namespace IncDec {
337+
constexpr int zero() {
338+
int a = 0;
339+
a++;
340+
++a;
341+
a--;
342+
--a;
343+
return a;
344+
}
345+
static_assert(zero() == 0, "");
346+
347+
constexpr int preInc() {
348+
int a = 0;
349+
return ++a;
350+
}
351+
static_assert(preInc() == 1, "");
352+
353+
constexpr int postInc() {
354+
int a = 0;
355+
return a++;
356+
}
357+
static_assert(postInc() == 0, "");
358+
359+
constexpr int preDec() {
360+
int a = 0;
361+
return --a;
362+
}
363+
static_assert(preDec() == -1, "");
364+
365+
constexpr int postDec() {
366+
int a = 0;
367+
return a--;
368+
}
369+
static_assert(postDec() == 0, "");
370+
371+
constexpr int three() {
372+
int a = 0;
373+
return ++a + ++a; // expected-warning {{multiple unsequenced modifications to 'a'}} \
374+
// ref-warning {{multiple unsequenced modifications to 'a'}} \
375+
376+
}
377+
static_assert(three() == 3, "");
378+
379+
constexpr bool incBool() {
380+
bool b = false;
381+
return ++b; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}} \
382+
// ref-error {{ISO C++17 does not allow incrementing expression of type bool}}
383+
}
384+
static_assert(incBool(), "");
385+
386+
constexpr int uninit() {
387+
int a;
388+
++a; // ref-note {{increment of uninitialized}} \
389+
// FIXME: Should also be rejected by new interpreter
390+
return 1;
391+
}
392+
static_assert(uninit(), ""); // ref-error {{not an integral constant expression}} \
393+
// ref-note {{in call to 'uninit()'}}
394+
395+
constexpr int OverFlow() { // ref-error {{never produces a constant expression}}
396+
int a = INT_MAX;
397+
++a; // ref-note 2{{is outside the range}} \
398+
// expected-note {{is outside the range}}
399+
return -1;
400+
}
401+
static_assert(OverFlow() == -1, ""); // expected-error {{not an integral constant expression}} \
402+
// expected-note {{in call to 'OverFlow()'}} \
403+
// ref-error {{not an integral constant expression}} \
404+
// ref-note {{in call to 'OverFlow()'}}
405+
406+
407+
constexpr int UnderFlow() { // ref-error {{never produces a constant expression}}
408+
int a = INT_MIN;
409+
--a; // ref-note 2{{is outside the range}} \
410+
// expected-note {{is outside the range}}
411+
return -1;
412+
}
413+
static_assert(UnderFlow() == -1, ""); // expected-error {{not an integral constant expression}} \
414+
// expected-note {{in call to 'UnderFlow()'}} \
415+
// ref-error {{not an integral constant expression}} \
416+
// ref-note {{in call to 'UnderFlow()'}}
417+
};
418+
#endif

0 commit comments

Comments
 (0)