[clang] [clang][bytecode] Handle null data() ptr in static_assert messages (PR #206413)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 29 00:20:11 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
Diagnose the read from a null pointer.
---
Full diff: https://github.com/llvm/llvm-project/pull/206413.diff
4 Files Affected:
- (modified) clang/lib/AST/ByteCode/Context.cpp (+16-5)
- (modified) clang/lib/AST/ByteCode/EvalEmitter.cpp (+1-1)
- (modified) clang/lib/AST/ByteCode/EvalEmitter.h (+2-1)
- (modified) clang/test/SemaCXX/static-assert-cxx26.cpp (+10)
``````````diff
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}}
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/206413
More information about the cfe-commits
mailing list