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?= <[email protected]>
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@-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}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to