diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 4708799773603..131525c98ca59 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1302,7 +1302,15 @@ bool ByteCodeExprGen::VisitCXXConstructExpr( 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; @@ -1479,6 +1487,77 @@ bool ByteCodeExprGen::visitZeroInitializer(QualType QT, 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 if (D->isCompositeArray()) { + const Record *ElemRecord = D->ElemDesc->ElemRecord; + assert(D->ElemDesc->ElemRecord); + for (uint32_t I = 0, N = D->getNumElems(); I != N; ++I) { + if (!this->emitConstUint32(I, E)) + return false; + if (!this->emitArrayElemPtr(PT_Uint32, E)) + return false; + if (!this->visitZeroRecordInitializer(ElemRecord, E)) + return false; + if (!this->emitPopPtr(E)) + return false; + } + } else if (D->isRecord()) { + if (!this->visitZeroRecordInitializer(D->ElemRecord, E)) + return false; + } else { + 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, diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index cd924e911759e..dda954320cd24 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -213,6 +213,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, /// 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. diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index b4c26ac88b5c6..db49a569eff33 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -285,6 +285,12 @@ QualType Descriptor::getType() const { 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(); diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index c5b73eca4c2e9..55a754c3505cc 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -138,6 +138,7 @@ struct Descriptor final { bool IsTemporary, bool IsMutable); QualType getType() const; + QualType getElemQualType() const; SourceLocation getLocation() const; const Decl *asDecl() const { return Source.dyn_cast(); } diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp index 2b79ec8be0311..7976f2d91e99c 100644 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -911,3 +911,115 @@ namespace TemporaryObjectExpr { } static_assert(foo(F()) == 0, ""); } + +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, ""); + } + + namespace Nested { + struct F2 { + float f; + char c; + }; + + struct F { + F2 f2; + int i; + }; + + constexpr int foo(F f) { + return f.i + f.f2.f + f.f2.c; + } + static_assert(foo(F()) == 0, ""); + } + + namespace CompositeArrays { + struct F2 { + float f; + char c; + }; + + struct F { + F2 f2[2]; + int i; + }; + + constexpr int foo(F f) { + return f.i + f.f2[0].f + f.f2[0].c + f.f2[1].f + f.f2[1].c; + } + static_assert(foo(F()) == 0, ""); + } + + /// FIXME: This needs support for unions on the new interpreter. + /// We diagnose an uninitialized object in c++14. +#if __cplusplus > 201402L + namespace Unions { + struct F { + union { + int a; + char c[4]; + float f; + } U; + int i; + }; + + constexpr int foo(F f) { + return f.i + f.U.f; // ref-note {{read of member 'f' of union with active member 'a'}} + } + static_assert(foo(F()) == 0, ""); // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} + } +#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 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 +}