From 1203ec58f331b7a0753be6bbaa4a506d1344d7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Tue, 5 Aug 2025 07:09:45 +0200 Subject: [PATCH] [clang][bytecode] Call CheckLocalLoad in GetLocal I forgot to call this here as well. It was only used in the EvalEmitter implementation of the function. Also fix a problem where we didn't diagnose out-of-lifetime reads here. --- clang/lib/AST/ByteCode/Interp.cpp | 2 ++ clang/lib/AST/ByteCode/Interp.h | 2 +- clang/test/AST/ByteCode/cxx2a.cpp | 38 +++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index f2366f674f45b..eb4e4800d44ae 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -752,6 +752,8 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { // Similarly, for local loads. bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckLifetime(S, OpPC, Ptr, AK_Read)) + return false; if (!CheckInitialized(S, OpPC, Ptr, AK_Read)) return false; if (!CheckVolatile(S, OpPC, Ptr, AK_Read)) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 61e776965a006..8a28106c0583a 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1352,7 +1352,7 @@ inline bool ConstFloat(InterpState &S, CodePtr OpPC, const Floating &F) { template ::T> bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Ptr = S.Current->getLocalPointer(I); - if (!CheckLoad(S, OpPC, Ptr)) + if (!CheckLocalLoad(S, OpPC, Ptr)) return false; S.Stk.push(Ptr.deref()); return true; diff --git a/clang/test/AST/ByteCode/cxx2a.cpp b/clang/test/AST/ByteCode/cxx2a.cpp index d9541aca225b9..ac2f9883d49b6 100644 --- a/clang/test/AST/ByteCode/cxx2a.cpp +++ b/clang/test/AST/ByteCode/cxx2a.cpp @@ -1,6 +1,25 @@ // RUN: %clang_cc1 -std=c++2a -fsyntax-only -fcxx-exceptions -verify=ref,both %s // RUN: %clang_cc1 -std=c++2a -fsyntax-only -fcxx-exceptions -verify=expected,both %s -fexperimental-new-constant-interpreter + +namespace std { + struct type_info; + struct destroying_delete_t { + explicit destroying_delete_t() = default; + } inline constexpr destroying_delete{}; + struct nothrow_t { + explicit nothrow_t() = default; + } inline constexpr nothrow{}; + using size_t = decltype(sizeof(0)); + enum class align_val_t : size_t {}; +}; + +constexpr void *operator new(std::size_t, void *p) { return p; } +namespace std { + template constexpr T *construct(T *p) { return new (p) T; } + template constexpr void destroy(T *p) { p->~T(); } +} + template struct S { S() requires (N==1) = default; @@ -187,3 +206,22 @@ namespace PureVirtual { struct PureVirtualCall : Abstract { void f(); }; // both-note {{in call to 'Abstract}} constexpr PureVirtualCall pure_virtual_call; // both-error {{constant expression}} both-note {{in call to 'PureVirtualCall}} } + +namespace Dtor { + constexpr bool pseudo(bool read, bool recreate) { + using T = bool; + bool b = false; // both-note {{lifetime has already ended}} + // This evaluates the store to 'b'... + (b = true).~T(); + // ... and ends the lifetime of the object. + return (read + ? b // both-note {{read of object outside its lifetime}} + : true) + + (recreate + ? (std::construct(&b), true) + : true); + } + static_assert(pseudo(false, false)); // both-error {{constant expression}} both-note {{in call}} + static_assert(pseudo(true, false)); // both-error {{constant expression}} both-note {{in call}} + static_assert(pseudo(false, true)); +}