[clang] [clang][bytecode] Fix uninitialized diagnostics (PR #209702)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 15 02:02:22 PDT 2026
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/209702
... if the pointer is also outside its lifetime. In that case, prefer to diagnose it as outside its lifetime.
>From d3d983275f9a23d81f12806052cfd56ed95172c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 15 Jul 2026 10:54:09 +0200
Subject: [PATCH] [clang][bytecode] Fix uninitialized diagnostics
... if the pointer is also outside its lifetime. In that case, prefer to
diagnose it as outside its lifetime.
---
clang/lib/AST/ByteCode/Interp.cpp | 24 +++++++++++++-----------
clang/lib/AST/ByteCode/Interp.h | 15 ++++++++-------
clang/lib/AST/ByteCode/Pointer.h | 18 +-----------------
clang/test/AST/ByteCode/cxx2a.cpp | 12 ++++++++++++
4 files changed, 34 insertions(+), 35 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 58bbf4aaf5f89..1310500ca871e 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -731,15 +731,16 @@ static bool CheckVolatile(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}
-bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK) {
assert(Ptr.isLive());
assert(!Ptr.isInitialized());
- return DiagnoseUninitialized(S, OpPC, Ptr.isExtern(), Ptr.block(), AK);
+ return diagnoseUninitialized(S, OpPC, Ptr.isExtern(), Ptr.block(),
+ Ptr.getLifetime(), AK);
}
-bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
- const Block *B, AccessKinds AK) {
+bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
+ const Block *B, Lifetime LT, AccessKinds AK) {
if (S.checkingPotentialConstantExpression()) {
// Extern and static member declarations might be initialized later.
if (Extern)
@@ -783,7 +784,8 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
if (!S.checkingPotentialConstantExpression()) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
- << AK << /*uninitialized=*/true << S.Current->getRange(OpPC);
+ << AK << /*uninitialized=*/(LT == Lifetime::Started)
+ << S.Current->getRange(OpPC);
noteValueLocation(S, B);
}
return false;
@@ -836,7 +838,7 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
if (!CheckConstant(S, OpPC, B->getDescriptor()))
return false;
if (Desc.InitState != GlobalInitState::Initialized)
- return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read);
+ return diagnoseUninitialized(S, OpPC, B->isExtern(), B);
if (!CheckTemporary(S, OpPC, B, AK_Read))
return false;
if (B->getDescriptor()->IsVolatile) {
@@ -858,10 +860,10 @@ bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
assert(!B->isExtern());
const auto &Desc = *reinterpret_cast<const InlineDescriptor *>(B->rawData());
const Descriptor *BlockDesc = B->getDescriptor();
+ if (!Desc.IsInitialized)
+ return diagnoseUninitialized(S, OpPC, /*Extern=*/false, B, Desc.LifeState);
if (!CheckLifetime(S, OpPC, Desc.LifeState, B, AK_Read))
return false;
- if (!Desc.IsInitialized)
- return DiagnoseUninitialized(S, OpPC, /*Extern=*/false, B, AK_Read);
if (BlockDesc->IsVolatile) {
if (!S.getLangOpts().CPlusPlus)
return Invalid(S, OpPC);
@@ -917,7 +919,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
if (!CheckActive(S, OpPC, Ptr, AK))
return false;
if (!Ptr.isInitialized())
- return DiagnoseUninitialized(S, OpPC, Ptr, AK);
+ return diagnoseUninitialized(S, OpPC, Ptr, AK);
if (!CheckLifetime(S, OpPC, Ptr, AK))
return false;
if (!CheckTemporary(S, OpPC, Ptr.block(), AK))
@@ -985,7 +987,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLifetime(S, OpPC, Ptr, AK_Read))
return false;
if (!Ptr.isInitialized())
- return DiagnoseUninitialized(S, OpPC, Ptr, AK_Read);
+ return diagnoseUninitialized(S, OpPC, Ptr, AK_Read);
if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Read))
return false;
if (!CheckMutable(S, OpPC, Ptr))
@@ -2122,7 +2124,7 @@ bool DynamicCast(InterpState &S, CodePtr OpPC, const Type *DestTypePtr,
return false;
if (!Ptr.isInitialized())
- return DiagnoseUninitialized(S, OpPC, Ptr, AK_Read);
+ return diagnoseUninitialized(S, OpPC, Ptr, AK_Read);
// Our given pointer, limited by the base that's currently being initialized,
// if any.
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 201679017c98b..b676e7ab43750 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -81,10 +81,11 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
-bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);
-bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
- const Block *B, AccessKinds AK);
+bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
+ const Block *B, Lifetime LT = Lifetime::Started,
+ AccessKinds AK = AK_Read);
/// Checks a direct load of a primitive value from a global or local variable.
bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B);
@@ -1764,7 +1765,7 @@ bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
const Block *B = S.P.getGlobal(I);
const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>();
if (Desc.InitState != GlobalInitState::Initialized)
- return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read);
+ return diagnoseUninitialized(S, OpPC, B->isExtern(), B);
S.Stk.push<T>(B->deref<T>());
return true;
@@ -2076,7 +2077,7 @@ inline bool GetRefGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>();
if (Desc.InitState != GlobalInitState::Initialized)
- return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read);
+ return diagnoseUninitialized(S, OpPC, B->isExtern(), B);
S.Stk.push<Pointer>(B->deref<Pointer>());
return true;
@@ -2693,7 +2694,7 @@ static inline bool IncPtr(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!Ptr.isInitialized())
- return DiagnoseUninitialized(S, OpPC, Ptr, AK_Increment);
+ return diagnoseUninitialized(S, OpPC, Ptr, AK_Increment);
return IncDecPtrHelper<ArithOp::Add>(S, OpPC, Ptr);
}
@@ -2702,7 +2703,7 @@ static inline bool DecPtr(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!Ptr.isInitialized())
- return DiagnoseUninitialized(S, OpPC, Ptr, AK_Decrement);
+ return diagnoseUninitialized(S, OpPC, Ptr, AK_Decrement);
return IncDecPtrHelper<ArithOp::Sub>(S, OpPC, Ptr);
}
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index dda5ac61ef126..417c28b46875a 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -957,23 +957,7 @@ class Pointer {
Lifetime getLifetime() const {
if (!isBlockPointer())
return Lifetime::Started;
- if (BS.Base < sizeof(InlineDescriptor))
- return Lifetime::Started;
-
- if (inArray() && !isArrayRoot()) {
- InitMapPtr &IM = getInitMap();
-
- if (!IM.hasInitMap()) {
- if (IM.allInitialized())
- return Lifetime::Started;
- return getArray().getLifetime();
- }
-
- return IM->isElementAlive(getIndex()) ? Lifetime::Started
- : Lifetime::Ended;
- }
-
- return getInlineDesc()->LifeState;
+ return view().getLifetime();
}
/// Start the lifetime of this pointer. This works for pointer with an
diff --git a/clang/test/AST/ByteCode/cxx2a.cpp b/clang/test/AST/ByteCode/cxx2a.cpp
index 2f2f2b95745b3..e2cf74aa4ec57 100644
--- a/clang/test/AST/ByteCode/cxx2a.cpp
+++ b/clang/test/AST/ByteCode/cxx2a.cpp
@@ -302,3 +302,15 @@ namespace PseudoDtorOnGlobal {
a.m.~T(); // both-note {{cannot modify an object that is visible outside}}
}
}
+
+namespace UninitializedAndLifetime {
+ struct A { int n; };
+ constexpr void use_after_destroy() {
+ A a; // both-note {{declared here}}
+ a.~A();
+ A b = a; // both-note {{in call}} \
+ // both-note {{read of object outside its lifetime}}
+ }
+ static_assert((use_after_destroy(), true)); // both-error {{not an integral constant expression}} \
+ // both-note {{in call}}
+}
More information about the cfe-commits
mailing list