[clang] [clang][bytecode] Handle null data() ptr in static_assert messages (PR #206413)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 29 00:19:34 PDT 2026
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/206413
Diagnose the read from a null pointer.
>From f422af968f83b2f0885984d34440e0a72d99b93c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 29 Jun 2026 09:13:51 +0200
Subject: [PATCH] [clang][bytecode] Handle null data() ptr in static_assert
messages
---
clang/lib/AST/ByteCode/Context.cpp | 21 ++++++++++++++++-----
clang/lib/AST/ByteCode/EvalEmitter.cpp | 2 +-
clang/lib/AST/ByteCode/EvalEmitter.h | 3 ++-
clang/test/SemaCXX/static-assert-cxx26.cpp | 10 ++++++++++
4 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 40cf29efcfb4f..f7d142c7ca226 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -194,13 +194,20 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
return false;
uint64_t Size = SizeValue.getInt().getZExtValue();
- auto PtrRes = C.interpretAsPointer(PtrExpr, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(PtrExpr, [&](InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr) {
if (Size == 0) {
if constexpr (std::is_same_v<ResultT, APValue>)
Result = APValue(APValue::UninitArray{}, 0, 0);
return true;
}
+ if (Ptr.isZero()) {
+ S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null)
+ << AK_Read;
+ return false;
+ }
+
if (!Ptr.isLive() || !Ptr.getFieldDesc()->isPrimitiveArray())
return false;
@@ -209,7 +216,8 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
return false;
if (Size > Ptr.getNumElems()) {
- Parent.FFDiag(SizeExpr, diag::note_constexpr_access_past_end) << AK_Read;
+ S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end)
+ << AK_Read;
Size = Ptr.getNumElems();
}
@@ -263,7 +271,8 @@ bool Context::evaluateString(State &Parent, const Expr *E,
assert(Stk.empty());
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr) {
if (!Ptr.isBlockPointer())
return false;
@@ -312,7 +321,8 @@ std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) {
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
std::optional<uint64_t> Result;
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr) {
if (!Ptr.isBlockPointer())
return false;
@@ -363,7 +373,8 @@ Context::tryEvaluateObjectSize(State &Parent, const Expr *E, unsigned Kind) {
std::optional<uint64_t> Result;
- auto PtrRes = C.interpretAsLValuePointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr) {
const Descriptor *DeclDesc = Ptr.getDeclDesc();
if (!DeclDesc)
return false;
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 83dac57353944..7c8cdc71a7519 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -233,7 +233,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
// If we're returning a raw pointer, call our callback.
if (this->PtrCB)
- return (*this->PtrCB)(Ptr);
+ return (*this->PtrCB)(S, OpPC, Ptr);
if (!EvalResult.checkDynamicAllocations(S, Ctx, Ptr, Info))
return false;
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index 6fd50da8cad76..10b4a168fbc7c 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -33,7 +33,8 @@ class EvalEmitter : public SourceMapper {
using LabelTy = uint32_t;
using AddrTy = uintptr_t;
using Local = Scope::Local;
- using PtrCallback = llvm::function_ref<bool(const Pointer &)>;
+ using PtrCallback =
+ llvm::function_ref<bool(InterpState &S, CodePtr OpPC, const Pointer &)>;
EvaluationResult interpretExpr(const Expr *E,
bool ConvertResultToRValue = false,
diff --git a/clang/test/SemaCXX/static-assert-cxx26.cpp b/clang/test/SemaCXX/static-assert-cxx26.cpp
index 5dbe2c37307bd..e9eb98dc72e04 100644
--- a/clang/test/SemaCXX/static-assert-cxx26.cpp
+++ b/clang/test/SemaCXX/static-assert-cxx26.cpp
@@ -416,3 +416,13 @@ static_assert(
// expected-note at -1 {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
);
}
+
+namespace DataPtrIsNull {
+ struct S {
+ constexpr int size() { return sizeof("foo"); }
+ constexpr char *data() { return 0; }
+ };
+ static_assert(false, S{}); // expected-error {{the message in a static assertion must be produced by a constant expression}} \
+ // expected-note {{read of dereferenced null pointer is not allowed in a constant expression}} \
+ // expected-error {{static assertion failed}}
+}
More information about the cfe-commits
mailing list