Skip to content

Commit 3a7d476

Browse files
committed
[clang][Interp] Implement array initializers and subscript expressions
Differential Revision: https://reviews.llvm.org/D132727
1 parent aa7c5c9 commit 3a7d476

File tree

7 files changed

+224
-36
lines changed

7 files changed

+224
-36
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,55 @@ bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueIni
231231
return false;
232232
}
233233

234+
template <class Emitter>
235+
bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr(
236+
const ArraySubscriptExpr *E) {
237+
const Expr *Base = E->getBase();
238+
const Expr *Index = E->getIdx();
239+
240+
// Take pointer of LHS, add offset from RHS, narrow result.
241+
// What's left on the stack after this is a pointer.
242+
if (Optional<PrimType> IndexT = classify(Index->getType())) {
243+
if (!this->Visit(Base))
244+
return false;
245+
246+
if (!this->Visit(Index))
247+
return false;
248+
249+
if (!this->emitAddOffset(*IndexT, E))
250+
return false;
251+
252+
if (!this->emitNarrowPtr(E))
253+
return false;
254+
255+
return true;
256+
}
257+
258+
return false;
259+
}
260+
261+
template <class Emitter>
262+
bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
263+
for (const Expr *Init : E->inits()) {
264+
if (!this->visit(Init))
265+
return false;
266+
}
267+
return true;
268+
}
269+
234270
template <class Emitter>
235271
bool ByteCodeExprGen<Emitter>::VisitSubstNonTypeTemplateParmExpr(
236272
const SubstNonTypeTemplateParmExpr *E) {
237273
return this->visit(E->getReplacement());
238274
}
239275

276+
template <class Emitter>
277+
bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) {
278+
// TODO: Check if the ConstantExpr already has a value set and if so,
279+
// use that instead of evaluating it again.
280+
return this->visit(E->getSubExpr());
281+
}
282+
240283
template <class Emitter>
241284
bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
242285
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true);
@@ -492,11 +535,62 @@ ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
492535
return Local.Offset;
493536
}
494537

538+
// NB: When calling this function, we have a pointer to the
539+
// array-to-initialize on the stack.
495540
template <class Emitter>
496-
bool ByteCodeExprGen<Emitter>::visitInitializer(
497-
const Expr *Init, InitFnRef InitFn) {
498-
OptionScope<Emitter> Scope(this, InitFn);
499-
return this->Visit(Init);
541+
bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
542+
assert(Initializer->getType()->isArrayType());
543+
544+
// TODO: Fillers?
545+
if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) {
546+
unsigned ElementIndex = 0;
547+
for (const Expr *Init : InitList->inits()) {
548+
QualType InitType = Init->getType();
549+
550+
if (InitType->isArrayType()) {
551+
// Advance the pointer currently on the stack to the given
552+
// dimension and narrow().
553+
if (!this->emitDupPtr(Init))
554+
return false;
555+
if (!this->emitConstUint32(ElementIndex, Init))
556+
return false;
557+
if (!this->emitAddOffsetUint32(Init))
558+
return false;
559+
if (!this->emitNarrowPtr(Init))
560+
return false;
561+
if (!visitArrayInitializer(Init))
562+
return false;
563+
if (!this->emitPopPtr(Init))
564+
return false;
565+
} else if (Optional<PrimType> T = classify(InitType)) {
566+
// Visit the primitive element like normal.
567+
if (!this->visit(Init))
568+
return false;
569+
if (!this->emitInitElem(*T, ElementIndex, Init))
570+
return false;
571+
} else {
572+
assert(false && "Unhandled type in array initializer initlist");
573+
}
574+
575+
++ElementIndex;
576+
}
577+
578+
} else {
579+
assert(false && "Unknown expression for array initialization");
580+
}
581+
582+
return true;
583+
}
584+
585+
template <class Emitter>
586+
bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) {
587+
QualType InitializerType = Initializer->getType();
588+
589+
if (InitializerType->isArrayType())
590+
return visitArrayInitializer(Initializer);
591+
592+
// Otherwise, visit the expression like normal.
593+
return this->Visit(Initializer);
500594
}
501595

502596
template <class Emitter>

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
6464
ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args)
6565
: Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}
6666

67-
// Expression visitors - result returned on stack.
67+
// Expression visitors - result returned on interp stack.
6868
bool VisitCastExpr(const CastExpr *E);
6969
bool VisitIntegerLiteral(const IntegerLiteral *E);
7070
bool VisitParenExpr(const ParenExpr *E);
@@ -77,6 +77,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
7777
bool VisitDeclRefExpr(const DeclRefExpr *E);
7878
bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E);
7979
bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E);
80+
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
81+
bool VisitInitListExpr(const InitListExpr *E);
82+
bool VisitConstantExpr(const ConstantExpr *E);
8083

