Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -83,6 +83,7 @@ bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); bool VisitMemberExpr(const MemberExpr *E); bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E); + bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E); bool VisitOpaqueValueExpr(const OpaqueValueExpr *E); bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E); bool VisitStringLiteral(const StringLiteral *E); @@ -93,7 +94,6 @@ bool VisitExprWithCleanups(const ExprWithCleanups *E); bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E); - bool VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *E); bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E); bool VisitTypeTraitExpr(const TypeTraitExpr *E); bool VisitLambdaExpr(const LambdaExpr *E); @@ -137,17 +137,18 @@ } llvm_unreachable("not a primitive type"); } - - /// Evaluates an expression for side effects and discards the result. - bool discard(const Expr *E); - /// Evaluates an expression and places result on stack. + /// Evaluates an expression and places the result on the stack. If the + /// expression is of composite type, a local variable will be created + /// and a pointer to said variable will be placed on the stack. bool visit(const Expr *E); - /// Compiles an initializer. + /// Compiles an initializer. This is like visit() but it will never + /// create a variable and instead rely on a variable already having + /// been created. visitInitializer() then relies on a pointer to this + /// variable being on top of the stack. bool visitInitializer(const Expr *E); - /// Compiles an array initializer. - bool visitArrayInitializer(const Expr *Initializer); - /// Compiles a record initializer. - bool visitRecordInitializer(const Expr *Initializer); + /// Evaluates an expression for side effects and discards the result. + bool discard(const Expr *E); + /// Creates and initializes a variable from the given decl. bool visitVarDecl(const VarDecl *VD); @@ -285,6 +286,10 @@ /// Flag indicating if return value is to be discarded. bool DiscardResult = false; + + /// Flag inidicating if we're initializing an already created + /// variable. This is set in visitInitializer(). + bool Initializing = false; }; extern template class ByteCodeExprGen; Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -43,18 +43,25 @@ template class OptionScope final { public: /// Root constructor, compiling or discarding primitives. - OptionScope(ByteCodeExprGen *Ctx, bool NewDiscardResult) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult) { + OptionScope(ByteCodeExprGen *Ctx, bool NewDiscardResult, + bool NewInitializing) + : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), + OldInitializing(Ctx->Initializing) { Ctx->DiscardResult = NewDiscardResult; + Ctx->Initializing = NewInitializing; } - ~OptionScope() { Ctx->DiscardResult = OldDiscardResult; } + ~OptionScope() { + Ctx->DiscardResult = OldDiscardResult; + Ctx->Initializing = OldInitializing; + } private: /// Parent context. ByteCodeExprGen *Ctx; /// Old discard flag to restore. bool OldDiscardResult; + bool OldInitializing; }; } // namespace interp @@ -229,7 +236,8 @@ case CK_BitCast: if (DiscardResult) return this->discard(SubExpr); - return this->visit(SubExpr); + return Initializing ? this->visitInitializer(SubExpr) + : this->visit(SubExpr); case CK_IntegralToBoolean: case CK_IntegralCast: { @@ -326,7 +334,8 @@ return this->discard(RHS); // Otherwise, visit RHS and optionally discard its value. - return Discard(this->visit(RHS)); + return Discard(Initializing ? this->visitInitializer(RHS) + : this->visit(RHS)); } if (!LT || !RT || !T) @@ -521,12 +530,38 @@ template bool ByteCodeExprGen::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) { - std::optional T = classify(E); + QualType QT = E->getType(); - if (!T) + if (classify(QT)) + return this->visitZeroInitializer(QT, E); + + if (QT->isRecordType()) return false; - return this->visitZeroInitializer(E->getType(), E); + if (QT->isArrayType()) { + const ArrayType *AT = QT->getAsArrayTypeUnsafe(); + assert(AT); + const auto *CAT = cast(AT); + size_t NumElems = CAT->getSize().getZExtValue(); + + if (std::optional ElemT = classify(CAT->getElementType())) { + // TODO(perf): For int and bool types, we can probably just skip this + // since we memset our Block*s to 0 and so we have the desired value + // without this. + for (size_t I = 0; I != NumElems; ++I) { + if (!this->visitZeroInitializer(CAT->getElementType(), E)) + return false; + if (!this->emitInitElem(*ElemT, I, E)) + return false; + } + } else { + assert(false && "default initializer for non-primitive type"); + } + + return true; + } + + return false; } template @@ -552,25 +587,106 @@ template bool ByteCodeExprGen::VisitInitListExpr(const InitListExpr *E) { + // Handle discarding first. + if (DiscardResult) { + for (const Expr *Init : E->inits()) { + if (!this->discard(Init)) + return false; + } + return true; + } + + // Primitive values. if (std::optional T = classify(E->getType())) { assert(E->getNumInits() == 1); return DiscardResult ? this->discard(E->inits()[0]) : this->visit(E->inits()[0]); } - if (std::optional LocalIndex = allocateLocal(E, false)) { - if (!this->emitGetPtrLocal(*LocalIndex, E)) - return false; + QualType T = E->getType(); + if (T->isRecordType()) { + const Record *R = getRecord(T); - if (!this->visitInitializer(E)) - return false; + unsigned InitIndex = 0; + for (const Expr *Init : E->inits()) { + if (!this->emitDupPtr(E)) + return false; - if (DiscardResult) - return this->emitPopPtr(E); + if (std::optional T = classify(Init)) { + const Record::Field *FieldToInit = R->getField(InitIndex); + if (!this->visit(Init)) + return false; + + if (FieldToInit->isBitField()) { + if (!this->emitInitBitField(*T, FieldToInit, E)) + return false; + } else { + if (!this->emitInitField(*T, FieldToInit->Offset, E)) + return false; + } + + if (!this->emitPopPtr(E)) + return false; + ++InitIndex; + } else { + // Initializer for a direct base class. + if (const Record::Base *B = R->getBase(Init->getType())) { + if (!this->emitGetPtrBasePop(B->Offset, Init)) + return false; + + if (!this->visitInitializer(Init)) + return false; + + if (!this->emitPopPtr(E)) + return false; + // Base initializers don't increase InitIndex, since they don't count + // into the Record's fields. + } else { + const Record::Field *FieldToInit = R->getField(InitIndex); + // 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(E)) + return false; + ++InitIndex; + } + } + } return true; } - // TODO: Array, complex, etc. types might appear here as well. + if (T->isArrayType()) { + // FIXME: Array fillers. + unsigned ElementIndex = 0; + for (const Expr *Init : E->inits()) { + if (std::optional T = classify(Init->getType())) { + // Visit the primitive element like normal. + if (!this->visit(Init)) + return false; + if (!this->emitInitElem(*T, ElementIndex, Init)) + return false; + } else { + // Advance the pointer currently on the stack to the given + // dimension. + if (!this->emitConstUint32(ElementIndex, Init)) + return false; + if (!this->emitArrayElemPtrUint32(Init)) + return false; + if (!this->visitInitializer(Init)) + return false; + if (!this->emitPopPtr(Init)) + return false; + } + + ++ElementIndex; + } + return true; + } return false; } @@ -587,7 +703,8 @@ bool ByteCodeExprGen::VisitConstantExpr(const ConstantExpr *E) { // TODO: Check if the ConstantExpr already has a value set and if so, // use that instead of evaluating it again. - return this->visit(E->getSubExpr()); + return Initializing ? this->visitInitializer(E->getSubExpr()) + : this->visit(E->getSubExpr()); } static CharUnits AlignOfType(QualType T, const ASTContext &ASTCtx, @@ -707,8 +824,48 @@ return this->emitConst(*ArrayIndex, E); } +template +bool ByteCodeExprGen::VisitArrayInitLoopExpr( + const ArrayInitLoopExpr *E) { + assert(Initializing); + assert(!DiscardResult); + // 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 = E->getSubExpr(); + size_t Size = E->getArraySize().getZExtValue(); + std::optional ElemT = classify(SubExpr->getType()); + + // So, every iteration, we execute an assignment here + // where the LHS is on the stack (the target array) + // and the RHS is our SubExpr. + for (size_t I = 0; I != Size; ++I) { + ArrayIndexScope IndexScope(this, I); + + if (ElemT) { + if (!this->visit(SubExpr)) + return false; + if (!this->emitInitElem(*ElemT, I, E)) + return false; + } else { + // Get to our array element and recurse into visitInitializer() + if (!this->emitConstUint64(I, SubExpr)) + return false; + if (!this->emitArrayElemPtrUint64(SubExpr)) + return false; + if (!visitInitializer(SubExpr)) + return false; + if (!this->emitPopPtr(E)) + return false; + } + } + return true; +} + template bool ByteCodeExprGen::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + if (Initializing) + return this->visitInitializer(E->getSourceExpr()); return this->visit(E->getSourceExpr()); } @@ -716,7 +873,9 @@ bool ByteCodeExprGen::VisitAbstractConditionalOperator( const AbstractConditionalOperator *E) { return this->visitConditional(E, [this](const Expr *E) { - return DiscardResult ? this->discard(E) : this->visit(E); + return DiscardResult + ? this->discard(E) + : (Initializing ? this->visitInitializer(E) : this->visit(E)); }); } @@ -724,8 +883,40 @@ bool ByteCodeExprGen::VisitStringLiteral(const StringLiteral *E) { if (DiscardResult) return true; - unsigned StringIndex = P.createGlobalString(E); - return this->emitGetPtrGlobal(StringIndex, E); + + if (!Initializing) { + unsigned StringIndex = P.createGlobalString(E); + return this->emitGetPtrGlobal(StringIndex, E); + } + + // We are initializing an array on the stack. + const ConstantArrayType *CAT = + Ctx.getASTContext().getAsConstantArrayType(E->getType()); + assert(CAT && "a string literal that's not a constant array?"); + + // If the initializer string is too long, a diagnostic has already been + // emitted. Read only the array length from the string literal. + unsigned N = + std::min(unsigned(CAT->getSize().getZExtValue()), E->getLength()); + size_t CharWidth = E->getCharByteWidth(); + + for (unsigned I = 0; I != N; ++I) { + uint32_t CodeUnit = E->getCodeUnit(I); + + if (CharWidth == 1) { + this->emitConstSint8(CodeUnit, E); + this->emitInitElemSint8(I, E); + } else if (CharWidth == 2) { + this->emitConstUint16(CodeUnit, E); + this->emitInitElemUint16(I, E); + } else if (CharWidth == 4) { + this->emitConstUint32(CodeUnit, E); + this->emitInitElemUint32(I, E); + } else { + llvm_unreachable("unsupported character width"); + } + } + return true; } template @@ -970,6 +1161,9 @@ if (DiscardResult) return this->discard(SubExpr); + if (Initializing) + return this->visitInitializer(SubExpr); + return this->visit(SubExpr); } @@ -977,13 +1171,17 @@ bool ByteCodeExprGen::VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *E) { const Expr *SubExpr = E->getSubExpr(); - std::optional SubExprT = classify(SubExpr); + if (Initializing) { + // We already have a value, just initialize that. + return this->visitInitializer(SubExpr); + } // If we don't end up using the materialized temporary anyway, don't // bother creating it. if (DiscardResult) return this->discard(SubExpr); + std::optional SubExprT = classify(SubExpr); if (E->getStorageDuration() == SD_Static) { std::optional GlobalIndex = P.createGlobal(E); if (!GlobalIndex) @@ -993,7 +1191,7 @@ E->getLifetimeExtendedTemporaryDecl(); if (SubExprT) { - if (!this->visitInitializer(SubExpr)) + if (!this->visit(SubExpr)) return false; if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) return false; @@ -1012,7 +1210,7 @@ if (SubExprT) { if (std::optional LocalIndex = allocateLocalPrimitive( SubExpr, *SubExprT, /*IsConst=*/true, /*IsExtended=*/true)) { - if (!this->visitInitializer(SubExpr)) + if (!this->visit(SubExpr)) return false; this->emitSetLocal(*SubExprT, *LocalIndex, E); return this->emitGetPtrLocal(*LocalIndex, E); @@ -1031,33 +1229,21 @@ template bool ByteCodeExprGen::VisitCXXBindTemporaryExpr( const CXXBindTemporaryExpr *E) { - + if (Initializing) + return this->visitInitializer(E->getSubExpr()); return this->visit(E->getSubExpr()); } -template -bool ByteCodeExprGen::VisitCXXTemporaryObjectExpr( - const CXXTemporaryObjectExpr *E) { - if (std::optional LocalIndex = - allocateLocal(E, /*IsExtended=*/false)) { - if (!this->emitGetPtrLocal(*LocalIndex, E)) - return false; - - if (!this->visitInitializer(E)) - return false; - - if (DiscardResult) - return this->emitPopPtr(E); - return true; - } - return false; -} - template bool ByteCodeExprGen::VisitCompoundLiteralExpr( const CompoundLiteralExpr *E) { - std::optional T = classify(E->getType()); const Expr *Init = E->getInitializer(); + if (Initializing) { + // We already have a value, just initialize that. + return this->visitInitializer(Init); + } + + std::optional T = classify(E->getType()); if (E->isFileScope()) { if (std::optional GlobalIndex = P.createGlobal(E)) { if (classify(E->getType())) @@ -1096,15 +1282,38 @@ template bool ByteCodeExprGen::VisitLambdaExpr(const LambdaExpr *E) { + assert(Initializing); + const Record *R = P.getOrCreateRecord(E->getLambdaClass()); + + auto *CaptureInitIt = E->capture_init_begin(); + // Initialize all fields (which represent lambda captures) of the + // record with their initializers. + for (const Record::Field &F : R->fields()) { + const Expr *Init = *CaptureInitIt; + ++CaptureInitIt; + + if (std::optional T = classify(Init)) { + if (!this->visit(Init)) + return false; - if (std::optional GI = allocateLocal(E, /*IsExtended=*/false)) { - if (!this->emitGetPtrLocal(*GI, E)) - return false; + if (!this->emitSetField(*T, F.Offset, E)) + return false; + } else { + if (!this->emitDupPtr(E)) + return false; + + if (!this->emitGetPtrField(F.Offset, E)) + return false; + + if (!this->visitInitializer(Init)) + return false; - return this->visitRecordInitializer(E); + if (!this->emitPopPtr(E)) + return false; + } } - return false; + return true; } template @@ -1112,6 +1321,7 @@ if (DiscardResult) return true; + assert(!Initializing); return this->visit(E->getFunctionName()); } @@ -1144,15 +1354,80 @@ template bool ByteCodeExprGen::VisitCXXConstructExpr( const CXXConstructExpr *E) { - if (std::optional GI = allocateLocal(E, /*IsExtended=*/false)) { - if (!this->emitGetPtrLocal(*GI, E)) + QualType T = E->getType(); + assert(!classify(T)); + + if (T->isRecordType()) { + const Function *Func = getFunction(E->getConstructor()); + + if (!Func) + return false; + + assert(Func->hasThisPointer()); + assert(!Func->hasRVO()); + + // If we're discarding a construct expression, we still need + // to allocate a variable and call the constructor and destructor. + if (DiscardResult) { + assert(!Initializing); + std::optional LocalIndex = + allocateLocal(E, /*IsExtended=*/true); + + if (!LocalIndex) + return false; + + if (!this->emitGetPtrLocal(*LocalIndex, E)) + 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(E)) return false; - if (!this->visitRecordInitializer(E)) + // Constructor arguments. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) + return false; + } + + if (!this->emitCall(Func, E)) return false; - if (DiscardResult) - return this->emitPopPtr(E); + // Immediately call the destructor if we have to. + if (DiscardResult) { + if (!this->emitPopPtr(E)) + return false; + } + return true; + } + + if (T->isArrayType()) { + const ConstantArrayType *CAT = + Ctx.getASTContext().getAsConstantArrayType(E->getType()); + assert(CAT); + size_t NumElems = CAT->getSize().getZExtValue(); + const Function *Func = getFunction(E->getConstructor()); + if (!Func || !Func->isConstexpr()) + return false; + + // FIXME(perf): We're calling the constructor once per array element here, + // in the old intepreter we had a special-case for trivial constructors. + for (size_t I = 0; I != NumElems; ++I) { + if (!this->emitConstUint64(I, E)) + return false; + if (!this->emitArrayElemPtrUint64(E)) + return false; + + // Constructor arguments. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) + return false; + } + + if (!this->emitCall(Func, E)) + return false; + } return true; } @@ -1163,7 +1438,8 @@ if (E->containsErrors()) return false; - OptionScope Scope(this, /*NewDiscardResult=*/true); + OptionScope Scope(this, /*NewDiscardResult=*/true, + /*NewInitializing=*/false); return this->Visit(E); } @@ -1172,7 +1448,36 @@ if (E->containsErrors()) return false; - OptionScope Scope(this, /*NewDiscardResult=*/false); + if (E->getType()->isVoidType()) + return this->discard(E); + + // Create local variable to hold the return value. + if (!E->isGLValue() && !classify(E->getType())) { + std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/true); + if (!LocalIndex) + return false; + + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + return this->visitInitializer(E); + } + + // Otherwise,we have a primitive return value, produce the value directly + // and puish it on the stack. + OptionScope Scope(this, /*NewDiscardResult=*/false, + /*NewInitializing=*/false); + return this->Visit(E); +} + +template +bool ByteCodeExprGen::visitInitializer(const Expr *E) { + assert(!classify(E->getType())); + + if (E->containsErrors()) + return false; + + OptionScope Scope(this, /*NewDiscardResult=*/false, + /*NewInitializing=*/true); return this->Visit(E); } @@ -1504,307 +1809,6 @@ return Local.Offset; } -// NB: When calling this function, we have a pointer to the -// array-to-initialize on the stack. -template -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()) { - if (std::optional T = classify(Init->getType())) { - // Visit the primitive element like normal. - if (!this->visit(Init)) - return false; - if (!this->emitInitElem(*T, ElementIndex, Init)) - return false; - } else { - // Advance the pointer currently on the stack to the given - // dimension. - if (!this->emitConstUint32(ElementIndex, Init)) - return false; - if (!this->emitArrayElemPtrUint32(Init)) - return false; - if (!visitInitializer(Init)) - return false; - if (!this->emitPopPtr(Init)) - return false; - } - - ++ElementIndex; - } - 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(); - std::optional ElemT = classify(SubExpr->getType()); - - // So, every iteration, we execute an assignment here - // where the LHS is on the stack (the target array) - // and the RHS is our SubExpr. - for (size_t I = 0; I != Size; ++I) { - ArrayIndexScope IndexScope(this, I); - - if (ElemT) { - if (!this->visit(SubExpr)) - return false; - if (!this->emitInitElem(*ElemT, I, Initializer)) - return false; - } else { - // Get to our array element and recurse into visitInitializer() - if (!this->emitConstUint64(I, SubExpr)) - return false; - if (!this->emitArrayElemPtrUint64(SubExpr)) - return false; - if (!visitInitializer(SubExpr)) - return false; - if (!this->emitPopPtr(Initializer)) - return false; - } - } - return true; - } else if (const auto *IVIE = dyn_cast(Initializer)) { - const ArrayType *AT = IVIE->getType()->getAsArrayTypeUnsafe(); - assert(AT); - const auto *CAT = cast(AT); - size_t NumElems = CAT->getSize().getZExtValue(); - - if (std::optional ElemT = classify(CAT->getElementType())) { - // TODO(perf): For int and bool types, we can probably just skip this - // since we memset our Block*s to 0 and so we have the desired value - // without this. - for (size_t I = 0; I != NumElems; ++I) { - if (!this->visitZeroInitializer(CAT->getElementType(), Initializer)) - return false; - if (!this->emitInitElem(*ElemT, I, Initializer)) - return false; - } - } else { - assert(false && "default initializer for non-primitive type"); - } - - return true; - } else if (const auto *Ctor = dyn_cast(Initializer)) { - const ConstantArrayType *CAT = - Ctx.getASTContext().getAsConstantArrayType(Ctor->getType()); - assert(CAT); - size_t NumElems = CAT->getSize().getZExtValue(); - const Function *Func = getFunction(Ctor->getConstructor()); - if (!Func || !Func->isConstexpr()) - return false; - - // FIXME(perf): We're calling the constructor once per array element here, - // in the old intepreter we had a special-case for trivial constructors. - for (size_t I = 0; I != NumElems; ++I) { - if (!this->emitConstUint64(I, Initializer)) - return false; - if (!this->emitArrayElemPtrUint64(Initializer)) - return false; - - // Constructor arguments. - for (const auto *Arg : Ctor->arguments()) { - if (!this->visit(Arg)) - return false; - } - - if (!this->emitCall(Func, Initializer)) - return false; - } - return true; - } else if (const auto *SL = dyn_cast(Initializer)) { - const ConstantArrayType *CAT = - Ctx.getASTContext().getAsConstantArrayType(SL->getType()); - assert(CAT && "a string literal that's not a constant array?"); - - // If the initializer string is too long, a diagnostic has already been - // emitted. Read only the array length from the string literal. - unsigned N = - std::min(unsigned(CAT->getSize().getZExtValue()), SL->getLength()); - size_t CharWidth = SL->getCharByteWidth(); - - for (unsigned I = 0; I != N; ++I) { - uint32_t CodeUnit = SL->getCodeUnit(I); - - if (CharWidth == 1) { - this->emitConstSint8(CodeUnit, SL); - this->emitInitElemSint8(I, SL); - } else if (CharWidth == 2) { - this->emitConstUint16(CodeUnit, SL); - this->emitInitElemUint16(I, SL); - } else if (CharWidth == 4) { - this->emitConstUint32(CodeUnit, SL); - this->emitInitElemUint32(I, SL); - } else { - llvm_unreachable("unsupported character width"); - } - } - return true; - } else if (const auto *CLE = dyn_cast(Initializer)) { - return visitInitializer(CLE->getInitializer()); - } else if (const auto *EWC = dyn_cast(Initializer)) { - return visitInitializer(EWC->getSubExpr()); - } - - assert(false && "Unknown expression for array initialization"); - return false; -} - -template -bool ByteCodeExprGen::visitRecordInitializer(const Expr *Initializer) { - Initializer = Initializer->IgnoreParenImpCasts(); - assert(Initializer->getType()->isRecordType()); - - if (const auto CtorExpr = dyn_cast(Initializer)) { - const Function *Func = getFunction(CtorExpr->getConstructor()); - - if (!Func) - 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->emitCall(Func, Initializer); - } else if (const auto *InitList = dyn_cast(Initializer)) { - const Record *R = getRecord(InitList->getType()); - - unsigned InitIndex = 0; - for (const Expr *Init : InitList->inits()) { - - if (!this->emitDupPtr(Initializer)) - return false; - - if (std::optional T = classify(Init)) { - const Record::Field *FieldToInit = R->getField(InitIndex); - if (!this->visit(Init)) - return false; - - if (FieldToInit->isBitField()) { - if (!this->emitInitBitField(*T, FieldToInit, Initializer)) - return false; - } else { - if (!this->emitInitField(*T, FieldToInit->Offset, Initializer)) - return false; - } - - if (!this->emitPopPtr(Initializer)) - return false; - ++InitIndex; - } else { - // Initializer for a direct base class. - if (const Record::Base *B = R->getBase(Init->getType())) { - if (!this->emitGetPtrBasePop(B->Offset, Init)) - return false; - - if (!this->visitInitializer(Init)) - return false; - - if (!this->emitPopPtr(Initializer)) - return false; - // Base initializers don't increase InitIndex, since they don't count - // into the Record's fields. - } else { - const Record::Field *FieldToInit = R->getField(InitIndex); - // 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; - } - } - } - - return true; - } else if (const CallExpr *CE = dyn_cast(Initializer)) { - // RVO functions expect a pointer to initialize on the stack. - // Dup our existing pointer so it has its own copy to use. - if (!this->emitDupPtr(Initializer)) - return false; - - return this->visit(CE); - } else if (const auto *DIE = dyn_cast(Initializer)) { - return this->visitInitializer(DIE->getExpr()); - } else if (const auto *BBCE = dyn_cast(Initializer)) { - return this->visit(BBCE); - } else if (const auto *CE = dyn_cast(Initializer)) { - return this->visitInitializer(CE->getSubExpr()); - } else if (const auto *CE = dyn_cast(Initializer)) { - return this->visitInitializer(CE->getSubExpr()); - } else if (const auto *ACO = - dyn_cast(Initializer)) { - return this->visitConditional( - ACO, [this](const Expr *E) { return this->visitRecordInitializer(E); }); - } else if (const auto *LE = dyn_cast(Initializer)) { - const Record *R = P.getOrCreateRecord(LE->getLambdaClass()); - - auto *CaptureInitIt = LE->capture_init_begin(); - // Initialize all fields (which represent lambda captures) of the - // record with their initializers. - for (const Record::Field &F : R->fields()) { - const Expr *Init = *CaptureInitIt; - ++CaptureInitIt; - - if (std::optional T = classify(Init)) { - if (!this->visit(Init)) - return false; - - if (!this->emitSetField(*T, F.Offset, LE)) - return false; - } else { - if (!this->emitDupPtr(LE)) - return false; - - if (!this->emitGetPtrField(F.Offset, LE)) - return false; - - if (!this->visitInitializer(Init)) - return false; - - if (!this->emitPopPtr(LE)) - return false; - } - } - - return true; - } - - return false; -} - -template -bool ByteCodeExprGen::visitInitializer(const Expr *Initializer) { - QualType InitializerType = Initializer->getType(); - - if (InitializerType->isArrayType()) - return visitArrayInitializer(Initializer); - - if (InitializerType->isRecordType()) - return visitRecordInitializer(Initializer); - - // Otherwise, visit the expression like normal. - return this->visit(Initializer); -} - template const RecordType *ByteCodeExprGen::getRecordTy(QualType Ty) { if (const PointerType *PT = dyn_cast(Ty)) @@ -1985,13 +1989,21 @@ std::optional T = classify(ReturnType); bool HasRVO = !ReturnType->isVoidType() && !T; - if (HasRVO && DiscardResult) { - // If we need to discard the return value but the function returns its - // value via an RVO pointer, we need to create one such pointer just - // for this call. - if (std::optional LocalIndex = allocateLocal(E)) { - if (!this->emitGetPtrLocal(*LocalIndex, E)) - return false; + if (HasRVO) { + if (DiscardResult) { + // If we need to discard the return value but the function returns its + // value via an RVO pointer, we need to create one such pointer just + // for this call. + if (std::optional LocalIndex = allocateLocal(E)) { + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + } + } else { + assert(Initializing); + if (!isa(E)) { + if (!this->emitDupPtr(E)) + return false; + } } } @@ -2054,7 +2066,13 @@ template bool ByteCodeExprGen::VisitCXXMemberCallExpr( const CXXMemberCallExpr *E) { - // Get a This pointer on the stack. + if (Initializing) { + // If we're initializing, the current stack top is the pointer to + // initialize, so dup that so this call has its own version. + if (!this->emitDupPtr(E)) + return false; + } + if (!this->visit(E->getImplicitObjectArgument())) return false; @@ -2064,6 +2082,10 @@ template bool ByteCodeExprGen::VisitCXXDefaultInitExpr( const CXXDefaultInitExpr *E) { + + if (Initializing) + return this->visitInitializer(E->getExpr()); + assert(classify(E->getType())); return this->visit(E->getExpr()); } @@ -2076,13 +2098,8 @@ if (std::optional T = classify(E->getExpr())) return this->visit(SubExpr); - if (std::optional LocalIndex = - allocateLocal(SubExpr, /*IsExtended=*/true)) { - if (!this->emitGetPtrLocal(*LocalIndex, E)) - return false; - return this->visitInitializer(SubExpr); - } - return false; + assert(Initializing); + return this->visitInitializer(SubExpr); } template Index: clang/lib/AST/Interp/Context.cpp =================================================================== --- clang/lib/AST/Interp/Context.cpp +++ clang/lib/AST/Interp/Context.cpp @@ -128,7 +128,7 @@ return PT_Float; if (T->isFunctionPointerType() || T->isFunctionReferenceType() || - T->isFunctionType()) + T->isFunctionType() || T->isSpecificBuiltinType(BuiltinType::BoundMember)) return PT_FnPtr; if (T->isReferenceType() || T->isPointerType())