Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -40,17 +40,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, public Emitter { protected: - // Emitters for opcodes of various arities. - using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &); - using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &); - using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType, - const SourceInfo &); - - // Aliases for types defined in the emitter. - using LabelTy = typename Emitter::LabelTy; - using AddrTy = typename Emitter::AddrTy; - - // Reference to a function generating the pointer of an initialized object.s + // Reference to a function generating the pointer of an initialized object. using InitFnRef = std::function; /// Current compilation context. @@ -64,7 +54,7 @@ ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args) : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {} - // Expression visitors - result returned on stack. + // Expression visitors - result returned on interp stack. bool VisitCastExpr(const CastExpr *E); bool VisitIntegerLiteral(const IntegerLiteral *E); bool VisitParenExpr(const ParenExpr *E); @@ -75,6 +65,8 @@ bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); bool VisitDeclRefExpr(const DeclRefExpr *E); + bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E); + bool VisitInitListExpr(const InitListExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -128,29 +120,45 @@ bool discard(const Expr *E); /// Evaluates an expression and places result on stack. bool visit(const Expr *E); - /// Compiles an initializer for a local. - bool visitInitializer(const Expr *E, InitFnRef GenPtr); + /// Compiles an initializer. + bool visitInitializer(const Expr *E); + /// Compiles an array initializer. + bool visitArrayInitializer(const Expr *Initializer); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); /// Visits an initializer for a local. bool visitLocalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrLocal(I, Init); - }); + if (!this->emitGetPtrLocal(I, Init)) + return false; + + if (!visitInitializer(Init)) + return false; + + return this->emitPopPtr(Init); } /// Visits an initializer for a global. bool visitGlobalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrGlobal(I, Init); - }); + if (!this->emitGetPtrGlobal(I, Init)) + return false; + + if (!visitInitializer(Init)) + return false; + + return this->emitPopPtr(Init); } /// Visits a delegated initializer. bool visitThisInitializer(const Expr *I) { - return visitInitializer(I, [this, I] { return this->emitThis(I); }); + if (!this->emitThis(I)) + return false; + + if (!visitInitializer(I)) + return false; + + return this->emitPopPtr(I); } /// Creates a local primitive value. @@ -228,9 +236,6 @@ /// Current scope. VariableScope *VarScope = nullptr; - /// Current argument index. - llvm::Optional ArrayIndex; - /// Flag indicating if return value is to be discarded. bool DiscardResult = false; Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -235,6 +235,42 @@ return this->bail(BO); } +template +bool ByteCodeExprGen::VisitArraySubscriptExpr( + const ArraySubscriptExpr *E) { + const Expr *Base = E->getBase(); + const Expr *Index = E->getIdx(); + + // Take pointer of LHS, add offset from RHS, narrow result. + // What's left on the stack after this is a pointer. + if (Optional IndexT = classify(Index->getType())) { + if (!this->Visit(Base)) + return false; + + if (!this->Visit(Index)) + return false; + + if (!this->emitAddOffset(*IndexT, E)) + return false; + + if (!this->emitNarrowPtr(E)) + return false; + + return true; + } + + return false; +} + +template +bool ByteCodeExprGen::VisitInitListExpr(const InitListExpr *E) { + for (const Expr *Init : E->inits()) { + if (!this->visit(Init)) + return false; + } + return true; +} + template bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); @@ -490,11 +526,62 @@ return Local.Offset; } +// NB: When calling this function, we have a pointer to the +// array-to-initialize on the stack. template -bool ByteCodeExprGen::visitInitializer( - const Expr *Init, InitFnRef InitFn) { - OptionScope Scope(this, InitFn); - return this->Visit(Init); +bool ByteCodeExprGen::visitArrayInitializer(const Expr *Initializer) { + assert(Initializer->getType()->isArrayType()); + + // TODO: Fillers? + if (const auto *InitList = dyn_cast(Initializer)) { + unsigned ElementIndex = 0; + for (const Expr *Init : InitList->inits()) { + QualType InitType = Init->getType(); + + if (InitType->isArrayType()) { + // Advance the pointer currently on the stack to the given + // dimension and narrow(). + if (!this->emitDupPtr(Init)) + return false; + if (!this->emitConstUint32(ElementIndex, Init)) + return false; + if (!this->emitAddOffsetUint32(Init)) + return false; + if (!this->emitNarrowPtr(Init)) + return false; + if (!visitArrayInitializer(Init)) + return false; + if (!this->emitPopPtr(Init)) + return false; + } else if (Optional T = classify(InitType)) { + // Visit the primitive element like normal. + if (!this->visit(Init)) + return false; + if (!this->emitInitElem(*T, ElementIndex, Init)) + return false; + } else { + assert(false && "Unhandled type in array initializer initlist"); + } + + ++ElementIndex; + } + + } else { + assert(false && "Unknown expression for array initialization"); + } + + return true; +} + +template +bool ByteCodeExprGen::visitInitializer(const Expr *Initializer) { + QualType InitializerType = Initializer->getType(); + + if (InitializerType->isArrayType()) + return visitArrayInitializer(Initializer); + + // Otherwise, visit the expression like normal. + return this->Visit(Initializer); } template Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -171,8 +171,7 @@ return this->emitRet(*ReturnType, RS); } else { // RVO - construct the value in the return location. - auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); }; - if (!this->visitInitializer(RE, ReturnLocation)) + if (!this->visitInitializer(RE)) return false; this->emitCleanup(); return this->emitRetVoid(RS); @@ -232,16 +231,15 @@ template bool ByteCodeStmtGen::visitVarDecl(const VarDecl *VD) { - auto DT = VD->getType(); - if (!VD->hasLocalStorage()) { // No code generation required. return true; } // Integers, pointers, primitives. - if (Optional T = this->classify(DT)) { - auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); + if (Optional T = this->classify(VD->getType())) { + auto Offset = + this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified()); // Compile the initialiser in its own scope. { ExprScope Scope(this); @@ -249,14 +247,13 @@ return false; } // Set the value. - return this->emitSetLocal(*T, Off, VD); + return this->emitSetLocal(*T, Offset, VD); } else { // Composite types - allocate storage and initialize it. - if (auto Off = this->allocateLocal(VD)) { - return this->visitLocalInitializer(VD->getInit(), *Off); - } else { + if (auto Offset = this->allocateLocal(VD)) + return this->visitLocalInitializer(VD->getInit(), *Offset); + else return this->bail(VD); - } } } Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -721,6 +721,9 @@ return true; } +/// 1) Pops the value from the stack +/// 2) Peeks a pointer and gets its index \Idx +/// 3) Sets the value on the pointer, leaving the pointer on the stack. template ::T> bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop(); @@ -732,6 +735,7 @@ return true; } +/// The same as InitElem, but pops the pointer as well. template ::T> bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop(); Index: clang/lib/AST/Interp/Pointer.cpp =================================================================== --- clang/lib/AST/Interp/Pointer.cpp +++ clang/lib/AST/Interp/Pointer.cpp @@ -154,7 +154,8 @@ void Pointer::initialize() const { assert(Pointee && "Cannot initialize null pointer"); Descriptor *Desc = getFieldDesc(); - if (Desc->isPrimitiveArray()) { + + if (Desc->isArray() || Desc->isPrimitiveArray()) { if (!Pointee->IsStatic) { // Primitive array initializer. InitMap *&Map = getInitMap(); Index: clang/lib/AST/Interp/Program.cpp =================================================================== --- clang/lib/AST/Interp/Program.cpp +++ clang/lib/AST/Interp/Program.cpp @@ -334,14 +334,15 @@ } else { // Arrays of composites. In this case, the array is a list of pointers, // followed by the actual elements. - Descriptor *Desc = + Descriptor *ElemDesc = createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); - if (!Desc) + if (!ElemDesc) return nullptr; - InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor); + InterpSize ElemSize = + ElemDesc->getAllocSize() + sizeof(InlineDescriptor); if (std::numeric_limits::max() / ElemSize <= NumElems) return {}; - return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary, + return allocateDescriptor(D, ElemDesc, NumElems, IsConst, IsTemporary, IsMutable); } } Index: clang/test/AST/Interp/arrays.cpp =================================================================== --- /dev/null +++ clang/test/AST/Interp/arrays.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s +// RUN: %clang_cc1 -verify=ref %s + +// expected-no-diagnostics +// ref-no-diagnostics + +constexpr int m = 3; +constexpr const int *foo[][5] = { + {nullptr, &m, nullptr, nullptr, nullptr}, + {nullptr, nullptr, &m, nullptr, nullptr}, + {nullptr, nullptr, nullptr, &m, nullptr}, +}; + +static_assert(foo[0][0] == nullptr, ""); +static_assert(foo[0][1] == &m, ""); +static_assert(foo[0][2] == nullptr, ""); +static_assert(foo[0][3] == nullptr, ""); +static_assert(foo[0][4] == nullptr, ""); +static_assert(foo[1][0] == nullptr, ""); +static_assert(foo[1][1] == nullptr, ""); +static_assert(foo[1][2] == &m, ""); +static_assert(foo[1][3] == nullptr, ""); +static_assert(foo[1][4] == nullptr, ""); +static_assert(foo[2][0] == nullptr, ""); +static_assert(foo[2][1] == nullptr, ""); +static_assert(foo[2][2] == nullptr, ""); +static_assert(foo[2][3] == &m, ""); +static_assert(foo[2][4] == nullptr, ""); + + +/// A init list for a primitive value. +constexpr int f{5}; +static_assert(f == 5, ""); + + +constexpr int getElement(int i) { + int values[] = {1, 4, 9, 16, 25, 36}; + return values[i]; +} +static_assert(getElement(1) == 4, ""); +static_assert(getElement(5) == 36, ""); + + +template +constexpr T getElementOf(T* array, int i) { + return array[i]; +} +static_assert(getElementOf(foo[0], 1) == &m, ""); + + +constexpr int data[] = {5, 4, 3, 2, 1}; +// TODO: There is currently a problem with how we handle +// Pointer::toAPValue() for arrays. The following static_assert +// will run into an assertion because we don't set the +// LValuePath correctly. +//static_assert(data[0] == 4); + + +constexpr int dynamic[] = { + f, 3, 2 + 5, data[3], *getElementOf(foo[2], 3) +}; +static_assert(dynamic[0] == f, ""); +static_assert(dynamic[3] == 2, ""); +