8184
protected:
8285
bool visitExpr(const Expr *E) override;
@@ -130,29 +133,45 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
130133
bool discard(const Expr *E);
131134
/// Evaluates an expression and places result on stack.
132135
bool visit(const Expr *E);
133-
/// Compiles an initializer for a local.
134-
bool visitInitializer(const Expr *E, InitFnRef GenPtr);
136+
/// Compiles an initializer.
137+
bool visitInitializer(const Expr *E);
138+
/// Compiles an array initializer.
139+
bool visitArrayInitializer(const Expr *Initializer);
135140

136141
/// Visits an expression and converts it to a boolean.
137142
bool visitBool(const Expr *E);
138143

139144
/// Visits an initializer for a local.
140145
bool visitLocalInitializer(const Expr *Init, unsigned I) {
141-
return visitInitializer(Init, [this, I, Init] {
142-
return this->emitGetPtrLocal(I, Init);
143-
});
146+
if (!this->emitGetPtrLocal(I, Init))
147+
return false;
148+
149+
if (!visitInitializer(Init))
150+
return false;
151+
152+
return this->emitPopPtr(Init);
144153
}
145154

146155
/// Visits an initializer for a global.
147156
bool visitGlobalInitializer(const Expr *Init, unsigned I) {
148-
return visitInitializer(Init, [this, I, Init] {
149-
return this->emitGetPtrGlobal(I, Init);
150-
});
157+
if (!this->emitGetPtrGlobal(I, Init))
158+
return false;
159+
160+
if (!visitInitializer(Init))
161+
return false;
162+
163+
return this->emitPopPtr(Init);
151164
}
152165

153166
/// Visits a delegated initializer.
154167
bool visitThisInitializer(const Expr *I) {
155-
return visitInitializer(I, [this, I] { return this->emitThis(I); });
168+
if (!this->emitThis(I))
169+
return false;
170+
171+
if (!visitInitializer(I))
172+
return false;
173+
174+
return this->emitPopPtr(I);
156175
}
157176

158177
/// Creates a local primitive value.

clang/lib/AST/Interp/ByteCodeStmtGen.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,7 @@ bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
171171
return this->emitRet(*ReturnType, RS);
172172
} else {
173173
// RVO - construct the value in the return location.
174-
auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); };
175-
if (!this->visitInitializer(RE, ReturnLocation))
174+
if (!this->visitInitializer(RE))
176175
return false;
177176
this->emitCleanup();
178177
return this->emitRetVoid(RS);
@@ -232,37 +231,35 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
232231

233232
template <class Emitter>
234233
bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
235-
auto DT = VD->getType();
236-
237234
if (!VD->hasLocalStorage()) {
238235
// No code generation required.
239236
return true;
240237
}
241238

242239
// Integers, pointers, primitives.
243-
if (Optional<PrimType> T = this->classify(DT)) {
240+
if (Optional<PrimType> T = this->classify(VD->getType())) {
244241
const Expr *Init = VD->getInit();
245242

246243
if (!Init)
247244
return false;
248245

249-
auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
246+
unsigned Offset =
247+
this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified());
250248
// Compile the initializer in its own scope.
251249
{
252250
ExprScope<Emitter> Scope(this);
253251
if (!this->visit(Init))
254252
return false;
255253
}
256254
// Set the value.
257-
return this->emitSetLocal(*T, Off, VD);
258-
} else {
259-
// Composite types - allocate storage and initialize it.
260-
if (auto Off = this->allocateLocal(VD)) {
261-
return this->visitLocalInitializer(VD->getInit(), *Off);
262-
} else {
263-
return this->bail(VD);
264-
}
255+
return this->emitSetLocal(*T, Offset, VD);
265256
}
257+
258+
// Composite types - allocate storage and initialize it.
259+
if (Optional<unsigned> Offset = this->allocateLocal(VD))
260+
return this->visitLocalInitializer(VD->getInit(), *Offset);
261+
262+
return this->bail(VD);
266263
}
267264

268265
namespace clang {

clang/lib/AST/Interp/Interp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,9 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
721721
return true;
722722
}
723723

724+
/// 1) Pops the value from the stack
725+
/// 2) Peeks a pointer and gets its index \Idx
726+
/// 3) Sets the value on the pointer, leaving the pointer on the stack.
724727
template <PrimType Name, class T = typename PrimConv<Name>::T>
725728
bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
726729
const T &Value = S.Stk.pop<T>();
@@ -732,6 +735,7 @@ bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
732735
return true;
733736
}
734737

738+
/// The same as InitElem, but pops the pointer as well.
735739
template <PrimType Name, class T = typename PrimConv<Name>::T>
736740
bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
737741
const T &Value = S.Stk.pop<T>();

clang/lib/AST/Interp/Pointer.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ APValue Pointer::toAPValue() const {
106106

107107
// Build the path into the object.
108108
Pointer Ptr = *this;
109-
while (Ptr.isField()) {
109+
while (Ptr.isField() || Ptr.isArrayElement()) {
110110
if (Ptr.isArrayElement()) {
111111
Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
112112
Ptr = Ptr.getArray();
@@ -154,7 +154,8 @@ bool Pointer::isInitialized() const {
154154
void Pointer::initialize() const {
155155
assert(Pointee && "Cannot initialize null pointer");
156156
Descriptor *Desc = getFieldDesc();
157-
if (Desc->isPrimitiveArray()) {
157+
158+
if (Desc->isArray()) {
158159
if (!Pointee->IsStatic) {
159160
// Primitive array initializer.
160161
InitMap *&Map = getInitMap();

clang/lib/AST/Interp/Program.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,14 +334,15 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
334334
} else {
335335
// Arrays of composites. In this case, the array is a list of pointers,
336336
// followed by the actual elements.
337-
Descriptor *Desc =
337+
Descriptor *ElemDesc =
338338
createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
339-
if (!Desc)
339+
if (!ElemDesc)
340340
return nullptr;
341-
InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor);
341+
InterpSize ElemSize =
342+
ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
342343
if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
343344
return {};
344-
return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary,
345+
return allocateDescriptor(D, ElemDesc, NumElems, IsConst, IsTemporary,
345346
IsMutable);
346347
}
347348
}

clang/test/AST/Interp/arrays.cpp

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,85 @@
11
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
22
// RUN: %clang_cc1 -verify=ref %s
33

4+
constexpr int m = 3;
5+
constexpr const int *foo[][5] = {
6+
{nullptr, &m, nullptr, nullptr, nullptr},
7+
{nullptr, nullptr, &m, nullptr, nullptr},
8+
{nullptr, nullptr, nullptr, &m, nullptr},
9+
};
10+
11+
static_assert(foo[0][0] == nullptr, "");
12+
static_assert(foo[0][1] == &m, "");
13+
static_assert(foo[0][2] == nullptr, "");
14+
static_assert(foo[0][3] == nullptr, "");
15+
static_assert(foo[0][4] == nullptr, "");
16+
static_assert(foo[1][0] == nullptr, "");
17+
static_assert(foo[1][1] == nullptr, "");
18+
static_assert(foo[1][2] == &m, "");
19+
static_assert(foo[1][3] == nullptr, "");
20+
static_assert(foo[1][4] == nullptr, "");
21+
static_assert(foo[2][0] == nullptr, "");
22+
static_assert(foo[2][1] == nullptr, "");
23+
static_assert(foo[2][2] == nullptr, "");
24+
static_assert(foo[2][3] == &m, "");
25+
static_assert(foo[2][4] == nullptr, "");
26+
27+
28+
/// A init list for a primitive value.
29+
constexpr int f{5};
30+
static_assert(f == 5, "");
31+
32+
33+
constexpr int getElement(int i) {
34+
int values[] = {1, 4, 9, 16, 25, 36};
35+
return values[i];
36+
}
37+
static_assert(getElement(1) == 4, "");
38+
static_assert(getElement(5) == 36, "");
39+
40+
41+
template<typename T>
42+
constexpr T getElementOf(T* array, int i) {
43+
return array[i];
44+
}
45+
static_assert(getElementOf(foo[0], 1) == &m, "");
46+
447

5-
/// expected-no-diagnostics
6-
/// ref-no-diagnostics
48+
constexpr int data[] = {5, 4, 3, 2, 1};
49+
static_assert(data[0] == 4, ""); // expected-error{{failed}} \
50+
// expected-note{{5 == 4}} \
51+
// ref-error{{failed}} \
52+
// ref-note{{5 == 4}}
53+
54+
55+
constexpr int dynamic[] = {
56+
f, 3, 2 + 5, data[3], *getElementOf(foo[2], 3)
57+
};
58+
static_assert(dynamic[0] == f, "");
59+
static_assert(dynamic[3] == 2, "");
60+
61+
62+
constexpr int dependent[4] = {
63+
0, 1, dependent[0], dependent[1]
64+
};
65+
static_assert(dependent[2] == dependent[0], "");
66+
static_assert(dependent[3] == dependent[1], "");
767

868
#pragma clang diagnostic push
969
#pragma clang diagnostic ignored "-Wc99-extensions"
1070
#pragma clang diagnostic ignored "-Winitializer-overrides"
71+
constexpr int DI[] = {
72+
[0] = 10,
73+
[1] = 20,
74+
30,
75+
40,
76+
[1] = 50
77+
};
78+
static_assert(DI[0] == 10, "");
79+
static_assert(DI[1] == 50, "");
80+
static_assert(DI[2] == 30, "");
81+
static_assert(DI[3] == 40, "");
82+
1183
/// FIXME: The example below tests ImplicitValueInitExprs, but we can't
1284
/// currently evaluate other parts of it.
1385
#if 0

0 commit comments

Comments
 (0)