Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -222,6 +222,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 @@ -1394,7 +1394,15 @@ assert(!classify(T)); if (T->isRecordType()) { - const Function *Func = getFunction(E->getConstructor()); + const CXXConstructorDecl *Ctor = E->getConstructor(); + + // Trivial zero initialization. + if (E->requiresZeroInitialization() && Ctor->isTrivial()) { + const Record *R = getRecord(E->getType()); + return this->visitZeroRecordInitializer(R, E); + } + + const Function *Func = getFunction(Ctor); if (!Func) return false; @@ -1467,7 +1475,7 @@ return true; } - return false; + return DiscardResult ? this->emitPopPtr(E) : true; } template @@ -1698,6 +1706,62 @@ llvm_unreachable("unknown primitive type"); } +template +bool ByteCodeExprGen::visitZeroRecordInitializer(const Record *R, + const Expr *E) { + assert(E); + assert(R); + // 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; + } + + for (const Record::Base &B : R->bases()) { + if (!this->emitGetPtrBase(B.Offset, E)) + return false; + if (!this->visitZeroRecordInitializer(B.R, E)) + return false; + if (!this->emitPopPtr(E)) + return false; + } + + // FIXME: Virtual bases. + + return true; +} + template bool ByteCodeExprGen::dereference( const Expr *LV, DerefKind AK, llvm::function_ref Direct, Index: clang/lib/AST/Interp/Descriptor.h =================================================================== --- clang/lib/AST/Interp/Descriptor.h +++ clang/lib/AST/Interp/Descriptor.h @@ -140,6 +140,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 @@ -289,6 +289,12 @@ llvm_unreachable("Invalid descriptor type"); } +QualType Descriptor::getElemQualType() const { + assert(isArray()); + const auto *AT = cast(getType()); + return AT->getElementType(); +} + 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 @@ -931,3 +931,63 @@ O o1(0); } #endif + + +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); + } + static_assert(foo(A()) == 0, ""); + } + + namespace Inheritance { + struct F2 : F { + float f; + }; + + constexpr int foo(F2 f) { + return (int)f.f + f.a; + } + static_assert(foo(F2()) == 0, ""); + } + + namespace BitFields { + struct F { + unsigned a : 6; + }; + constexpr int foo(F f) { + return f.a; + } + static_assert(foo(F()) == 0, ""); + } + + +#if __cplusplus >= 202002L + namespace Failure { + struct S { + int a; + F f{12}; + }; + constexpr int foo(S x) { + return x.a; // expected-note {{read of uninitialized object}} \ + // 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 +} +