[clang] [clang][Interp] Add inline descriptor to global variables (PR #72892)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 20 09:07:09 PST 2023
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/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.
(Side note: The changes in `Pointer.h` make me want to refactor `Pointer::Base` to simply be relative to `Pointee->data()` and not `Pointee->rawData()`. That way, the 'root' value would always be `0` and not `0 || sizeof(InlineDescriptor)`.)
>From b5360dd44bd5d8f10287e0a3641b0846e051a5c4 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] [clang][Interp] Add inline descriptor to global variables
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 15 ++++++-
clang/lib/AST/Interp/Descriptor.cpp | 27 ++++++------
clang/lib/AST/Interp/Descriptor.h | 6 ++-
clang/lib/AST/Interp/Interp.cpp | 16 +++++++
clang/lib/AST/Interp/Interp.h | 23 +++++++---
clang/lib/AST/Interp/Pointer.cpp | 4 +-
clang/lib/AST/Interp/Pointer.h | 30 +++++++++----
clang/lib/AST/Interp/Program.cpp | 56 +++++++++++++++++-------
clang/test/AST/Interp/cxx17.cpp | 23 ++++++++--
clang/test/AST/Interp/cxx23.cpp | 27 +++++++-----
clang/test/AST/Interp/literals.cpp | 17 +++++++
11 files changed, 185 insertions(+), 59 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 5dc1f9dfb10ff32..180749a75ebd22e 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -662,13 +662,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 59a952135a2d809..7330295132618ed 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, Descriptor *Elem, bool IsTemporary,
- UnknownSize)
+Descriptor::Descriptor(const DeclTy &D, 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 8135f3d12f7035c..b0ffc21b7ea960f 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -128,14 +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, Descriptor *Elem, bool IsTemporary, UnknownSize);
+ Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+ bool IsTemporary, UnknownSize);
/// Allocates a descriptor for a record.
Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 13b77e9a87725c7..69d6504696311df 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -289,6 +289,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 (!CheckDummy(S, OpPC, Ptr))
return false;
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 4f7778bdd2ff333..3a665c12d663b25 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -86,6 +86,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);
@@ -1004,10 +1006,16 @@ 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 (B->isExtern())
+ const Pointer &Ptr = S.P.getPtrGlobal(I);
+ 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;
}
@@ -1019,7 +1027,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;
}
@@ -1035,7 +1045,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 52e13398163ecf7..fbcc358f9ae9348 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;
@@ -338,8 +364,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
} else {
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
- Descriptor *ElemDesc = createDescriptor(
- D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
+ Descriptor *ElemDesc = createDescriptor(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 {
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 e284a66626fb331..8f9ffbe5d100b9d 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -3,28 +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
-
-// expected23-no-diagnostics
-
-
/// 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 85adfe551384d27..6749dbc1abb314c 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, "");
More information about the cfe-commits
mailing list