Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/72...@github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/72892 >From a5b16989efd7deaef47fc6e35032f2abf5e3a9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Mon, 20 Nov 2023 11:53:40 +0100 Subject: [PATCH 1/3] [clang][Interp] Add inline descriptor to global variables --- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 15 ++++++- clang/lib/AST/Interp/Descriptor.cpp | 25 +++++------ clang/lib/AST/Interp/Descriptor.h | 7 +-- clang/lib/AST/Interp/Interp.cpp | 16 +++++++ clang/lib/AST/Interp/Interp.h | 26 +++++++++--- clang/lib/AST/Interp/Pointer.cpp | 4 +- clang/lib/AST/Interp/Pointer.h | 30 +++++++++---- clang/lib/AST/Interp/Program.cpp | 54 ++++++++++++++++++------ clang/test/AST/Interp/cxx17.cpp | 23 ++++++++-- clang/test/AST/Interp/cxx23.cpp | 24 ++++++++--- clang/test/AST/Interp/literals.cpp | 17 ++++++++ 11 files changed, 184 insertions(+), 57 deletions(-) diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index d6be9a306aeaf67..981e319a3080542 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -830,13 +830,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/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index b330e54baf335a7..5701cf0acf915dc 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 580c200f9095296..a69ff610ccf1f8e 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -128,15 +128,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 b95a52199846fa0..5aec12014f2025e 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -332,6 +332,22 @@ 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; + + const VarDecl *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl()); + if ((S.getLangOpts().CPlusPlus && !VD->hasConstantInitialization() && + VD->mightBeUsableInConstantExpressions(S.getCtx())) || + ((S.getLangOpts().CPlusPlus || 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 dbbc4c09ce42a18..f185005b9845fb9 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); @@ -1006,13 +1008,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 initialize 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; } @@ -1032,7 +1039,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; } @@ -1048,7 +1057,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; } diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index e979b99b0fdd0a0..88b945b7902cf22 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 a8f6e62fa76d356..f5e76b253931c8b 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; } @@ -282,7 +286,9 @@ class Pointer { bool isArrayElement() const { return inArray() && Base != Offset; } /// Pointer points directly to a block. bool isRoot() const { - return (Base == 0 || Base == RootPtrMark) && Offset == 0; + return (Base == 0 || Base == sizeof(InlineDescriptor) || + Base == RootPtrMark) && + Offset == 0; } /// Returns the record descriptor of a class. @@ -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 1daefab4dcdac1a..985fe05d05e53af 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,16 @@ unsigned Program::createGlobalString(const StringLiteral *S) { auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, /*isExtern=*/false); G->block()->invokeCtor(); + + InlineDescriptor *ID = + reinterpret_cast<InlineDescriptor *>(G->block()->rawData()); + ID->Offset = sizeof(InlineDescriptor); + ID->Desc = Desc; + ID->IsConst = true; + ID->IsInitialized = true; + ID->IsBase = false; + ID->IsActive = true; + ID->IsFieldMutable = false; Globals.push_back(G); // Construct the string in storage. @@ -78,16 +88,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 +203,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 +220,18 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, Global(getCurrentDecl(), Desc, IsStatic, IsExtern); G->block()->invokeCtor(); + // Initialize InlineDescriptor fields. + InlineDescriptor *ID = + reinterpret_cast<InlineDescriptor *>(G->block()->rawData()); + ID->Offset = sizeof(InlineDescriptor); + ID->Desc = Desc; + ID->IsConst = true; + ID->IsInitialized = true; + ID->IsInitialized = false; + ID->IsBase = false; + ID->IsActive = true; + ID->IsFieldMutable = false; + Globals.push_back(G); return I; @@ -339,7 +365,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 +381,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/cxx17.cpp b/clang/test/AST/Interp/cxx17.cpp index e1f578a4418d9fe..76d985eb22e178a 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 different 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 133ab10023df0e0..a50a9b7183699a2 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 77b75151a125598..c88e7c121480732 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, ""); >From 1cd490ff7842c2df6d142da439d0c1dbfee3f34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Tue, 21 Nov 2023 13:03:03 +0100 Subject: [PATCH 2/3] Use placement-new to default-initialize InlineDescriptors --- clang/lib/AST/Interp/Descriptor.h | 4 ++++ clang/lib/AST/Interp/InterpFrame.cpp | 11 ++--------- clang/lib/AST/Interp/Program.cpp | 22 ++-------------------- 3 files changed, 8 insertions(+), 29 deletions(-) diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index a69ff610ccf1f8e..6cca9d5feedede3 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. diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp index d460d7ea3710a88..dd05dac1703fd65 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/Program.cpp b/clang/lib/AST/Interp/Program.cpp index 985fe05d05e53af..b2b478af2e73116 100644 --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -68,15 +68,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) { /*isExtern=*/false); G->block()->invokeCtor(); - InlineDescriptor *ID = - reinterpret_cast<InlineDescriptor *>(G->block()->rawData()); - ID->Offset = sizeof(InlineDescriptor); - ID->Desc = Desc; - ID->IsConst = true; - ID->IsInitialized = true; - ID->IsBase = false; - ID->IsActive = true; - ID->IsFieldMutable = false; + new (G->block()->rawData()) InlineDescriptor(Desc); Globals.push_back(G); // Construct the string in storage. @@ -221,17 +213,7 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, G->block()->invokeCtor(); // Initialize InlineDescriptor fields. - InlineDescriptor *ID = - reinterpret_cast<InlineDescriptor *>(G->block()->rawData()); - ID->Offset = sizeof(InlineDescriptor); - ID->Desc = Desc; - ID->IsConst = true; - ID->IsInitialized = true; - ID->IsInitialized = false; - ID->IsBase = false; - ID->IsActive = true; - ID->IsFieldMutable = false; - + new (G->block()->rawData()) InlineDescriptor(Desc); Globals.push_back(G); return I; >From 1ec5cebd0c2fbff46a63820b97e47ba5b2c4ff4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Thu, 23 Nov 2023 11:18:35 +0100 Subject: [PATCH 3/3] Fix pointer initialization problems in tests --- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 2 +- clang/lib/AST/Interp/ByteCodeExprGen.h | 8 +++++++- clang/lib/AST/Interp/Interp.h | 14 ++++++++++---- clang/lib/AST/Interp/Opcodes.td | 5 ++--- clang/lib/AST/Interp/Pointer.h | 6 +++--- clang/unittests/AST/Interp/Descriptor.cpp | 6 +++--- 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 981e319a3080542..d855ae246adc4c2 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -802,7 +802,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> diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index bbb13e97e725692..1e32dec58b8ed81 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; + if ((Init->getType()->isArrayType() || Init->getType()->isRecordType()) && !this->emitCheckGlobalCtor(Init)) return false; @@ -206,7 +212,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/Interp.h b/clang/lib/AST/Interp/Interp.h index f185005b9845fb9..d685dac590f7b96 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1275,6 +1275,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; @@ -1331,7 +1337,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; @@ -1343,7 +1349,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; @@ -1355,7 +1361,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())); @@ -1370,7 +1376,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/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index e01b6b9eea7dbb4..0f01092aa18be20 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.h b/clang/lib/AST/Interp/Pointer.h index f5e76b253931c8b..3f8a9ac12020cce 100644 --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -286,10 +286,10 @@ class Pointer { bool isArrayElement() const { return inArray() && Base != Offset; } /// Pointer points directly to a block. bool isRoot() const { - return (Base == 0 || Base == sizeof(InlineDescriptor) || - Base == RootPtrMark) && - Offset == 0; + 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; } diff --git a/clang/unittests/AST/Interp/Descriptor.cpp b/clang/unittests/AST/Interp/Descriptor.cpp index fb1690a97061890..4ea0fbc285a98e5 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