Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -92,7 +92,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); @@ -210,6 +209,7 @@ /// Emits a zero initializer. bool visitZeroInitializer(QualType QT, const Expr *E); + bool visitZeroRecordInitializer(const Record *R, const Expr *E); enum class DerefKind { /// Value is read and pushed to stack. Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1019,24 +1019,6 @@ 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) { @@ -1119,19 +1101,30 @@ template bool ByteCodeExprGen::VisitCXXConstructExpr( const CXXConstructExpr *E) { - if (std::optional GI = allocateLocal(E, /*IsExtended=*/false)) { - if (!this->emitGetPtrLocal(*GI, E)) - return false; + std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/false); + if (!LocalIndex) + return false; - if (!this->visitRecordInitializer(E)) - return false; + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; - if (DiscardResult) - return this->emitPopPtr(E); - return true; + const CXXConstructorDecl *Ctor = E->getConstructor(); + + // If zero-init is requested and the constructor is trivial, zero-init + // all field and bases explicitly. + if (E->requiresZeroInitialization() && Ctor->isTrivial()) { + assert(E->getType()->isRecordType()); + const Record *R = getRecord(E->getType()); + + if (!this->visitZeroRecordInitializer(R, E)) + return false; + } else { + // Otherwise, just call the constructor. + if (!this->visitInitializer(E)) + return false; } - return false; + return DiscardResult ? this->emitPopPtr(E) : true; } template bool ByteCodeExprGen::discard(const Expr *E) { @@ -1240,6 +1233,48 @@ } llvm_unreachable("unknown primitive type"); } +template +bool ByteCodeExprGen::visitZeroRecordInitializer(const Record *R, + const Expr *E) { + // Fields + for (const Record::Field &Field : R->fields()) { + const Descriptor *D = Field.Desc; + if (D->isPrimitive()) { + QualType QT = D->getType(); + PrimType T = classifyPrim(D->getType()); + if (!this->visitZeroInitializer(QT, E)) + return false; + if (!this->emitInitField(T, Field.Offset, E)) + return false; + continue; + } + + // TODO: Add GetPtrFieldPop and get rid of this dup. + if (!this->emitDupPtr(E)) + return false; + if (!this->emitGetPtrField(Field.Offset, E)) + return false; + + if (D->isPrimitiveArray()) { + QualType ET = D->getElemQualType(); + PrimType T = classifyPrim(ET); + for (uint32_t I = 0, N = D->getNumElems(); I != N; ++I) { + if (!this->visitZeroInitializer(ET, E)) + return false; + if (!this->emitInitElem(T, I, E)) + return false; + } + } else { + // FIXME: Implement class and composite array types. + assert(false); + } + + if (!this->emitPopPtr(E)) + return false; + } + + return true; +} template bool ByteCodeExprGen::dereference( Index: clang/lib/AST/Interp/Descriptor.h =================================================================== --- clang/lib/AST/Interp/Descriptor.h +++ clang/lib/AST/Interp/Descriptor.h @@ -137,6 +137,7 @@ bool IsTemporary, bool IsMutable); QualType getType() const; + QualType getElemQualType() const; SourceLocation getLocation() const; const Decl *asDecl() const { return Source.dyn_cast(); } Index: clang/lib/AST/Interp/Descriptor.cpp =================================================================== --- clang/lib/AST/Interp/Descriptor.cpp +++ clang/lib/AST/Interp/Descriptor.cpp @@ -278,6 +278,16 @@ llvm_unreachable("Invalid descriptor type"); } +QualType Descriptor::getElemQualType() const { + assert(isArray()); + QualType T = getType(); + + const auto *CAT = cast(T); + return CAT->getElementType(); + + return T; +} + SourceLocation Descriptor::getLocation() const { if (auto *D = Source.dyn_cast()) return D->getLocation(); Index: clang/test/AST/Interp/records.cpp =================================================================== --- clang/test/AST/Interp/records.cpp +++ clang/test/AST/Interp/records.cpp @@ -899,3 +899,45 @@ return bp.first && bp.second; } static_assert(BPand(BoolPair{true, false}) == false, ""); + + +namespace ZeroInit { + struct F { + int a; + }; + + namespace Simple { + struct A { + char a; + bool b; + int c[4]; + float d; + }; + constexpr int foo(A x) { + return x.a + static_cast(x.b) + x.c[0] + x.c[3] + static_cast(x.d); + } +#if __cplusplus >= 201703L + static_assert(foo(A()) == 0, ""); +#endif + } + +#if __cplusplus >= 202002L + namespace Failure { + struct S { + int a; + F f{12}; + }; + constexpr int foo(S x) { + return x.a; // expected-note {{read of object outside its lifetime}} \ + // ref-note {{read of uninitialized object}} + } + static_assert(foo(S()) == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} + }; +#endif + + +} +