Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -746,7 +746,9 @@ PrimType Ty, bool IsConst, bool IsExtended) { - Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is()); + const Descriptor::MetadataSize MDSize{sizeof(InlineDescriptor)}; + Descriptor *D = + P.createDescriptor(Src, Ty, MDSize, IsConst, Src.is()); Scope::Local Local = this->createLocal(D); if (auto *VD = dyn_cast_or_null(Src.dyn_cast())) Locals.insert({VD, Local}); @@ -757,6 +759,7 @@ template llvm::Optional ByteCodeExprGen::allocateLocal(DeclTy &&Src, bool IsExtended) { + const Descriptor::MetadataSize MDSize{sizeof(InlineDescriptor)}; QualType Ty; const ValueDecl *Key = nullptr; @@ -774,8 +777,9 @@ Ty = E->getType(); } - Descriptor *D = P.createDescriptor( - Src, Ty.getTypePtr(), Ty.isConstQualified(), IsTemporary, false, Init); + Descriptor *D = + P.createDescriptor(Src, Ty.getTypePtr(), MDSize, Ty.isConstQualified(), + IsTemporary, false, Init); if (!D) return {}; Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -394,19 +394,17 @@ if (Optional T = this->classify(VD->getType())) { const Expr *Init = VD->getInit(); - if (!Init) - return false; - unsigned Offset = this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified()); // Compile the initializer in its own scope. - { + if (Init) { ExprScope Scope(this); if (!this->visit(Init)) return false; + + return this->emitSetLocal(*T, Offset, VD); } - // Set the value. - return this->emitSetLocal(*T, Offset, VD); + return true; } // Composite types - allocate storage and initialize it. Index: clang/lib/AST/Interp/Context.cpp =================================================================== --- clang/lib/AST/Interp/Context.cpp +++ clang/lib/AST/Interp/Context.cpp @@ -125,7 +125,7 @@ bool Context::Run(State &Parent, Function *Func, APValue &Result) { InterpState State(Parent, *P, Stk, *this); - State.Current = new InterpFrame(State, Func, nullptr, {}, {}); + State.Current = new InterpFrame(State, Func, nullptr, {}); if (Interpret(State, Result)) return true; Stk.clear(); Index: clang/lib/AST/Interp/Descriptor.h =================================================================== --- clang/lib/AST/Interp/Descriptor.h +++ clang/lib/AST/Interp/Descriptor.h @@ -56,6 +56,8 @@ const InterpSize ElemSize; /// Size of the storage, in host bytes. const InterpSize Size; + // Size of the metadata. + const InterpSize MDSize; /// Size of the allocation (storage + metadata), in host bytes. const InterpSize AllocSize; @@ -66,6 +68,11 @@ /// Token to denote structures of unknown size. struct UnknownSize {}; + struct MetadataSize { + size_t NumBytes; + }; + static constexpr MetadataSize NoMetadata = MetadataSize{0}; + /// Pointer to the record, if block contains records. Record *const ElemRecord = nullptr; /// Descriptor of the array element. @@ -85,26 +92,26 @@ const BlockMoveFn MoveFn = nullptr; /// Allocates a descriptor for a primitive. - Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary, - bool IsMutable); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst, + bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of primitives. - Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems, + bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of primitives of unknown size. Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize); /// Allocates a descriptor for an array of composites. - Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD, + unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of composites of unknown size. Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize); /// Allocates a descriptor for a record. - Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary, - bool IsMutable); + Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst, + bool IsTemporary, bool IsMutable); QualType getType() const; SourceLocation getLocation() const; @@ -134,6 +141,8 @@ unsigned getAllocSize() const { return AllocSize; } /// returns the size of an element when the structure is viewed as an array. unsigned getElemSize() const { return ElemSize; } + /// Returns the size of the metadata. + unsigned getMetadataSize() const { return MDSize; } /// Returns the number of elements stored in the block. unsigned getNumElems() const { Index: clang/lib/AST/Interp/Descriptor.cpp =================================================================== --- clang/lib/AST/Interp/Descriptor.cpp +++ clang/lib/AST/Interp/Descriptor.cpp @@ -190,60 +190,65 @@ COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy, return nullptr); } -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst, - bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), - MoveFn(getMovePrim(Type)) { +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + bool IsConst, bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(primSize(Type)), Size(ElemSize), MDSize(MD.NumBytes), + AllocSize(align(Size + MDSize)), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)), + DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) { + assert(AllocSize >= Size); assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + size_t NumElems, bool IsConst, bool IsTemporary, + bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), - AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), - CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), - MoveFn(getMoveArrayPrim(Type)) { + MDSize(MD.NumBytes), AllocSize(align(Size) + sizeof(InitMap *) + MDSize), + IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), + IsArray(true), CtorFn(getCtorArrayPrim(Type)), + DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { assert(Source && "Missing source"); } Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize) - : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), + : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD, + unsigned NumElems, bool IsConst, bool IsTemporary, + bool IsMutable) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(ElemSize * NumElems), - AllocSize(std::max(alignof(void *), Size)), ElemDesc(Elem), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), - MoveFn(moveArrayDesc) { + Size(ElemSize * NumElems), MDSize(MD.NumBytes), + AllocSize(std::max(alignof(void *), Size) + MDSize), + ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), + DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem), - IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), - CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { + Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)), + ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), + IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), + MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst, - bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD, + bool IsConst, bool IsTemporary, bool IsMutable) : Source(D), ElemSize(std::max(alignof(void *), R->getFullSize())), - Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord), - DtorFn(dtorRecord), MoveFn(moveRecord) { + Size(ElemSize), MDSize(MD.NumBytes), AllocSize(Size + MDSize), + ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord), + MoveFn(moveRecord) { assert(Source && "Missing source"); } Index: clang/lib/AST/Interp/EvalEmitter.cpp =================================================================== --- clang/lib/AST/Interp/EvalEmitter.cpp +++ clang/lib/AST/Interp/EvalEmitter.cpp @@ -23,7 +23,7 @@ InterpStack &Stk, APValue &Result) : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) { // Create a dummy frame for the interpreter which does not have locals. - S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer()); + S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr()); } llvm::Expected EvalEmitter::interpretExpr(const Expr *E) { Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -561,7 +561,10 @@ template ::T> bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Stk.push(S.Current->getLocal(I)); + const Pointer &Ptr = S.Current->getLocalPointer(I); + if (!CheckLoad(S, OpPC, Ptr)) + return false; + S.Stk.push(Ptr.deref()); return true; } @@ -920,6 +923,8 @@ const Pointer &Ptr = S.Stk.peek(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); Ptr.deref() = Value; return true; } @@ -930,6 +935,8 @@ const Pointer &Ptr = S.Stk.pop(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); Ptr.deref() = Value; return true; } Index: clang/lib/AST/Interp/InterpBlock.h =================================================================== --- clang/lib/AST/Interp/InterpBlock.h +++ clang/lib/AST/Interp/InterpBlock.h @@ -31,7 +31,21 @@ /// A memory block, either on the stack or in the heap. /// -/// The storage described by the block immediately follows it in memory. +/// The storage described by the block is immediately followed by +/// optional metadata, which is followed by the actual data. +/// +/// Block* rawData() data() +/// │ │ │ +/// │ │ │ +/// ▼ ▼ ▼ +/// ┌───────────────┬─────────────────────────┬─────────────────┐ +/// │ Block │ Metadata │ Data │ +/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │ +/// └───────────────┴─────────────────────────┴─────────────────┘ +/// +/// Desc->getAllocSize() describes the size after the Block, i.e. +/// the data size and the metadata size. +/// class Block final { public: // Creates a new block. @@ -59,7 +73,20 @@ llvm::Optional getDeclID() const { return DeclID; } /// Returns a pointer to the stored data. - char *data() { return reinterpret_cast(this + 1); } + /// You are allowed to read Desc->getSize() bytes from this address. + char *data() { + // Data might contain metadata as well. + size_t DataOffset = Desc->getMetadataSize(); + return reinterpret_cast(reinterpret_cast(this) + + sizeof(Block) + DataOffset); + } + + /// Returns a pointer to the raw data, including metadata. + /// You are allowed to read Desc->getAllocSize() bytes from this address. + char *rawData() { + return reinterpret_cast(reinterpret_cast(this) + + sizeof(Block)); + } /// Returns a view over the data. template @@ -67,7 +94,7 @@ /// Invokes the constructor. void invokeCtor() { - std::memset(data(), 0, getSize()); + std::memset(rawData(), 0, Desc->getAllocSize()); if (Desc->CtorFn) Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, /*isActive=*/true, Desc); Index: clang/lib/AST/Interp/InterpFrame.h =================================================================== --- clang/lib/AST/Interp/InterpFrame.h +++ clang/lib/AST/Interp/InterpFrame.h @@ -33,7 +33,7 @@ /// Creates a new frame for a method call. InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This); + CodePtr RetPC); /// Creates a new frame with the values that make sense. /// I.e., the caller is the current frame of S, @@ -75,10 +75,11 @@ /// Mutates a local variable. template void setLocal(unsigned Offset, const T &Value) { localRef(Offset) = Value; + localInlineDesc(Offset)->IsInitialized = true; } /// Returns a pointer to a local variables. - Pointer getLocalPointer(unsigned Offset); + Pointer getLocalPointer(unsigned Offset) const; /// Returns the value of an argument. template const T &getParam(unsigned Offset) const { @@ -124,7 +125,7 @@ /// Returns an offset to a local. template T &localRef(unsigned Offset) const { - return *reinterpret_cast(Locals.get() + Offset); + return getLocalPointer(Offset).deref(); } /// Returns a pointer to a local's block. @@ -132,6 +133,11 @@ return Locals.get() + Offset - sizeof(Block); } + // Returns the inline descriptor of the local. + InlineDescriptor *localInlineDesc(unsigned Offset) const { + return reinterpret_cast(Locals.get() + Offset); + } + private: /// Reference to the interpreter state. InterpState &S; Index: clang/lib/AST/Interp/InterpFrame.cpp =================================================================== --- clang/lib/AST/Interp/InterpFrame.cpp +++ clang/lib/AST/Interp/InterpFrame.cpp @@ -20,8 +20,8 @@ using namespace clang::interp; InterpFrame::InterpFrame(InterpState &S, const Function *Func, - InterpFrame *Caller, CodePtr RetPC, Pointer &&This) - : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC), + InterpFrame *Caller, CodePtr RetPC) + : Caller(Caller), S(S), Func(Func), RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0), Args(static_cast(S.Stk.top())), FrameOffset(S.Stk.size()) { if (Func) { @@ -31,6 +31,10 @@ for (auto &Local : Scope.locals()) { Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); B->invokeCtor(); + auto *ID = localInlineDesc(Local.Offset); + ID->Desc = Local.Desc; + ID->IsActive = true; + ID->Offset = Local.Offset; } } } @@ -38,11 +42,7 @@ } InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC) - : Caller(S.Current), S(S), Func(Func), RetPC(RetPC), - ArgSize(Func ? Func->getArgSize() : 0), - Args(static_cast(S.Stk.top())), FrameOffset(S.Stk.size()) { - assert(Func); - + : InterpFrame(S, Func, S.Current, RetPC) { // As per our calling convention, the this pointer is // part of the ArgSize. // If the function has RVO, the RVO pointer is first. @@ -55,16 +55,6 @@ else This = stackRef(0); } - - if (unsigned FrameSize = Func->getFrameSize()) { - Locals = std::make_unique(FrameSize); - for (auto &Scope : Func->scopes()) { - for (auto &Local : Scope.locals()) { - Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); - B->invokeCtor(); - } - } - } } InterpFrame::~InterpFrame() { @@ -183,10 +173,10 @@ return Func->getDecl(); } -Pointer InterpFrame::getLocalPointer(unsigned Offset) { +Pointer InterpFrame::getLocalPointer(unsigned Offset) const { assert(Offset < Func->getFrameSize() && "Invalid local offset."); - return Pointer( - reinterpret_cast(Locals.get() + Offset - sizeof(Block))); + return Pointer(reinterpret_cast(localBlock(Offset)), + sizeof(InlineDescriptor)); } Pointer InterpFrame::getParamPointer(unsigned Off) { Index: clang/lib/AST/Interp/Pointer.h =================================================================== --- clang/lib/AST/Interp/Pointer.h +++ clang/lib/AST/Interp/Pointer.h @@ -33,6 +33,31 @@ /// /// This object can be allocated into interpreter stack frames. If pointing to /// a live block, it is a link in the chain of pointers pointing to the block. +/// +/// In the simplest form, a Pointer has a Block* (the pointee) and both Base +/// and Offset are 0, which means it will point to raw data. +/// +/// The Base field is used to access metadata about the data. For primitive +/// arrays, the Base is followed by an InitMap. In a variety of case, the +/// Base is preceded by an InlineDescriptor, which is used to track the +/// initialization state, among others. +/// +/// The Offset field is used to access the actual data. In other words, the +/// data the pointer decribes can be found at +/// Pointee->rawData() + Pointer.Offset. +/// +/// +/// Pointee Offset +/// │ │ +/// │ │ +/// ▼ ▼ +/// ┌───────┬────────────┬─────────┬────────────────────────────┐ +/// │ Block │ InlineDesc │ InitMap │ Actual Data │ +/// └───────┴────────────┴─────────┴────────────────────────────┘ +/// ▲ +/// │ +/// │ +/// Base class Pointer { private: static constexpr unsigned PastEndMark = (unsigned)-1; @@ -41,6 +66,7 @@ public: Pointer() {} Pointer(Block *B); + Pointer(Block *B, unsigned BaseAndOffset); Pointer(const Pointer &P); Pointer(Pointer &&P); ~Pointer(); @@ -276,12 +302,12 @@ /// Dereferences the pointer, if it's live. template T &deref() const { assert(isLive() && "Invalid pointer"); - return *reinterpret_cast(Pointee->data() + Offset); + return *reinterpret_cast(Pointee->rawData() + Offset); } /// Dereferences a primitive element. template T &elem(unsigned I) const { - return reinterpret_cast(Pointee->data())[I]; + return reinterpret_cast(Pointee->rawData())[I]; } /// Initializes a field. @@ -318,12 +344,13 @@ /// Returns a descriptor at a given offset. InlineDescriptor *getDescriptor(unsigned Offset) const { assert(Offset != 0 && "Not a nested pointer"); - return reinterpret_cast(Pointee->data() + Offset) - 1; + return reinterpret_cast(Pointee->rawData() + Offset) - + 1; } /// Returns a reference to the pointer which stores the initialization map. InitMap *&getInitMap() const { - return *reinterpret_cast(Pointee->data() + Base); + return *reinterpret_cast(Pointee->rawData() + Base); } /// The block the pointer is pointing to. Index: clang/lib/AST/Interp/Pointer.cpp =================================================================== --- clang/lib/AST/Interp/Pointer.cpp +++ clang/lib/AST/Interp/Pointer.cpp @@ -16,6 +16,9 @@ Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} +Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset) + : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} + Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {} Pointer::Pointer(Pointer &&P) Index: clang/lib/AST/Interp/Program.h =================================================================== --- clang/lib/AST/Interp/Program.h +++ clang/lib/AST/Interp/Program.h @@ -111,18 +111,20 @@ Record *getOrCreateRecord(const RecordDecl *RD); /// Creates a descriptor for a primitive type. - Descriptor *createDescriptor(const DeclTy &D, PrimType Type, - bool IsConst = false, - bool IsTemporary = false, - bool IsMutable = false) { - return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable); + Descriptor *createDescriptor( + const DeclTy &D, PrimType Type, + Descriptor::MetadataSize MDSize = Descriptor::MetadataSize{0}, + bool IsConst = false, bool IsTemporary = false, bool IsMutable = false) { + return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable); } /// Creates a descriptor for a composite type. - Descriptor *createDescriptor(const DeclTy &D, const Type *Ty, - bool IsConst = false, bool IsTemporary = false, - bool IsMutable = false, - const Expr *Init = nullptr); + Descriptor *createDescriptor( + const DeclTy &D, const Type *Ty, + Descriptor::MetadataSize MDSize = Descriptor::MetadataSize{0}, + // size_t MetadataSize = 0, + bool IsConst = false, bool IsTemporary = false, bool IsMutable = false, + const Expr *Init = nullptr); /// Context to manage declaration lifetimes. class DeclScope { Index: clang/lib/AST/Interp/Program.cpp =================================================================== --- clang/lib/AST/Interp/Program.cpp +++ clang/lib/AST/Interp/Program.cpp @@ -53,7 +53,8 @@ } // Create a descriptor for the string. - Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1, + Descriptor *Desc = allocateDescriptor(S, CharType, Descriptor::NoMetadata, + S->getLength() + 1, /*isConst=*/true, /*isTemporary=*/false, /*isMutable=*/false); @@ -184,9 +185,11 @@ const bool IsConst = Ty.isConstQualified(); const bool IsTemporary = D.dyn_cast(); if (auto T = Ctx.classify(Ty)) { - Desc = createDescriptor(D, *T, IsConst, IsTemporary); + Desc = + createDescriptor(D, *T, Descriptor::NoMetadata, IsConst, IsTemporary); } else { - Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary); + Desc = createDescriptor(D, Ty.getTypePtr(), Descriptor::NoMetadata, IsConst, + IsTemporary); } if (!Desc) return {}; @@ -230,7 +233,7 @@ auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * { if (!BR) return nullptr; - return allocateDescriptor(BD, BR, /*isConst=*/false, + return allocateDescriptor(BD, BR, Descriptor::NoMetadata, /*isConst=*/false, /*isTemporary=*/false, /*isMutable=*/false); }; @@ -280,11 +283,12 @@ const bool IsMutable = FD->isMutable(); Descriptor *Desc; if (llvm::Optional T = Ctx.classify(FT)) { - Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false, - IsMutable); - } else { - Desc = createDescriptor(FD, FT.getTypePtr(), IsConst, + Desc = createDescriptor(FD, *T, Descriptor::NoMetadata, IsConst, /*isTemporary=*/false, IsMutable); + } else { + Desc = + createDescriptor(FD, FT.getTypePtr(), Descriptor::NoMetadata, IsConst, + /*isTemporary=*/false, IsMutable); } if (!Desc) return nullptr; @@ -299,12 +303,14 @@ } Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, + Descriptor::MetadataSize MDSize, bool IsConst, bool IsTemporary, bool IsMutable, const Expr *Init) { // Classes and structures. if (auto *RT = Ty->getAs()) { if (auto *Record = getOrCreateRecord(RT->getDecl())) - return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary, + IsMutable); } // Arrays. @@ -319,21 +325,22 @@ if (std::numeric_limits::max() / ElemSize <= NumElems) { return {}; } - return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary, + return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary, IsMutable); } else { // Arrays of composites. In this case, the array is a list of pointers, // followed by the actual elements. Descriptor *ElemDesc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); + createDescriptor(D, ElemTy.getTypePtr(), Descriptor::NoMetadata, + IsConst, IsTemporary); if (!ElemDesc) return nullptr; InterpSize ElemSize = ElemDesc->getAllocSize() + sizeof(InlineDescriptor); if (std::numeric_limits::max() / ElemSize <= NumElems) return {}; - return allocateDescriptor(D, ElemDesc, NumElems, IsConst, IsTemporary, - IsMutable); + return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst, + IsTemporary, IsMutable); } } @@ -344,8 +351,8 @@ return allocateDescriptor(D, *T, IsTemporary, Descriptor::UnknownSize{}); } else { - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); + Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize, + IsConst, IsTemporary); if (!Desc) return nullptr; return allocateDescriptor(D, Desc, IsTemporary, @@ -357,13 +364,15 @@ // Atomic types. if (auto *AT = Ty->getAs()) { const Type *InnerTy = AT->getValueType().getTypePtr(); - return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable); + return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary, + IsMutable); } // Complex types - represented as arrays of elements. if (auto *CT = Ty->getAs()) { PrimType ElemTy = *Ctx.classify(CT->getElementType()); - return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary, + IsMutable); } return nullptr; Index: clang/test/AST/Interp/cxx20.cpp =================================================================== --- clang/test/AST/Interp/cxx20.cpp +++ clang/test/AST/Interp/cxx20.cpp @@ -52,33 +52,27 @@ } static_assert(pointerAssign2() == 12, ""); - constexpr int unInitLocal() { int a; - return a; // ref-note{{read of uninitialized object}} + return a; // ref-note {{read of uninitialized object}} \ + // expected-note {{read of object outside its lifetime}} + // FIXME: ^^^ Wrong diagnostic. } -static_assert(unInitLocal() == 0, ""); // expected-error {{not an integral constant expression}} \ - // ref-error {{not an integral constant expression}} \ - // ref-note {{in call to 'unInitLocal()'}} - -/// TODO: The example above is correctly rejected by the new constexpr -/// interpreter, but for the wrong reasons. We don't reject it because -/// it is an uninitialized read, we reject it simply because -/// the local variable does not have an initializer. -/// -/// The code below should be accepted but is also being rejected -/// right now. -#if 0 +static_assert(unInitLocal() == 0, ""); // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'unInitLocal()'}} \ + // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'unInitLocal()'}} \ + constexpr int initializedLocal() { int a; - int b; - a = 20; return a; } static_assert(initializedLocal() == 20); -/// Similar here, but the uninitialized local is passed as a function parameter. +#if 0 +// FIXME: This code should be rejected because we pass an uninitialized value +// as a function parameter. constexpr int inc(int a) { return a + 1; } constexpr int f() { int i; Index: clang/test/AST/Interp/literals.cpp =================================================================== --- clang/test/AST/Interp/literals.cpp +++ clang/test/AST/Interp/literals.cpp @@ -407,8 +407,7 @@ return 1; } static_assert(uninit(), ""); // ref-error {{not an integral constant expression}} \ - // ref-note {{in call to 'uninit()'}} \ - // expected-error {{not an integral constant expression}} + // ref-note {{in call to 'uninit()'}} constexpr int OverFlow() { // ref-error {{never produces a constant expression}} int a = INT_MAX; Index: clang/test/AST/Interp/loops.cpp =================================================================== --- clang/test/AST/Interp/loops.cpp +++ clang/test/AST/Interp/loops.cpp @@ -5,6 +5,7 @@ // ref-no-diagnostics // expected-no-diagnostics +// expected-cpp20-no-diagnostics namespace WhileLoop { constexpr int f() { @@ -165,8 +166,6 @@ static_assert(f5(true) == 8, ""); static_assert(f5(false) == 5, ""); - /// FIXME: This should be accepted in C++20 but is currently being rejected - /// because the variable declaration doesn't have an initializier. #if __cplusplus >= 202002L constexpr int f6() { int i; @@ -176,7 +175,7 @@ } while (true); return i; } - static_assert(f6() == 5, ""); // expected-cpp20-error {{not an integral constant}} + static_assert(f6() == 5, ""); #endif #if 0