[clang] [clang][Interp] Add inline descriptor to global variables (PR #72892)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 15 02:25:10 PST 2024
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/72892 at 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?= <tbaeder at 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?= <tbaeder at 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?= <tbaeder at 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());
More information about the cfe-commits
mailing list