diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -34,6 +34,7 @@ template class VariableScope; template class DeclScope; template class OptionScope; +template class ArrayIndexScope; /// Compilation context for expressions. template @@ -85,6 +86,8 @@ bool VisitConstantExpr(const ConstantExpr *E); bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); bool VisitMemberExpr(const MemberExpr *E); + bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E); + bool VisitOpaqueValueExpr(const OpaqueValueExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -199,6 +202,7 @@ friend class RecordScope; friend class DeclScope; friend class OptionScope; + friend class ArrayIndexScope; /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, const Expr *E); @@ -260,7 +264,7 @@ /// Current scope. VariableScope *VarScope = nullptr; - /// Current argument index. + /// Current argument index. Needed to emit ArrayInitIndexExpr. llvm::Optional ArrayIndex; /// Flag indicating if return value is to be discarded. @@ -362,6 +366,20 @@ } }; +template class ArrayIndexScope final { +public: + ArrayIndexScope(ByteCodeExprGen *Ctx, uint64_t Index) : Ctx(Ctx) { + OldArrayIndex = Ctx->ArrayIndex; + Ctx->ArrayIndex = Index; + } + + ~ArrayIndexScope() { Ctx->ArrayIndex = OldArrayIndex; } + +private: + ByteCodeExprGen *Ctx; + Optional OldArrayIndex; +}; + } // namespace interp } // namespace clang diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -326,6 +326,18 @@ return false; } +template +bool ByteCodeExprGen::VisitArrayInitIndexExpr( + const ArrayInitIndexExpr *E) { + assert(ArrayIndex); + return this->emitConstUint64(*ArrayIndex, E); +} + +template +bool ByteCodeExprGen::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + return this->visit(E->getSourceExpr()); +} + template bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); @@ -628,6 +640,32 @@ return true; } else if (const auto *DIE = dyn_cast(Initializer)) { return this->visitInitializer(DIE->getExpr()); + } else if (const auto *AILE = dyn_cast(Initializer)) { + // TODO: This compiles to quite a lot of bytecode if the array is larger. + // Investigate compiling this to a loop, or at least try to use + // the AILE's Common expr. + const Expr *SubExpr = AILE->getSubExpr(); + size_t Size = AILE->getArraySize().getZExtValue(); + Optional ElemT = classify(SubExpr->getType()); + + if (!ElemT) + return false; + + for (size_t I = 0; I != Size; ++I) { + ArrayIndexScope IndexScope(this, I); + if (!this->emitDupPtr(SubExpr)) + return false; + + if (!this->visit(SubExpr)) + return false; + + if (!this->emitInitElem(*ElemT, I, Initializer)) + return false; + + if (!this->emitPopPtr(Initializer)) + return false; + } + return true; } assert(false && "Unknown expression for array initialization"); @@ -642,13 +680,20 @@ if (const auto CtorExpr = dyn_cast(Initializer)) { const Function *Func = getFunction(CtorExpr->getConstructor()); - if (!Func) + if (!Func || !Func->isConstexpr()) return false; // The This pointer is already on the stack because this is an initializer, // but we need to dup() so the call() below has its own copy. if (!this->emitDupPtr(Initializer)) return false; + + // Constructor arguments. + for (const auto *Arg : CtorExpr->arguments()) { + if (!this->visit(Arg)) + return false; + } + return this->emitCallVoid(Func, Initializer); } else if (const auto *InitList = dyn_cast(Initializer)) { const Record *R = getRecord(InitList->getType()); @@ -657,15 +702,26 @@ for (const Expr *Init : InitList->inits()) { const Record::Field *FieldToInit = R->getField(InitIndex); - if (Optional T = classify(Init->getType())) { - if (!this->emitDupPtr(Initializer)) - return false; + if (!this->emitDupPtr(Initializer)) + return false; + if (Optional T = classify(Init->getType())) { if (!this->visit(Init)) return false; if (!this->emitInitField(*T, FieldToInit->Offset, Initializer)) return false; + } else { + // Non-primitive case. Get a pointer to the field-to-initialize + // on the stack and recurse into visitInitializer(). + if (!this->emitGetPtrField(FieldToInit->Offset, Init)) + return false; + + if (!this->visitInitializer(Init)) + return false; + + if (!this->emitPopPtr(Initializer)) + return false; } ++InitIndex; } diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -105,7 +105,7 @@ const Record::Field *F = R->getField(Member); if (Optional T = this->classify(InitExpr->getType())) { - if (!this->emitDupPtr(InitExpr)) + if (!this->emitThis(InitExpr)) return false; if (!this->visit(InitExpr)) @@ -116,7 +116,7 @@ } else { // Non-primitive case. Get a pointer to the field-to-initialize // on the stack and call visitInitialzer() for it. - if (!this->emitDupPtr(InitExpr)) + if (!this->emitThis(InitExpr)) return false; if (!this->emitGetPtrField(F->Offset, InitExpr)) diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -32,6 +32,9 @@ static_assert(ints.b == 30, ""); static_assert(ints.c, ""); static_assert(ints.getTen() == 10, ""); +static_assert(ints.numbers[0] == 1, ""); +static_assert(ints.numbers[1] == 2, ""); +static_assert(ints.numbers[2] == 3, ""); constexpr const BoolPair &BP = ints.bp; static_assert(BP.first, ""); @@ -62,11 +65,17 @@ static_assert(ints4.a == (40 * 50), ""); static_assert(ints4.b == 0, ""); static_assert(ints4.c, ""); - - -// FIXME: Implement initialization by DeclRefExpr. -//constexpr Ints ints4 = ints3; TODO - +static_assert(ints4.numbers[0] == 1, ""); +static_assert(ints4.numbers[1] == 2, ""); +static_assert(ints4.numbers[2] == 3, ""); + +constexpr Ints ints5 = ints4; +static_assert(ints5.a == (40 * 50), ""); +static_assert(ints5.b == 0, ""); +static_assert(ints5.c, ""); +static_assert(ints5.numbers[0] == 1, ""); +static_assert(ints5.numbers[1] == 2, ""); +static_assert(ints5.numbers[2] == 3, ""); struct Ints2 {