Author: Timm Baeder Date: 2024-01-31T08:03:37+01:00 New Revision: 5bb99edcb6726e5dcc20d2236ef51b11c248acb9
URL: https://github.com/llvm/llvm-project/commit/5bb99edcb6726e5dcc20d2236ef51b11c248acb9 DIFF: https://github.com/llvm/llvm-project/commit/5bb99edcb6726e5dcc20d2236ef51b11c248acb9.diff LOG: [clang][Interp] Add inline descriptor to global variables (#72892) Some time ago, I did a similar patch for local variables. Initializing global variables can fail as well: ```c++ constexpr int a = 1/0; static_assert(a == 0); ``` ... would succeed in the new interpreter, because we never saved the fact that `a` has not been successfully initialized. Added: Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/ByteCodeExprGen.h clang/lib/AST/Interp/Descriptor.cpp clang/lib/AST/Interp/Descriptor.h clang/lib/AST/Interp/Interp.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/InterpFrame.cpp clang/lib/AST/Interp/Opcodes.td clang/lib/AST/Interp/Pointer.cpp clang/lib/AST/Interp/Pointer.h clang/lib/AST/Interp/Program.cpp clang/test/AST/Interp/cxx11.cpp clang/test/AST/Interp/cxx17.cpp clang/test/AST/Interp/cxx23.cpp clang/test/AST/Interp/literals.cpp clang/unittests/AST/Interp/Descriptor.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index d4501cefb2131..8188d6f7f5c24 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -826,7 +826,7 @@ bool ByteCodeExprGen<Emitter>::visitArrayElemInit(unsigned ElemIndex, return false; if (!this->visitInitializer(Init)) return false; - return this->emitPopPtr(Init); + return this->emitInitPtrPop(Init); } template <class Emitter> @@ -854,13 +854,26 @@ bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) { return this->visitInitList(E->inits(), E); if (T->isArrayType()) { - // FIXME: Array fillers. unsigned ElementIndex = 0; for (const Expr *Init : E->inits()) { if (!this->visitArrayElemInit(ElementIndex, Init)) return false; ++ElementIndex; } + + // Expand the filler expression. + // FIXME: This should go away. + if (const Expr *Filler = E->getArrayFiller()) { + const ConstantArrayType *CAT = + Ctx.getASTContext().getAsConstantArrayType(E->getType()); + uint64_t NumElems = CAT->getSize().getZExtValue(); + + for (; ElementIndex != NumElems; ++ElementIndex) { + if (!this->visitArrayElemInit(ElementIndex, Filler)) + return false; + } + } + return true; } diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 1d5ae46e56346..893b75b028b6f 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -180,6 +180,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, if (!visitInitializer(Init)) return false; + if (!this->emitInitPtr(Init)) + return false; + return this->emitPopPtr(Init); } @@ -191,6 +194,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, if (!visitInitializer(Init)) return false; + if (!this->emitInitPtr(Init)) + return false; + return this->emitPopPtr(Init); } @@ -202,7 +208,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, if (!visitInitializer(I)) return false; - return this->emitPopPtr(I); + return this->emitInitPtrPop(I); } bool visitInitList(ArrayRef<const Expr *> Inits, const Expr *E); diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index b330e54baf335..5701cf0acf915 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -243,18 +243,19 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), MDSize(MD.value_or(0)), - AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), - CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), - MoveFn(getMoveArrayPrim(Type)) { + AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), + IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), + IsArray(true), CtorFn(getCtorArrayPrim(Type)), + DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { assert(Source && "Missing source"); } /// Primitive unknown-size arrays. -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, - UnknownSize) - : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0), - AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true), +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + bool IsTemporary, UnknownSize) + : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), + MDSize(MD.value_or(0)), + AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { @@ -275,12 +276,12 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, } /// Unknown-size arrays of composite elements. -Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, +Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, bool IsTemporary, UnknownSize) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(UnknownSizeMark), MDSize(0), - AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem), - IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), + Size(UnknownSizeMark), MDSize(MD.value_or(0)), + AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true), + IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index 580c200f90952..6cca9d5feeded 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -73,6 +73,10 @@ struct InlineDescriptor { unsigned IsFieldMutable : 1; const Descriptor *Desc; + + InlineDescriptor(const Descriptor *D) + : Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false), + IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {} }; /// Describes a memory block created by an allocation site. @@ -128,15 +132,16 @@ struct Descriptor final { 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); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize, + bool IsTemporary, UnknownSize); /// Allocates a descriptor for an array of composites. Descriptor(const DeclTy &D, const 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, const Descriptor *Elem, bool IsTemporary, - UnknownSize); + Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, + bool IsTemporary, UnknownSize); /// Allocates a descriptor for a record. Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst, diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 807b860f3565d..be9ed81af69ec 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -356,6 +356,23 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } +bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (Ptr.isInitialized()) + return true; + + assert(S.getLangOpts().CPlusPlus); + const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl()); + if ((!VD->hasConstantInitialization() && + VD->mightBeUsableInConstantExpressions(S.getCtx())) || + (S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 && + !VD->hasICEInitializer(S.getCtx()))) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD; + S.Note(VD->getLocation(), diag::note_declared_at); + } + return false; +} + bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!CheckLive(S, OpPC, Ptr, AK_Read)) return false; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 65c54ed9c89b6..875fe3f9870e5 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -88,6 +88,8 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr); bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK); +/// Check if a global variable is initialized. +bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a value can be stored in a block. bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr); @@ -1003,13 +1005,18 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { - const Block *B = S.P.getGlobal(I); - - if (!CheckConstant(S, OpPC, B->getDescriptor())) + const Pointer &Ptr = S.P.getPtrGlobal(I); + if (!CheckConstant(S, OpPC, Ptr.getFieldDesc())) return false; - if (B->isExtern()) + if (Ptr.isExtern()) return false; - S.Stk.push<T>(B->deref<T>()); + + // If a global variable is uninitialized, that means the initializer we've + // compiled for it wasn't a constant expression. Diagnose that. + if (!CheckGlobalInitialized(S, OpPC, Ptr)) + return false; + + S.Stk.push<T>(Ptr.deref<T>()); return true; } @@ -1029,7 +1036,9 @@ bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>(); + const Pointer &P = S.P.getGlobal(I); + P.deref<T>() = S.Stk.pop<T>(); + P.initialize(); return true; } @@ -1045,7 +1054,10 @@ bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I, APValue *Cached = Temp->getOrCreateValue(true); *Cached = APV; - S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>(); + const Pointer &P = S.P.getGlobal(I); + P.deref<T>() = S.Stk.pop<T>(); + P.initialize(); + return true; } @@ -1267,6 +1279,12 @@ inline bool InitPtrPop(InterpState &S, CodePtr OpPC) { return true; } +inline bool InitPtr(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.peek<Pointer>(); + Ptr.initialize(); + return true; +} + inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl, const Pointer &Ptr) { Pointer Base = Ptr; @@ -1323,7 +1341,7 @@ bool Store(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; - if (!Ptr.isRoot()) + if (Ptr.canBeInitialized()) Ptr.initialize(); Ptr.deref<T>() = Value; return true; @@ -1335,7 +1353,7 @@ bool StorePop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; - if (!Ptr.isRoot()) + if (Ptr.canBeInitialized()) Ptr.initialize(); Ptr.deref<T>() = Value; return true; @@ -1347,7 +1365,7 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; - if (!Ptr.isRoot()) + if (Ptr.canBeInitialized()) Ptr.initialize(); if (const auto *FD = Ptr.getField()) Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); @@ -1362,7 +1380,7 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; - if (!Ptr.isRoot()) + if (Ptr.canBeInitialized()) Ptr.initialize(); if (const auto *FD = Ptr.getField()) Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp index d460d7ea3710a..dd05dac1703fd 100644 --- a/clang/lib/AST/Interp/InterpFrame.cpp +++ b/clang/lib/AST/Interp/InterpFrame.cpp @@ -38,14 +38,7 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func, for (auto &Local : Scope.locals()) { Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); B->invokeCtor(); - InlineDescriptor *ID = localInlineDesc(Local.Offset); - ID->Desc = Local.Desc; - ID->IsActive = true; - ID->Offset = sizeof(InlineDescriptor); - ID->IsBase = false; - ID->IsFieldMutable = false; - ID->IsConst = false; - ID->IsInitialized = false; + new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc); } } } @@ -201,7 +194,7 @@ const FunctionDecl *InterpFrame::getCallee() const { Pointer InterpFrame::getLocalPointer(unsigned Offset) const { assert(Offset < Func->getFrameSize() && "Invalid local offset."); - return Pointer(localBlock(Offset), sizeof(InlineDescriptor)); + return Pointer(localBlock(Offset)); } Pointer InterpFrame::getParamPointer(unsigned Off) { diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 24747b6b98c16..1564457bb9984 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -317,9 +317,8 @@ def GetPtrBasePop : Opcode { let Args = [ArgUint32]; } -def InitPtrPop : Opcode { - let Args = []; -} +def InitPtrPop : Opcode; +def InitPtr : Opcode; def GetPtrDerivedPop : Opcode { let Args = [ArgUint32]; diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index 5af1d6d52e93e..316a7ed5fa1bb 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -19,7 +19,9 @@ using namespace clang; using namespace clang::interp; -Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} +Pointer::Pointer(Block *Pointee) + : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(), + Pointee->getDescriptor()->getMetadataSize()) {} Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset) : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h index 8ccaff41ded8d..7ae4617f28137 100644 --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -134,7 +134,8 @@ class Pointer { // Pointer to an array of base types - enter block. if (Base == RootPtrMark) - return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); + return Pointer(Pointee, sizeof(InlineDescriptor), + Offset == 0 ? Offset : PastEndMark); // Pointer is one past end - magic offset marks that. if (isOnePastEnd()) @@ -179,7 +180,7 @@ class Pointer { return *this; // If at base, point to an array of base types. - if (Base == 0) + if (Base == 0 || Base == sizeof(InlineDescriptor)) return Pointer(Pointee, RootPtrMark, 0); // Step into the containing array, if inside one. @@ -196,7 +197,10 @@ class Pointer { /// Checks if the pointer is live. bool isLive() const { return Pointee && !Pointee->IsDead; } /// Checks if the item is a field in an object. - bool isField() const { return Base != 0 && Base != RootPtrMark; } + bool isField() const { + return Base != 0 && Base != sizeof(InlineDescriptor) && + Base != RootPtrMark && getFieldDesc()->asDecl(); + } /// Accessor for information about the declaration site. const Descriptor *getDeclDesc() const { @@ -227,7 +231,7 @@ class Pointer { /// Accessors for information about the innermost field. const Descriptor *getFieldDesc() const { - if (Base == 0 || Base == RootPtrMark) + if (Base == 0 || Base == sizeof(InlineDescriptor) || Base == RootPtrMark) return getDeclDesc(); return getInlineDesc()->Desc; } @@ -284,6 +288,8 @@ class Pointer { bool isRoot() const { return (Base == 0 || Base == RootPtrMark) && Offset == 0; } + /// If this pointer has an InlineDescriptor we can use to initialize. + bool canBeInitialized() const { return Pointee && Base > 0; } /// Returns the record descriptor of a class. const Record *getRecord() const { return getFieldDesc()->ElemRecord; } @@ -315,12 +321,16 @@ class Pointer { /// Checks if the field is mutable. bool isMutable() const { - return Base != 0 && getInlineDesc()->IsFieldMutable; + return Base != 0 && Base != sizeof(InlineDescriptor) && + getInlineDesc()->IsFieldMutable; } /// Checks if an object was initialized. bool isInitialized() const; /// Checks if the object is active. - bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } + bool isActive() const { + return Base == 0 || Base == sizeof(InlineDescriptor) || + getInlineDesc()->IsActive; + } /// Checks if a structure is a base class. bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } /// Checks if the pointer pointers to a dummy value. @@ -328,7 +338,9 @@ class Pointer { /// Checks if an object or a subfield is mutable. bool isConst() const { - return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; + return (Base == 0 || Base == sizeof(InlineDescriptor)) + ? getDeclDesc()->IsConst + : getInlineDesc()->IsConst; } /// Returns the declaration ID. @@ -353,7 +365,7 @@ class Pointer { return 1; // narrow()ed element in a composite array. - if (Base > 0 && Base == Offset) + if (Base > sizeof(InlineDescriptor) && Base == Offset) return 0; if (auto ElemSize = elemSize()) diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp index 1daefab4dcdac..b2b478af2e731 100644 --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -54,11 +54,11 @@ unsigned Program::createGlobalString(const StringLiteral *S) { } // Create a descriptor for the string. - Descriptor *Desc = - allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1, - /*isConst=*/true, - /*isTemporary=*/false, - /*isMutable=*/false); + Descriptor *Desc = allocateDescriptor(S, CharType, Descriptor::InlineDescMD, + S->getLength() + 1, + /*isConst=*/true, + /*isTemporary=*/false, + /*isMutable=*/false); // Allocate storage for the string. // The byte length does not include the null terminator. @@ -67,6 +67,8 @@ unsigned Program::createGlobalString(const StringLiteral *S) { auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, /*isExtern=*/false); G->block()->invokeCtor(); + + new (G->block()->rawData()) InlineDescriptor(Desc); Globals.push_back(G); // Construct the string in storage. @@ -78,16 +80,19 @@ unsigned Program::createGlobalString(const StringLiteral *S) { case PT_Sint8: { using T = PrimConv<PT_Sint8>::T; Field.deref<T>() = T::from(CodePoint, BitWidth); + Field.initialize(); break; } case PT_Uint16: { using T = PrimConv<PT_Uint16>::T; Field.deref<T>() = T::from(CodePoint, BitWidth); + Field.initialize(); break; } case PT_Uint32: { using T = PrimConv<PT_Uint32>::T; Field.deref<T>() = T::from(CodePoint, BitWidth); + Field.initialize(); break; } default: @@ -190,12 +195,13 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, Descriptor *Desc; const bool IsConst = Ty.isConstQualified(); const bool IsTemporary = D.dyn_cast<const Expr *>(); - if (auto T = Ctx.classify(Ty)) { - Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary); - } else { - Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst, - IsTemporary); - } + if (std::optional<PrimType> T = Ctx.classify(Ty)) + Desc = + createDescriptor(D, *T, Descriptor::InlineDescMD, IsConst, IsTemporary); + else + Desc = createDescriptor(D, Ty.getTypePtr(), Descriptor::InlineDescMD, + IsConst, IsTemporary); + if (!Desc) return std::nullopt; @@ -206,6 +212,8 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, Global(getCurrentDecl(), Desc, IsStatic, IsExtern); G->block()->invokeCtor(); + // Initialize InlineDescriptor fields. + new (G->block()->rawData()) InlineDescriptor(Desc); Globals.push_back(G); return I; @@ -339,7 +347,7 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, // Arrays of composites. In this case, the array is a list of pointers, // followed by the actual elements. const Descriptor *ElemDesc = createDescriptor( - D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary); + D, ElemTy.getTypePtr(), MDSize, IsConst, IsTemporary); if (!ElemDesc) return nullptr; unsigned ElemSize = @@ -355,14 +363,14 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, // is forbidden on pointers to such objects. if (isa<IncompleteArrayType>(ArrayType)) { if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { - return allocateDescriptor(D, *T, IsTemporary, + return allocateDescriptor(D, *T, MDSize, IsTemporary, Descriptor::UnknownSize{}); } else { const Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize, IsConst, IsTemporary); if (!Desc) return nullptr; - return allocateDescriptor(D, Desc, IsTemporary, + return allocateDescriptor(D, Desc, MDSize, IsTemporary, Descriptor::UnknownSize{}); } } diff --git a/clang/test/AST/Interp/cxx11.cpp b/clang/test/AST/Interp/cxx11.cpp index 81e293fec1750..0a1e0f3fd28e9 100644 --- a/clang/test/AST/Interp/cxx11.cpp +++ b/clang/test/AST/Interp/cxx11.cpp @@ -1,8 +1,6 @@ // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++11 %s // RUN: %clang_cc1 -verify=both,ref -std=c++11 %s -// expected-no-diagnostics - namespace IntOrEnum { const int k = 0; const int &p = k; @@ -17,8 +15,10 @@ template struct C<cval>; /// FIXME: This example does not get properly diagnosed in the new interpreter. extern const int recurse1; -const int recurse2 = recurse1; // ref-note {{here}} +const int recurse2 = recurse1; // both-note {{here}} const int recurse1 = 1; int array1[recurse1]; -int array2[recurse2]; // ref-warning 2{{variable length array}} \ - // ref-note {{initializer of 'recurse2' is not a constant expression}} +int array2[recurse2]; // both-warning {{variable length arrays in C++}} \ + // both-note {{initializer of 'recurse2' is not a constant expression}} \ + // expected-error {{variable length array declaration not allowed at file scope}} \ + // ref-warning {{variable length array folded to constant array as an extension}} diff --git a/clang/test/AST/Interp/cxx17.cpp b/clang/test/AST/Interp/cxx17.cpp index e1f578a4418d9..76d985eb22e17 100644 --- a/clang/test/AST/Interp/cxx17.cpp +++ b/clang/test/AST/Interp/cxx17.cpp @@ -1,9 +1,6 @@ // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify %s // RUN: %clang_cc1 -std=c++17 -verify=ref %s -// ref-no-diagnostics -// expected-no-diagnostics - struct F { int a; int b;}; constexpr F getF() { return {12, 3}; @@ -83,3 +80,23 @@ constexpr int b() { return a[0] + a[1]; } static_assert(b() == 11); + +/// The diagnostics between the two interpreters are diff erent here. +struct S { int a; }; +constexpr S getS() { // expected-error {{constexpr function never produces a constant expression}} \\ + // ref-error {{constexpr function never produces a constant expression}} + (void)(1/0); // expected-note 2{{division by zero}} \ + // expected-warning {{division by zero}} \ + // ref-note 2{{division by zero}} \ + // ref-warning {{division by zero}} + return S{12}; +} +constexpr S s = getS(); // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'getS()'}} \ + // ref-error {{must be initialized by a constant expression}} \\ + // ref-note {{in call to 'getS()'}} \ + // ref-note {{declared here}} +static_assert(s.a == 12, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{read of uninitialized object}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{initializer of 's' is not a constant expression}} diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp index 133ab10023df0..a50a9b7183699 100644 --- a/clang/test/AST/Interp/cxx23.cpp +++ b/clang/test/AST/Interp/cxx23.cpp @@ -3,25 +3,35 @@ // RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20 %s -fexperimental-new-constant-interpreter // RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23 %s -fexperimental-new-constant-interpreter - /// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics. constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \ - // ref23-error {{constexpr function never produces a constant expression}} + // ref23-error {{constexpr function never produces a constant expression}} \ + // expected20-error {{constexpr function never produces a constant expression}} \ + // expected23-error {{constexpr function never produces a constant expression}} static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \ // ref20-warning {{is a C++23 extension}} \ // ref23-note {{control flows through the definition of a static variable}} \ - // expected20-warning {{is a C++23 extension}} + // expected20-warning {{is a C++23 extension}} \ + // expected20-note {{declared here}} \ + // expected23-note {{declared here}} - return m; + return m; // expected20-note {{initializer of 'm' is not a constant expression}} \ + // expected23-note {{initializer of 'm' is not a constant expression}} } constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \ - // ref23-error {{constexpr function never produces a constant expression}} + // ref23-error {{constexpr function never produces a constant expression}} \ + // expected20-error {{constexpr function never produces a constant expression}} \ + // expected23-error {{constexpr function never produces a constant expression}} thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \ // ref20-warning {{is a C++23 extension}} \ // ref23-note {{control flows through the definition of a thread_local variable}} \ - // expected20-warning {{is a C++23 extension}} - return m; + // expected20-warning {{is a C++23 extension}} \ + // expected20-note {{declared here}} \ + // expected23-note {{declared here}} + return m; // expected20-note {{initializer of 'm' is not a constant expression}} \ + // expected23-note {{initializer of 'm' is not a constant expression}} + } constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \ diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index 77b75151a1255..c88e7c1214807 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -35,6 +35,23 @@ static_assert(one == 1, ""); constexpr bool b2 = bool(); static_assert(!b2, ""); +constexpr int Failed1 = 1 / 0; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{division by zero}} \ + // expected-note {{declared here}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{division by zero}} \ + // ref-note {{declared here}} +constexpr int Failed2 = Failed1 + 1; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{declared here}} \ + // expected-note {{initializer of 'Failed1' is not a constant expression}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{declared here}} \ + // ref-note {{initializer of 'Failed1' is not a constant expression}} +static_assert(Failed2 == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{initializer of 'Failed2' is not a constant expression}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{initializer of 'Failed2' is not a constant expression}} + namespace ScalarTypes { constexpr int ScalarInitInt = int(); static_assert(ScalarInitInt == 0, ""); diff --git a/clang/unittests/AST/Interp/Descriptor.cpp b/clang/unittests/AST/Interp/Descriptor.cpp index fb1690a970618..4ea0fbc285a98 100644 --- a/clang/unittests/AST/Interp/Descriptor.cpp +++ b/clang/unittests/AST/Interp/Descriptor.cpp @@ -52,7 +52,7 @@ TEST(Descriptor, Primitives) { ASSERT_FALSE(GlobalDesc->asRecordDecl()); // Still true because this is a global variable. - ASSERT_TRUE(GlobalDesc->getMetadataSize() == 0); + ASSERT_TRUE(GlobalDesc->getMetadataSize() == sizeof(InlineDescriptor)); ASSERT_FALSE(GlobalDesc->isPrimitiveArray()); ASSERT_FALSE(GlobalDesc->isCompositeArray()); ASSERT_FALSE(GlobalDesc->isZeroSizeArray()); @@ -114,8 +114,8 @@ TEST(Descriptor, Primitives) { ASSERT_TRUE(F4->Desc->ElemDesc->isPrimitiveArray()); // Check pointer stuff. - // Global variables have no inline descriptor (yet). - ASSERT_TRUE(GlobalPtr.isRoot()); + // Global variables have an inline descriptor. + ASSERT_FALSE(GlobalPtr.isRoot()); ASSERT_TRUE(GlobalPtr.isLive()); ASSERT_FALSE(GlobalPtr.isZero()); ASSERT_FALSE(GlobalPtr.isField()); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits