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
