From e1228695f3a8958457ad4cede2cf2fba1fea0248 Mon Sep 17 00:00:00 2001 From: Schrodinger ZHU Yifan Date: Sat, 28 Mar 2026 20:14:08 -0400 Subject: [PATCH] Add portable Clang safety annotations --- CMakeLists.txt | 65 +++++++++++++++++- src/snmalloc/backend/globalconfig.h | 46 ++++++------- src/snmalloc/backend_helpers/commonconfig.h | 5 +- src/snmalloc/ds_aal/flaglock.h | 16 ++--- src/snmalloc/ds_core/defines.h | 75 +++++++++++++++++++++ src/snmalloc/global/scopedalloc.h | 2 +- src/snmalloc/mem/pool.h | 32 +++++---- src/snmalloc/pal/pal_windows.h | 46 +++++++------ src/snmalloc/stl/gnu/array.h | 26 ++++--- src/snmalloc/stl/gnu/utility.h | 7 -- 10 files changed, 230 insertions(+), 90 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e21d6bd71..9c7c22b1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ option(SNMALLOC_ENABLE_DYNAMIC_LOADING "Build such that snmalloc can be dynamica option(SNMALLOC_ENABLE_WAIT_ON_ADDRESS "Use wait on address backoff strategy if it is available" ON) option(SNMALLOC_PTHREAD_FORK_PROTECTION "Guard against forking while allocator locks are held using pthread_atfork hooks" OFF) option(SNMALLOC_ENABLE_FUZZING "Enable fuzzing instrumentation tests" OFF) +option(SNMALLOC_ENABLE_LIFETIME_SAFETY "Enable Clang lifetime safety diagnostics when supported" ON) option(SNMALLOC_USE_SELF_VENDORED_STL "Avoid using system STL" OFF) # Options that apply only if we're not building the header-only library cmake_dependent_option(SNMALLOC_RUST_SUPPORT "Build static library for rust" OFF "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF) @@ -374,6 +375,61 @@ if(SNMALLOC_COMPILER_SUPPORT_MCX16) target_compile_options(snmalloc INTERFACE $<$:-mcx16>) endif() +set(SNMALLOC_LIFETIME_SAFETY_FLAGS "") +if(SNMALLOC_ENABLE_LIFETIME_SAFETY AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + check_cxx_compiler_flag( + "-Werror -Wlifetime-safety-permissive" + SNMALLOC_COMPILER_SUPPORT_LIFETIME_SAFETY_PERMISSIVE) + if(SNMALLOC_COMPILER_SUPPORT_LIFETIME_SAFETY_PERMISSIVE) + list(APPEND SNMALLOC_LIFETIME_SAFETY_FLAGS -Wlifetime-safety-permissive) + else() + check_cxx_compiler_flag( + "-Werror -Wexperimental-lifetime-safety-permissive" + SNMALLOC_COMPILER_SUPPORT_EXPERIMENTAL_LIFETIME_SAFETY_PERMISSIVE) + if(SNMALLOC_COMPILER_SUPPORT_EXPERIMENTAL_LIFETIME_SAFETY_PERMISSIVE) + list(APPEND SNMALLOC_LIFETIME_SAFETY_FLAGS + -Wexperimental-lifetime-safety-permissive) + endif() + endif() + + check_cxx_compiler_flag( + "-Werror -Wlifetime-safety-suggestions" + SNMALLOC_COMPILER_SUPPORT_LIFETIME_SAFETY_SUGGESTIONS) + if(SNMALLOC_COMPILER_SUPPORT_LIFETIME_SAFETY_SUGGESTIONS) + list(APPEND SNMALLOC_LIFETIME_SAFETY_FLAGS -Wlifetime-safety-suggestions) + else() + check_cxx_compiler_flag( + "-Werror -Wexperimental-lifetime-safety-suggestions" + SNMALLOC_COMPILER_SUPPORT_EXPERIMENTAL_LIFETIME_SAFETY_SUGGESTIONS) + if(SNMALLOC_COMPILER_SUPPORT_EXPERIMENTAL_LIFETIME_SAFETY_SUGGESTIONS) + list(APPEND SNMALLOC_LIFETIME_SAFETY_FLAGS + -Wexperimental-lifetime-safety-suggestions) + endif() + endif() + + check_cxx_compiler_flag( + "-flifetime-safety-inference" + SNMALLOC_COMPILER_SUPPORT_LIFETIME_SAFETY_INFERENCE) + if(SNMALLOC_COMPILER_SUPPORT_LIFETIME_SAFETY_INFERENCE) + list(APPEND SNMALLOC_LIFETIME_SAFETY_FLAGS -flifetime-safety-inference) + endif() + + check_cxx_compiler_flag( + "-fexperimental-lifetime-safety-tu-analysis" + SNMALLOC_COMPILER_SUPPORT_EXPERIMENTAL_LIFETIME_SAFETY_TU_ANALYSIS) + if(SNMALLOC_COMPILER_SUPPORT_EXPERIMENTAL_LIFETIME_SAFETY_TU_ANALYSIS) + list(APPEND SNMALLOC_LIFETIME_SAFETY_FLAGS + -fexperimental-lifetime-safety-tu-analysis) + endif() + + if(SNMALLOC_LIFETIME_SAFETY_FLAGS) + string(JOIN " " SNMALLOC_LIFETIME_SAFETY_FLAGS_MESSAGE + ${SNMALLOC_LIFETIME_SAFETY_FLAGS}) + message(STATUS + "snmalloc: Enabling lifetime safety flags: ${SNMALLOC_LIFETIME_SAFETY_FLAGS_MESSAGE}") + endif() +endif() + if (NOT SNMALLOC_HEADER_ONLY_LIBRARY AND SNMALLOC_IPO) check_ipo_supported(RESULT HAS_IPO) if (HAS_IPO) @@ -442,8 +498,13 @@ endif() function(add_warning_flags name) target_compile_options(${name} PRIVATE $<$:/Zi /W4 /WX /wd4127 /wd4324 /wd4201> - $<$,$>>:-fno-rtti -Wall -Wextra -Werror -Wundef> - $<$:-Wsign-conversion -Wconversion>) + $<$,$>>:-fno-rtti -Wall -Wextra -Werror -Wundef>) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(${name} PRIVATE -Wsign-conversion -Wconversion) + if(SNMALLOC_LIFETIME_SAFETY_FLAGS) + target_compile_options(${name} PRIVATE ${SNMALLOC_LIFETIME_SAFETY_FLAGS}) + endif() + endif() target_link_options(${name} PRIVATE $<$:-Wl,--no-undefined> $<$:$<${ci_or_debug}:/DEBUG>>) diff --git a/src/snmalloc/backend/globalconfig.h b/src/snmalloc/backend/globalconfig.h index 208210b65..492e3c890 100644 --- a/src/snmalloc/backend/globalconfig.h +++ b/src/snmalloc/backend/globalconfig.h @@ -95,42 +95,42 @@ namespace snmalloc // Performs initialisation for this configuration // of allocators. - SNMALLOC_SLOW_PATH static void ensure_init_slow() + SNMALLOC_SLOW_PATH static void + ensure_init_slow() SNMALLOC_EXCLUDES(initialisation_lock) { if (initialised) return; - with(initialisation_lock, [&]() { + FlagLock guard(initialisation_lock); #ifdef SNMALLOC_TRACING - message<1024>("Run init_impl"); + message<1024>("Run init_impl"); #endif - if (initialised) - return; + if (initialised) + return; - SecondaryAllocator::initialize(); + SecondaryAllocator::initialize(); - LocalEntropy entropy; - entropy.init(); - // Initialise key for remote deallocation lists - entropy.make_free_list_key(RemoteAllocator::key_global); - entropy.make_free_list_key(freelist::Object::key_root); + LocalEntropy entropy; + entropy.init(); + // Initialise key for remote deallocation lists + entropy.make_free_list_key(RemoteAllocator::key_global); + entropy.make_free_list_key(freelist::Object::key_root); - // Need to randomise pagemap location. If requested and not a - // StrictProvenance architecture, randomize its table's location within - // a significantly larger address space allocation. - static constexpr bool pagemap_randomize = - mitigations(random_pagemap) && !aal_supports; + // Need to randomise pagemap location. If requested and not a + // StrictProvenance architecture, randomize its table's location within + // a significantly larger address space allocation. + static constexpr bool pagemap_randomize = + mitigations(random_pagemap) && !aal_supports; - Pagemap::concretePagemap.template init(); + Pagemap::concretePagemap.template init(); - if constexpr (aal_supports) - { - Authmap::init(); - } + if constexpr (aal_supports) + { + Authmap::init(); + } - initialised.store(true, stl::memory_order_release); - }); + initialised.store(true, stl::memory_order_release); } public: diff --git a/src/snmalloc/backend_helpers/commonconfig.h b/src/snmalloc/backend_helpers/commonconfig.h index d7fc56340..879ebc910 100644 --- a/src/snmalloc/backend_helpers/commonconfig.h +++ b/src/snmalloc/backend_helpers/commonconfig.h @@ -79,7 +79,7 @@ namespace snmalloc return 1; } - static DataRef get(StorageType* base, size_t) + static DataRef get(SNMALLOC_LIFETIMEBOUND StorageType* base, size_t) { return *base; } @@ -96,7 +96,8 @@ namespace snmalloc return max_count; } - static DataRef get(StorageType* base, size_t index) + static DataRef + get(SNMALLOC_LIFETIMEBOUND StorageType* base, size_t index) { return base[index]; } diff --git a/src/snmalloc/ds_aal/flaglock.h b/src/snmalloc/ds_aal/flaglock.h index c55b80bb1..e8e045193 100644 --- a/src/snmalloc/ds_aal/flaglock.h +++ b/src/snmalloc/ds_aal/flaglock.h @@ -12,7 +12,7 @@ namespace snmalloc * Wrapper for stl::AtomicBool so that we can examine * the re-entrancy problem at debug mode. */ - struct DebugFlagWord + struct SNMALLOC_CAPABILITY("mutex") DebugFlagWord { using ThreadIdentity = size_t; @@ -80,7 +80,7 @@ namespace snmalloc * all member functions associated with ownership checkings * are empty so that they can be optimised out at Release mode. */ - struct ReleaseFlagWord + struct SNMALLOC_CAPABILITY("mutex") ReleaseFlagWord { stl::AtomicBool flag{false}; @@ -104,7 +104,7 @@ namespace snmalloc using FlagWord = DebugFlagWord; #endif - class FlagLock + class SNMALLOC_SCOPED_CAPABILITY FlagLock { private: FlagWord& lock; @@ -114,7 +114,7 @@ namespace snmalloc PreventFork pf{}; public: - FlagLock(FlagWord& lock) : lock(lock) + FlagLock(FlagWord& lock) SNMALLOC_ACQUIRE(lock) : lock(lock) { while ( SNMALLOC_UNLIKELY(lock.flag.exchange(true, stl::memory_order_acquire))) @@ -133,17 +133,11 @@ namespace snmalloc lock.set_owner(); } - ~FlagLock() + ~FlagLock() SNMALLOC_RELEASE() { lock.clear_owner(); lock.flag.store(false, stl::memory_order_release); } }; - template - inline void with(FlagWord& lock, F&& f) - { - FlagLock l(lock); - f(); - } } // namespace snmalloc diff --git a/src/snmalloc/ds_core/defines.h b/src/snmalloc/ds_core/defines.h index 8c83210ce..c53790713 100644 --- a/src/snmalloc/ds_core/defines.h +++ b/src/snmalloc/ds_core/defines.h @@ -111,10 +111,85 @@ # define SNMALLOC_FORCE_BSS #endif +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#ifndef __has_cpp_attribute +# define __has_cpp_attribute(x) 0 +#endif + #ifndef __has_builtin # define __has_builtin(x) 0 #endif +// Clang's thread-safety annotations are used opportunistically and compile +// away on other toolchains. +#if defined(__clang__) && __has_attribute(capability) && !defined(SWIG) +# define SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +# define SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(x) +#endif + +#define SNMALLOC_CAPABILITY(x) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) +#define SNMALLOC_REENTRANT_CAPABILITY \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(reentrant_capability) +#define SNMALLOC_SCOPED_CAPABILITY \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define SNMALLOC_GUARDED_BY(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(__VA_ARGS__)) +#define SNMALLOC_PT_GUARDED_BY(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(__VA_ARGS__)) +#define SNMALLOC_ACQUIRED_BEFORE(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) +#define SNMALLOC_ACQUIRED_AFTER(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) +#define SNMALLOC_REQUIRES(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) +#define SNMALLOC_REQUIRES_SHARED(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) +#define SNMALLOC_ACQUIRE(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) +#define SNMALLOC_ACQUIRE_SHARED(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) +#define SNMALLOC_RELEASE(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) +#define SNMALLOC_RELEASE_SHARED(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) +#define SNMALLOC_RELEASE_GENERIC(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__)) +#define SNMALLOC_TRY_ACQUIRE(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) +#define SNMALLOC_TRY_ACQUIRE_SHARED(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) +#define SNMALLOC_EXCLUDES(...) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) +#define SNMALLOC_ASSERT_CAPABILITY(x) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) +#define SNMALLOC_ASSERT_SHARED_CAPABILITY(x) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) +#define SNMALLOC_RETURN_CAPABILITY(x) \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +#define SNMALLOC_NO_THREAD_SAFETY_ANALYSIS \ + SNMALLOC_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +// Clang lifetime annotations are also opportunistic and erase cleanly on +// older or non-Clang toolchains. +#if __has_cpp_attribute(clang::lifetimebound) +# define SNMALLOC_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute(_Clang::__lifetimebound__) +# define SNMALLOC_LIFETIMEBOUND [[_Clang::__lifetimebound__]] +#else +# define SNMALLOC_LIFETIMEBOUND +#endif + +#if __has_cpp_attribute(clang::noescape) +# define SNMALLOC_NOESCAPE [[clang::noescape]] +#else +# define SNMALLOC_NOESCAPE +#endif + namespace snmalloc { #ifdef NDEBUG diff --git a/src/snmalloc/global/scopedalloc.h b/src/snmalloc/global/scopedalloc.h index 460f0c779..9c1f6b878 100644 --- a/src/snmalloc/global/scopedalloc.h +++ b/src/snmalloc/global/scopedalloc.h @@ -72,7 +72,7 @@ namespace snmalloc * Arrow operator, allows methods exposed by `Alloc` to be called on the * wrapper. */ - SAlloc* operator->() + SAlloc* operator->() SNMALLOC_LIFETIMEBOUND { return alloc; } diff --git a/src/snmalloc/mem/pool.h b/src/snmalloc/mem/pool.h index 2a94ec440..63d9755e5 100644 --- a/src/snmalloc/mem/pool.h +++ b/src/snmalloc/mem/pool.h @@ -25,12 +25,13 @@ namespace snmalloc friend class Pool; private: + FlagWord lock{}; + // Queue of elements in not currently in use // Must hold lock to modify - capptr::Alloc front{nullptr}; - capptr::Alloc back{nullptr}; + capptr::Alloc front SNMALLOC_GUARDED_BY(lock){nullptr}; + capptr::Alloc back SNMALLOC_GUARDED_BY(lock){nullptr}; - FlagWord lock{}; capptr::Alloc list{nullptr}; public: @@ -100,7 +101,8 @@ namespace snmalloc PoolState& pool = get_state(); T* result{nullptr}; - with(pool.lock, [&]() { + { + FlagLock guard(pool.lock); if (pool.front != nullptr) { auto p = pool.front; @@ -113,19 +115,20 @@ namespace snmalloc p->set_in_use(); result = p.unsafe_ptr(); } - }); + } if (result != nullptr) return result; auto p = ConstructT::make(); - with(pool.lock, [&]() { + { + FlagLock guard(pool.lock); p->list_next = pool.list; pool.list = p; p->set_in_use(); - }); + } return p.unsafe_ptr(); } @@ -150,11 +153,12 @@ namespace snmalloc if (p == nullptr) { T* result; - with(pool.lock, [&]() { + { + FlagLock guard(pool.lock); result = pool.front.unsafe_ptr(); pool.front = nullptr; pool.back = nullptr; - }); + } return result; } @@ -170,7 +174,8 @@ namespace snmalloc { PoolState& pool = get_state(); last->next = nullptr; - with(pool.lock, [&]() { + { + FlagLock guard(pool.lock); if (pool.front == nullptr) { pool.front = capptr::Alloc::unsafe_from(first); @@ -181,7 +186,7 @@ namespace snmalloc } pool.back = capptr::Alloc::unsafe_from(last); - }); + } } /** @@ -194,7 +199,8 @@ namespace snmalloc PoolState& pool = get_state(); last->next = nullptr; - with(pool.lock, [&]() { + { + FlagLock guard(pool.lock); if (pool.front == nullptr) { pool.back = capptr::Alloc::unsafe_from(last); @@ -204,7 +210,7 @@ namespace snmalloc last->next = pool.front; } pool.front = capptr::Alloc::unsafe_from(first); - }); + } } static T* iterate(T* p = nullptr) diff --git a/src/snmalloc/pal/pal_windows.h b/src/snmalloc/pal/pal_windows.h index 5199c0604..c48a878e5 100644 --- a/src/snmalloc/pal/pal_windows.h +++ b/src/snmalloc/pal/pal_windows.h @@ -71,13 +71,14 @@ namespace snmalloc low_memory_callbacks.notify_all(); } + // Lock for the reserved ranges. + inline static FlagWord reserved_ranges_lock{}; + // A list of reserved ranges, used to handle lazy commit on readonly pages. // We currently only need one, so haven't implemented a backup if the // initial 16 is insufficient. - inline static stl::Array, 16> reserved_ranges; - - // Lock for the reserved ranges. - inline static FlagWord reserved_ranges_lock{}; + inline static stl::Array, 16> reserved_ranges + SNMALLOC_GUARDED_BY(reserved_ranges_lock); // Exception handler for handling lazy commit on readonly pages. static LONG NTAPI @@ -392,19 +393,19 @@ namespace snmalloc */ class VirtualVector { - void** data = nullptr; - size_t size = 0; - size_t committed_elements = 0; - size_t reserved_elements = 0; + // Lock protecting structural mutations to the vector storage. + inline static snmalloc::FlagWord push_back_lock{}; + + void** data SNMALLOC_GUARDED_BY(push_back_lock) = nullptr; + size_t size SNMALLOC_GUARDED_BY(push_back_lock) = 0; + size_t committed_elements SNMALLOC_GUARDED_BY(push_back_lock) = 0; + size_t reserved_elements SNMALLOC_GUARDED_BY(push_back_lock) = 0; static constexpr size_t MinCommit = snmalloc::PALWindows::page_size / sizeof(void*); static constexpr size_t MinReserve = 16 * snmalloc::PALWindows::page_size / sizeof(void*); - // Lock for the reserved ranges. - inline static snmalloc::FlagWord push_back_lock{}; - public: VirtualVector( size_t reserve_elems = MinReserve, size_t initial_commit = MinCommit) @@ -443,7 +444,7 @@ namespace snmalloc } } - void push_back(void* value) + void push_back(void* value) SNMALLOC_EXCLUDES(push_back_lock) { snmalloc::FlagLock lock(push_back_lock); ensure_capacity(); @@ -457,7 +458,7 @@ namespace snmalloc return a > b ? a : b; } - void ensure_capacity() + void ensure_capacity() SNMALLOC_REQUIRES(push_back_lock) { if (size >= committed_elements) { @@ -472,6 +473,7 @@ namespace snmalloc } void reserve_and_commit(size_t reserve_elems, size_t commit_elems) + SNMALLOC_REQUIRES(push_back_lock) { size_t reserve_bytes = reserve_elems * sizeof(void*); void** new_block = (void**)VirtualAlloc( @@ -492,6 +494,7 @@ namespace snmalloc } void commit_more(size_t new_commit_elements) + SNMALLOC_REQUIRES(push_back_lock) { if (new_commit_elements > reserved_elements) { @@ -514,7 +517,7 @@ namespace snmalloc } } - void grow_reserved() + void grow_reserved() SNMALLOC_REQUIRES(push_back_lock) { size_t new_reserved = reserved_elements == 0 ? MinReserve : reserved_elements * 2; @@ -545,42 +548,43 @@ namespace snmalloc } public: - void*& operator[](size_t index) + void*& operator[](size_t index) SNMALLOC_REQUIRES(push_back_lock) { return data[index]; } const void* operator[](size_t index) const + SNMALLOC_REQUIRES(push_back_lock) { return data[index]; } - size_t get_size() const + size_t get_size() const SNMALLOC_REQUIRES(push_back_lock) { return size; } - size_t get_capacity() const + size_t get_capacity() const SNMALLOC_REQUIRES(push_back_lock) { return committed_elements; } - void** begin() + void** begin() SNMALLOC_REQUIRES(push_back_lock) { return data; } - void** end() + void** end() SNMALLOC_REQUIRES(push_back_lock) { return data + size; } - const void* const* begin() const + const void* const* begin() const SNMALLOC_REQUIRES(push_back_lock) { return data; } - const void* const* end() const + const void* const* end() const SNMALLOC_REQUIRES(push_back_lock) { return data + size; } diff --git a/src/snmalloc/stl/gnu/array.h b/src/snmalloc/stl/gnu/array.h index fd176fcd4..fb3041d24 100644 --- a/src/snmalloc/stl/gnu/array.h +++ b/src/snmalloc/stl/gnu/array.h @@ -20,12 +20,12 @@ namespace snmalloc return N; } - constexpr T& operator[](size_t i) + constexpr T& operator[](size_t i) SNMALLOC_LIFETIMEBOUND { return _storage[i]; } - constexpr const T& operator[](size_t i) const + constexpr const T& operator[](size_t i) const SNMALLOC_LIFETIMEBOUND { return _storage[i]; } @@ -36,80 +36,86 @@ namespace snmalloc using const_iterator = const T*; [[nodiscard]] constexpr SNMALLOC_FAST_PATH iterator begin() + SNMALLOC_LIFETIMEBOUND { return &_storage[0]; } [[nodiscard]] constexpr SNMALLOC_FAST_PATH const_iterator begin() const + SNMALLOC_LIFETIMEBOUND { return &_storage[0]; } [[nodiscard]] constexpr SNMALLOC_FAST_PATH iterator end() + SNMALLOC_LIFETIMEBOUND { return &_storage[N]; } [[nodiscard]] constexpr SNMALLOC_FAST_PATH const_iterator end() const + SNMALLOC_LIFETIMEBOUND { return &_storage[N]; } [[nodiscard]] constexpr SNMALLOC_FAST_PATH T* data() + SNMALLOC_LIFETIMEBOUND { return &_storage[0]; } [[nodiscard]] constexpr SNMALLOC_FAST_PATH const T* data() const + SNMALLOC_LIFETIMEBOUND { return &_storage[0]; } }; template - constexpr T* begin(Array& a) + constexpr T* begin(SNMALLOC_LIFETIMEBOUND Array& a) { return a.begin(); } template - constexpr T* end(Array& a) + constexpr T* end(SNMALLOC_LIFETIMEBOUND Array& a) { return a.end(); } template - constexpr const T* begin(const Array& a) + constexpr const T* begin(SNMALLOC_LIFETIMEBOUND const Array& a) { return a.begin(); } template - constexpr const T* end(const Array& a) + constexpr const T* end(SNMALLOC_LIFETIMEBOUND const Array& a) { return a.end(); } template - constexpr T* begin(T (&a)[N]) + constexpr T* begin(SNMALLOC_LIFETIMEBOUND T (&a)[N]) { return &a[0]; } template - constexpr T* end(T (&a)[N]) + constexpr T* end(SNMALLOC_LIFETIMEBOUND T (&a)[N]) { return &a[N]; } template - constexpr const T* begin(const T (&a)[N]) + constexpr const T* begin(SNMALLOC_LIFETIMEBOUND const T (&a)[N]) { return &a[0]; } template - constexpr const T* end(const T (&a)[N]) + constexpr const T* end(SNMALLOC_LIFETIMEBOUND const T (&a)[N]) { return &a[N]; } diff --git a/src/snmalloc/stl/gnu/utility.h b/src/snmalloc/stl/gnu/utility.h index 346b32c55..fd7822ec6 100644 --- a/src/snmalloc/stl/gnu/utility.h +++ b/src/snmalloc/stl/gnu/utility.h @@ -3,13 +3,6 @@ #include "snmalloc/ds_core/defines.h" #include "snmalloc/stl/type_traits.h" -// This is used by clang to provide better analysis of lifetimes. -#if __has_cpp_attribute(_Clang::__lifetimebound__) -# define SNMALLOC_LIFETIMEBOUND [[_Clang::__lifetimebound__]] -#else -# define SNMALLOC_LIFETIMEBOUND -#endif - namespace snmalloc { namespace stl