[clang] [llvm] [clang][bytecode] fix assertion failure on invalid init list (GH175432) (PR #180261)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 08:22:35 PST 2026
https://github.com/Serosh-commits updated https://github.com/llvm/llvm-project/pull/180261
>From 9e3a16f024b3d1160264c3eee87b979a763207e1 Mon Sep 17 00:00:00 2001
From: Serosh-commits <janmejayapanda400 at gmail.com>
Date: Fri, 13 Feb 2026 21:51:25 +0530
Subject: [PATCH] Fix crash when constexpr variables have invalid initializers
---
clang/lib/AST/ByteCode/Pointer.cpp | 104 +++++++++++++++++++----------
clang/lib/AST/ByteCode/Pointer.h | 22 +++++-
clang/lib/Sema/SemaDecl.cpp | 9 ++-
test-invalid-constexpr.cpp | 2 +
4 files changed, 98 insertions(+), 39 deletions(-)
create mode 100644 test-invalid-constexpr.cpp
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index fb9202c6d66c8..bef0f6cacadf9 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -385,14 +385,17 @@ size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const {
Pointer P = *this;
while (true) {
if (P.isVirtualBaseClass()) {
- Result += getInlineDesc()->Offset;
+ if (InlineDescriptor *ID = getInlineDesc())
+ Result += ID->Offset;
P = P.getBase();
continue;
}
if (P.isBaseClass()) {
- if (P.getRecord()->getNumVirtualBases() > 0)
- Result += P.getInlineDesc()->Offset;
+ if (P.getRecord()->getNumVirtualBases() > 0) {
+ if (InlineDescriptor *ID = P.getInlineDesc())
+ Result += ID->Offset;
+ }
P = P.getBase();
continue;
}
@@ -444,26 +447,31 @@ std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
return toAPValue(Ctx).getAsString(Ctx, getType());
}
+bool Pointer::isGlobalInitialized() const {
+ assert(isBlockPointer());
+ if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor)) {
+ if (Block *B = block()) {
+ const auto &GD = B->getBlockDesc<GlobalInlineDescriptor>();
+ return GD.InitState == GlobalInitState::Initialized;
+ }
+ }
+ return false;
+}
+
bool Pointer::isInitialized() const {
if (!isBlockPointer())
return true;
- if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
- Offset == BS.Base) {
- const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
- return GD.InitState == GlobalInitState::Initialized;
- }
-
assert(BS.Pointee && "Cannot check if null pointer was initialized");
const Descriptor *Desc = getFieldDesc();
assert(Desc);
if (Desc->isPrimitiveArray())
return isElementInitialized(getIndex());
- if (asBlockPointer().Base == 0)
- return true;
- // Field has its bit in an inline descriptor.
- return getInlineDesc()->IsInitialized;
+ if (InlineDescriptor *D = getInlineDesc())
+ return D->IsInitialized;
+
+ return isGlobalInitialized();
}
bool Pointer::isElementInitialized(unsigned Index) const {
@@ -476,11 +484,8 @@ bool Pointer::isElementInitialized(unsigned Index) const {
if (isStatic() && BS.Base == 0)
return true;
- if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
- Offset == BS.Base) {
- const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
- return GD.InitState == GlobalInitState::Initialized;
- }
+ if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor))
+ return isGlobalInitialized();
if (Desc->isPrimitiveArray()) {
InitMapPtr IM = getInitMap();
@@ -525,7 +530,8 @@ void Pointer::startLifetime() const {
return;
}
- getInlineDesc()->LifeState = Lifetime::Started;
+ if (InlineDescriptor *ID = getInlineDesc())
+ ID->LifeState = Lifetime::Started;
}
void Pointer::endLifetime() const {
@@ -545,7 +551,8 @@ void Pointer::endLifetime() const {
return;
}
- getInlineDesc()->LifeState = Lifetime::Ended;
+ if (InlineDescriptor *ID = getInlineDesc())
+ ID->LifeState = Lifetime::Ended;
}
void Pointer::initialize() const {
@@ -556,8 +563,10 @@ void Pointer::initialize() const {
if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
Offset == BS.Base) {
- auto &GD = BS.Pointee->getBlockDesc<GlobalInlineDescriptor>();
- GD.InitState = GlobalInitState::Initialized;
+ if (Block *B = block()) {
+ auto &GD = B->getBlockDesc<GlobalInlineDescriptor>();
+ GD.InitState = GlobalInitState::Initialized;
+ }
return;
}
@@ -571,7 +580,16 @@ void Pointer::initialize() const {
// Field has its bit in an inline descriptor.
assert(BS.Base != 0 && "Only composite fields can be initialised");
- getInlineDesc()->IsInitialized = true;
+ if (InlineDescriptor *D = getInlineDesc()) {
+ D->IsInitialized = true;
+ return;
+ }
+
+ assert(isRoot() && BS.Base == sizeof(GlobalInlineDescriptor));
+ if (Block *B = block()) {
+ auto &GD = B->getBlockDesc<GlobalInlineDescriptor>();
+ GD.InitState = GlobalInitState::Initialized;
+ }
}
void Pointer::initializeElement(unsigned Index) const {
@@ -611,8 +629,11 @@ bool Pointer::allElementsInitialized() const {
if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
Offset == BS.Base) {
- const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
- return GD.InitState == GlobalInitState::Initialized;
+ if (Block *B = block()) {
+ const auto &GD = B->getBlockDesc<GlobalInlineDescriptor>();
+ return GD.InitState == GlobalInitState::Initialized;
+ }
+ return false;
}
InitMapPtr IM = getInitMap();
@@ -628,8 +649,11 @@ bool Pointer::allElementsAlive() const {
if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
Offset == BS.Base) {
- const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
- return GD.InitState == GlobalInitState::Initialized;
+ if (Block *B = block()) {
+ const auto &GD = B->getBlockDesc<GlobalInlineDescriptor>();
+ return GD.InitState == GlobalInitState::Initialized;
+ }
+ return false;
}
InitMapPtr &IM = getInitMap();
@@ -642,17 +666,26 @@ void Pointer::activate() const {
if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor))
return;
- if (!getInlineDesc()->InUnion)
+
+ if (InlineDescriptor *ID = getInlineDesc()) {
+ if (!ID->InUnion)
+ return;
+ } else {
return;
+ }
std::function<void(Pointer &)> activate;
activate = [&activate](Pointer &P) -> void {
- P.getInlineDesc()->IsActive = true;
+ if (InlineDescriptor *ID = P.getInlineDesc())
+ ID->IsActive = true;
+
if (const Record *R = P.getRecord(); R && !R->isUnion()) {
for (const Record::Field &F : R->fields()) {
Pointer FieldPtr = P.atField(F.Offset);
- if (!FieldPtr.getInlineDesc()->IsActive)
- activate(FieldPtr);
+ if (InlineDescriptor *ID = FieldPtr.getInlineDesc()) {
+ if (!ID->IsActive)
+ activate(FieldPtr);
+ }
}
// FIXME: Bases?
}
@@ -660,13 +693,16 @@ void Pointer::activate() const {
std::function<void(Pointer &)> deactivate;
deactivate = [&deactivate](Pointer &P) -> void {
- P.getInlineDesc()->IsActive = false;
+ if (InlineDescriptor *ID = P.getInlineDesc())
+ ID->IsActive = false;
if (const Record *R = P.getRecord()) {
for (const Record::Field &F : R->fields()) {
Pointer FieldPtr = P.atField(F.Offset);
- if (FieldPtr.getInlineDesc()->IsActive)
- deactivate(FieldPtr);
+ if (InlineDescriptor *ID = FieldPtr.getInlineDesc()) {
+ if (ID->IsActive)
+ deactivate(FieldPtr);
+ }
}
// FIXME: Bases?
}
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 2515b2fe56ab9..fc2d815f33fac 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -246,7 +246,11 @@ class Pointer {
return Pointer(Pointee, BS.Base, BS.Base);
// Step into the containing array, if inside one.
- unsigned Next = BS.Base - getInlineDesc()->Offset;
+ InlineDescriptor *ID = getInlineDesc();
+ if (!ID)
+ return *this;
+
+ unsigned Next = BS.Base - ID->Offset;
const Descriptor *Desc =
(Next == Pointee->getDescriptor()->getMetadataSize())
? getDeclDesc()
@@ -315,7 +319,14 @@ class Pointer {
assert(Offset == PastEndMark && "cannot get base of a block");
return Pointer(BS.Pointee, BS.Base, 0);
}
- unsigned NewBase = BS.Base - getInlineDesc()->Offset;
+ if (isRoot())
+ return *this;
+
+ InlineDescriptor *ID = getInlineDesc();
+ if (!ID)
+ return *this;
+
+ unsigned NewBase = BS.Base - ID->Offset;
return Pointer(BS.Pointee, NewBase, NewBase);
}
/// Returns the parent array.
@@ -829,12 +840,17 @@ class Pointer {
/// Returns the embedded descriptor preceding a field.
InlineDescriptor *getInlineDesc() const {
assert(isBlockPointer());
- assert(BS.Base != sizeof(GlobalInlineDescriptor));
+ if (BS.Base == sizeof(GlobalInlineDescriptor))
+ return nullptr;
+
assert(BS.Base <= BS.Pointee->getSize());
assert(BS.Base >= sizeof(InlineDescriptor));
return getDescriptor(BS.Base);
}
+ /// Returns whether the pointer is a global root and is initialized.
+ bool isGlobalInitialized() const;
+
/// Returns a descriptor at a given offset.
InlineDescriptor *getDescriptor(unsigned Offset) const {
assert(Offset != 0 && "Not a nested pointer");
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3b2c93b9fe7b5..06fa33e5615e8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14337,7 +14337,9 @@ void Sema::ActOnInitializerError(Decl *D) {
BD->setInvalidDecl();
// Auto types are meaningless if we can't make sense of the initializer.
- if (VD->getType()->isUndeducedType()) {
+ // Similarly, constexpr variables require a valid constant initializer;
+ // if the initializer is erroneous, the variable is unusable.
+ if (VD->getType()->isUndeducedType() || VD->isConstexpr()) {
D->setInvalidDecl();
return;
}
@@ -14949,9 +14951,11 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
QualType baseType = Context.getBaseElementType(type);
bool HasConstInit = true;
- if (getLangOpts().C23 && var->isConstexpr() && !Init)
+ if (getLangOpts().C23 && var->isConstexpr() && !Init) {
Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init)
<< var;
+ var->setInvalidDecl();
+ }
// Check whether the initializer is sufficiently constant.
if ((getLangOpts().CPlusPlus || (getLangOpts().C23 && var->isConstexpr())) &&
@@ -15012,6 +15016,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
<< var << Init->getSourceRange();
for (unsigned I = 0, N = Notes.size(); I != N; ++I)
Diag(Notes[I].first, Notes[I].second);
+ var->setInvalidDecl();
} else if (GlobalStorage && var->hasAttr<ConstInitAttr>()) {
auto *Attr = var->getAttr<ConstInitAttr>();
Diag(var->getLocation(), diag::err_require_constant_init_failed)
diff --git a/test-invalid-constexpr.cpp b/test-invalid-constexpr.cpp
new file mode 100644
index 0000000000000..2dd10750e66ef
--- /dev/null
+++ b/test-invalid-constexpr.cpp
@@ -0,0 +1,2 @@
+constexpr const int *foo[][2] = { {nullptr, int}, };
+static_assert(foo[0][0] == nullptr, "");
More information about the cfe-commits
mailing list