From c38d330dd150e18c90b2f1d79262e2534820e82d Mon Sep 17 00:00:00 2001 From: Arendelle Date: Sun, 14 Jun 2026 16:35:36 +0800 Subject: [PATCH 1/6] [deque] fix: constexpr support for deque in C++26 - Add null-pointer guard in deque_destroy_trivial_common_align consteval - Fix controller array deallocation to use typed allocator, matching allocation method - Use construct_at in overlapped_copy_trivial for constexpr output - Add consteval paths in deque allocation functions using new/delete[] for ctfe - Add constexpr deque tests (push, pop, copy, move, assign, swap, iterators) --- .../fast_io_core_impl/allocation/adapters.h | 24 +- .../freestanding/algorithm.h | 14 +- include/fast_io_dsal/impl/deque.h | 178 +++++++++-- include/fast_io_dsal/impl/string.h | 12 +- tests/0026.container/0003.deque/constexpr.cc | 280 ++++++++++++++++++ 5 files changed, 463 insertions(+), 45 deletions(-) create mode 100644 tests/0026.container/0003.deque/constexpr.cc diff --git a/include/fast_io_core_impl/allocation/adapters.h b/include/fast_io_core_impl/allocation/adapters.h index 38dad7e60..c6ebd69db 100644 --- a/include/fast_io_core_impl/allocation/adapters.h +++ b/include/fast_io_core_impl/allocation/adapters.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once // To make a constexpr allocator, we need ::std::allocator. Because only new expression and // ::std::allocator::allocate are allowed in constexpr functions. See https://github.com/microsoft/STL/issues/1532 @@ -145,10 +145,10 @@ class generic_allocator_adapter if (false) #endif { - auto p{::operator new(n)}; + auto p{__builtin_operator_new(n)}; if (zero) { - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + ::fast_io::freestanding::bytes_clear_n(static_cast<::std::byte *>(p), n); } return p; } @@ -682,7 +682,7 @@ class generic_allocator_adapter #if __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) { - ::operator delete(p); + __builtin_operator_delete(p); } else #endif @@ -704,7 +704,7 @@ class generic_allocator_adapter #if __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) { - ::operator delete(p); + __builtin_operator_delete(p); } else #endif @@ -747,10 +747,10 @@ class generic_allocator_adapter if (false) #endif { - auto p{::operator new(n)}; + auto p{__builtin_operator_new(n)}; if (zero) { - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + ::fast_io::freestanding::bytes_clear_n(static_cast<::std::byte *>(p), n); } return p; } @@ -847,7 +847,7 @@ class generic_allocator_adapter if (false) #endif { - return ::operator new(n); + return __builtin_operator_new(n); } if constexpr (::fast_io::details::has_allocate_aligned_impl) { @@ -874,7 +874,7 @@ class generic_allocator_adapter if (false) #endif { - return ::operator new(n); + return __builtin_operator_new(n); } if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) { @@ -904,7 +904,7 @@ class generic_allocator_adapter if (false) #endif { - return {::operator new(n), n}; + return {__builtin_operator_new(n), n}; } else { @@ -931,7 +931,7 @@ class generic_allocator_adapter if (false) #endif { - return {::operator new(n), n}; + return {__builtin_operator_new(n), n}; } else { @@ -2062,7 +2062,7 @@ class generic_allocator_adapter if (false) #endif { - return ::operator new(n); + return static_cast(::fast_io::freestanding::allocator<::std::byte>{}.allocate(n)); } else { diff --git a/include/fast_io_core_impl/freestanding/algorithm.h b/include/fast_io_core_impl/freestanding/algorithm.h index 1d658024c..59f63aaef 100644 --- a/include/fast_io_core_impl/freestanding/algorithm.h +++ b/include/fast_io_core_impl/freestanding/algorithm.h @@ -301,9 +301,19 @@ inline constexpr output_iter overlapped_copy_trivial(input_iter first, ::std::si { tempbufferptr[i] = first[i]; } - for (::std::size_t i{}; i != n; ++i) + if (__builtin_is_constant_evaluated()) { - result[i] = ::std::move(tempbufferptr[i]); + for (::std::size_t i{}; i != n; ++i) + { + ::std::construct_at(::std::addressof(result[i]), ::std::move(tempbufferptr[i])); + } + } + else + { + for (::std::size_t i{}; i != n; ++i) + { + result[i] = ::std::move(tempbufferptr[i]); + } } return result + n; } diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index f9b871ee7..aa7333d66 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -432,12 +432,32 @@ inline constexpr auto operator<=>(::fast_io::containers::details::deque_iterator template inline constexpr void deque_destroy_trivial_common_align(controllerblocktype &controller, ::std::size_t aligns, ::std::size_t totalsz) noexcept { - for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) +#if __cpp_if_consteval >= 202106L + if consteval { - allocator::deallocate_aligned_n(*i, aligns, totalsz); + if (controller.controller_start_ptr == nullptr) + { + return; + } + for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) + { + delete[] *i; + } + using controller_replacetype = typename controllerblocktype::replacetype; + ::std::size_t const element_count{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1)}; + ::fast_io::typed_generic_allocator_adapter::deallocate_n(controller.controller_start_ptr, element_count); + return; + } + else +#endif + { + for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) + { + allocator::deallocate_aligned_n(*i, aligns, totalsz); + } + ::std::size_t const n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1) * sizeof(void *)}; + allocator::deallocate_n(controller.controller_start_ptr, n); } - ::std::size_t const n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1) * sizeof(void *)}; - allocator::deallocate_n(controller.controller_start_ptr, n); } template @@ -573,7 +593,8 @@ inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltyp --allocated_blocks_count; auto &controller_block{controller.controller_block}; - using begin_ptrtype = typename dequecontroltype::replacetype *; + using replacetype = typename dequecontroltype::replacetype; + using begin_ptrtype = replacetype *; controller_block.controller_after_ptr = (controller_block.controller_start_ptr = allocated_blocks_ptr) + allocated_blocks_count; @@ -585,11 +606,23 @@ inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltyp auto start_block_ptr{allocated_mid_block - initial_allocated_block_counts_half}; auto end_block_ptr{start_block_ptr + initial_allocated_block_counts}; - for (auto i{start_block_ptr}; i != end_block_ptr; ++i) +#if __cpp_if_consteval >= 202106L + if consteval { - ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, bytes))); + for (auto i{start_block_ptr}; i != end_block_ptr; ++i) + { + ::std::construct_at(i, new replacetype[bytes]); + } + } + else +#endif + { + for (auto i{start_block_ptr}; i != end_block_ptr; ++i) + { + ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, bytes * sizeof(replacetype)))); + } } - *end_block_ptr = nullptr; + ::std::construct_at(end_block_ptr, static_cast(nullptr)); controller_block.controller_start_reserved_ptr = start_block_ptr; controller_block.controller_after_reserved_ptr = end_block_ptr; } @@ -604,8 +637,12 @@ inline constexpr void deque_allocate_empty_single_block_impl(dequecontroltype &c auto begin_ptr{*controllerptr}; auto mid_ptr{begin_ptr + (bytes >> 1u)}; - controller.front_block = - controller.back_block = {begin_ptr, mid_ptr, controllerptr}; + controller.front_block.begin_ptr = begin_ptr; + controller.front_block.curr_ptr = mid_ptr; + controller.front_block.controller_ptr = controllerptr; + controller.back_block.begin_ptr = begin_ptr; + controller.back_block.curr_ptr = mid_ptr; + controller.back_block.controller_ptr = controllerptr; controller.front_end_ptr = controller.back_end_ptr = (begin_ptr + bytes); } @@ -661,22 +698,39 @@ inline constexpr void deque_allocate_init_blocks_dezeroing_impl(dequecontroltype ::std::size_t const half_blocks_count_least{blocks_count_least >> 1u}; ::std::size_t const offset{half_blocks_count - half_blocks_count_least}; auto reserve_start{start_ptr + offset}, reserve_after{reserve_start + blocks_count_least}; - using begin_ptrtype = typename dequecontroltype::replacetype *; - for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) + using replacetype = typename dequecontroltype::replacetype; + using begin_ptrtype = replacetype *; +#if __cpp_if_consteval >= 202106L + if consteval { - ::std::construct_at(it, static_cast(allocator::allocate_aligned_conditional_zero(align, blockbytes, zeroing))); + for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) + { + ::std::construct_at(it, new replacetype[blockbytes]); + } + } + else +#endif + { + for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) + { + ::std::construct_at(it, static_cast(allocator::allocate_aligned_conditional_zero(align, blockbytes * sizeof(replacetype), zeroing))); + } } ::std::construct_at(reserve_after, nullptr); - using replacetype = typename dequecontroltype::replacetype; - using begin_ptrtype = replacetype *; begin_ptrtype reserve_start_block{static_cast(*reserve_start)}; - controller.front_block = {reserve_start_block, reserve_start_block, reserve_start}; + controller.front_block.begin_ptr = reserve_start_block; + controller.front_block.curr_ptr = reserve_start_block; + controller.front_block.controller_ptr = reserve_start; controller.front_end_ptr = reserve_start_block + blockbytes; begin_ptrtype reserve_back_block{static_cast(reserve_after[-1])}; - controller.back_block = {reserve_back_block, reserve_back_block, reserve_after - 1}; + controller.back_block.begin_ptr = reserve_back_block; + controller.back_block.curr_ptr = reserve_back_block; + controller.back_block.controller_ptr = reserve_after - 1; controller.back_end_ptr = reserve_back_block + blockbytes; - controller.controller_block = { - start_ptr, reserve_start, reserve_after, start_ptr + blocks_count}; + controller.controller_block.controller_start_ptr = start_ptr; + controller.controller_block.controller_start_reserved_ptr = reserve_start; + controller.controller_block.controller_after_reserved_ptr = reserve_after; + controller.controller_block.controller_after_ptr = start_ptr + blocks_count; } template @@ -1471,10 +1525,22 @@ inline constexpr void deque_reserve_back_blocks_impl_none_empty(dequecontroltype controller.controller_block.controller_after_ptr-controller.controller_block.controller_start_ptr,"\n" "to_allocate_blocks=",to_allocate_blocks); #endif +#if __cpp_if_consteval >= 202106L + if consteval + { for (auto e{pos + to_allocate_blocks}; pos != e; ++pos) { - ::std::construct_at(pos, static_cast(allocator::allocate_aligned(align, blockbytes))); + ::std::construct_at(pos, new replacetype[blockbytes]); } + } + else +#endif + { + for (auto e{pos + to_allocate_blocks}; pos != e; ++pos) + { + ::std::construct_at(pos, static_cast(allocator::allocate_aligned(align, blockbytes * sizeof(replacetype)))); + } + } ::std::construct_at(pos, nullptr); controller.controller_block.controller_after_reserved_ptr = pos; } @@ -1653,10 +1719,22 @@ inline constexpr void deque_reserve_front_blocks_none_empty_impl(dequecontroltyp auto ed{new_controller_start_reserved_ptr}; new_controller_start_reserved_ptr -= to_allocate_blocks; +#if __cpp_if_consteval >= 202106L + if consteval + { + for (auto i{new_controller_start_reserved_ptr}; i != ed; ++i) + { + ::std::construct_at(i, new replacetype[blockbytes]); + } + } + else +#endif + { for (auto i{new_controller_start_reserved_ptr}; i != ed; ++i) { - ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, blockbytes))); + ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, blockbytes * sizeof(replacetype)))); } + } controller.controller_block.controller_start_reserved_ptr = new_controller_start_reserved_ptr; } } @@ -2576,7 +2654,23 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE grow_back(); } auto currptr{controller.back_block.curr_ptr}; - ::std::construct_at(currptr, ::std::forward(args)...); +#if __cpp_if_consteval >= 202106L + if consteval + { + if constexpr (::std::is_trivially_constructible_v) + { + *currptr = value_type{::std::forward(args)...}; + } + else + { + ::std::construct_at(currptr, ::std::forward(args)...); + } + } + else +#endif + { + ::std::construct_at(currptr, ::std::forward(args)...); + } controller.back_block.curr_ptr = currptr + 1; return *currptr; } @@ -2677,16 +2771,44 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE grow_front(); } auto front_curr_ptr{controller.front_block.curr_ptr}; - if constexpr (::std::is_nothrow_constructible_v) +#if __cpp_if_consteval >= 202106L + if consteval { - return *(controller.front_block.curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...)); + if constexpr (::std::is_trivially_constructible_v) + { + *(front_curr_ptr - 1) = value_type{::std::forward(args)...}; + controller.front_block.curr_ptr = front_curr_ptr - 1; + return *(front_curr_ptr - 1); + } + else + { + if constexpr (::std::is_nothrow_constructible_v) + { + return *(controller.front_block.curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...)); + } + else + { + emplace_front_guard guard(__builtin_addressof(this->controller)); + front_curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...); + guard.thisdeq = nullptr; + return *(controller.front_block.curr_ptr = front_curr_ptr); + } + } } else +#endif { - emplace_front_guard guard(__builtin_addressof(this->controller)); - front_curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...); - guard.thisdeq = nullptr; - return *(controller.front_block.curr_ptr = front_curr_ptr); + if constexpr (::std::is_nothrow_constructible_v) + { + return *(controller.front_block.curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...)); + } + else + { + emplace_front_guard guard(__builtin_addressof(this->controller)); + front_curr_ptr = ::std::construct_at(front_curr_ptr - 1, ::std::forward(args)...); + guard.thisdeq = nullptr; + return *(controller.front_block.curr_ptr = front_curr_ptr); + } } } diff --git a/include/fast_io_dsal/impl/string.h b/include/fast_io_dsal/impl/string.h index b114f82ea..26f4dd669 100644 --- a/include/fast_io_dsal/impl/string.h +++ b/include/fast_io_dsal/impl/string.h @@ -85,7 +85,9 @@ inline constexpr void string_heap_dilate_uncheck(::fast_io::containers::details: rsize = newcap - 1u; } } - imp = {ptr, ptr + strsize, ptr + rsize}; + imp.begin_ptr = ptr; + imp.curr_ptr = ptr + strsize; + imp.end_ptr = ptr + rsize; } template @@ -138,13 +140,17 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE using typed_allocator_type = typed_generic_allocator_adapter; auto [ptr, cap]{typed_allocator_type::allocate_at_least(2)}; ::std::construct_at(ptr, char_type{}); - this->imp = {ptr, ptr, ptr + static_cast(cap - 1u)}; + this->imp.begin_ptr = ptr; + this->imp.curr_ptr = ptr; + this->imp.end_ptr = ptr + static_cast(cap - 1u); } else #endif { char_type *const ncstr{const_cast(null_terminated_c_str_v)}; - this->imp = {ncstr, ncstr, ncstr}; + this->imp.begin_ptr = ncstr; + this->imp.curr_ptr = ncstr; + this->imp.end_ptr = ncstr; } } diff --git a/tests/0026.container/0003.deque/constexpr.cc b/tests/0026.container/0003.deque/constexpr.cc new file mode 100644 index 000000000..2ad3887e3 --- /dev/null +++ b/tests/0026.container/0003.deque/constexpr.cc @@ -0,0 +1,280 @@ +#include +#include +#include + +namespace +{ + +// --- Test 1: basic push_back and iteration --- +constexpr bool test_push_back() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + if (dq.size() != 3) + return false; + if (dq[0] != 1 || dq[1] != 2 || dq[2] != 3) + return false; + return true; +} +static_assert(test_push_back()); + +// --- Test 2: push_front and random access --- +constexpr bool test_push_front() +{ + ::fast_io::deque dq; + dq.push_front(10); + dq.push_front(20); + dq.push_front(30); + if (dq.size() != 3) + return false; + if (dq[0] != 30 || dq[1] != 20 || dq[2] != 10) + return false; + return true; +} +static_assert(test_push_front()); + +// --- Test 3: pop_back --- +constexpr bool test_pop_back() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.pop_back(); + if (dq.size() != 2) + return false; + if (dq[0] != 1 || dq[1] != 2) + return false; + dq.pop_back(); + if (dq.size() != 1) + return false; + if (dq[0] != 1) + return false; + dq.pop_back(); + if (!dq.is_empty()) + return false; + return true; +} +static_assert(test_pop_back()); + +// --- Test 4: pop_front --- +constexpr bool test_pop_front() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.pop_front(); + if (dq.size() != 2) + return false; + if (dq[0] != 2 || dq[1] != 3) + return false; + dq.pop_front(); + if (dq.size() != 1 || dq[0] != 3) + return false; + dq.pop_front(); + if (!dq.is_empty()) + return false; + return true; +} +static_assert(test_pop_front()); + +// --- Test 5: front() and back() --- +constexpr bool test_front_back() +{ + ::fast_io::deque dq; + dq.push_back(10); + dq.push_back(20); + dq.push_back(30); + if (dq.front() != 10 || dq.back() != 30) + return false; + dq.front() = 100; + dq.back() = 300; + if (dq[0] != 100 || dq[2] != 300) + return false; + return true; +} +static_assert(test_front_back()); + +// --- Test 6: clear() and reuse --- +constexpr bool test_clear() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + dq.clear(); + if (!dq.is_empty() || dq.size() != 0) + return false; + dq.push_back(42); + if (dq.size() != 1 || dq[0] != 42) + return false; + return true; +} +static_assert(test_clear()); + +// --- Test 7: copy construction --- +constexpr bool test_copy_construct() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2(dq); + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + if (dq.size() != 3) + return false; + if (dq[0] != 1 || dq[1] != 2 || dq[2] != 3) + return false; + return true; +} +static_assert(test_copy_construct()); + +// --- Test 8: move construction --- +constexpr bool test_move_construct() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2(::std::move(dq)); + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + return true; +} +static_assert(test_move_construct()); + +// --- Test 9: push_back sufficient elements to trigger growth --- +constexpr bool test_push_back_many() +{ + ::fast_io::deque dq; + // push enough to force multiple block allocations + for (int i{}; i != 5000; ++i) + { + dq.push_back(i); + } + if (dq.size() != 5000) + return false; + for (int i{}; i != 5000; ++i) + { + if (dq[i] != i) + return false; + } + return true; +} +static_assert(test_push_back_many()); + +// --- Test 10: mixed push_front and push_back --- +constexpr bool test_mixed_push() +{ + ::fast_io::deque dq; + dq.push_back(2); + dq.push_front(1); + dq.push_back(3); + dq.push_front(0); + if (dq.size() != 4) + return false; + if (dq[0] != 0 || dq[1] != 1 || dq[2] != 2 || dq[3] != 3) + return false; + return true; +} +static_assert(test_mixed_push()); + +// --- Test 11: copy assignment --- +constexpr bool test_copy_assign() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2; + dq2.push_back(99); + dq2 = dq; + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + return true; +} +static_assert(test_copy_assign()); + +// --- Test 12: move assignment --- +constexpr bool test_move_assign() +{ + ::fast_io::deque dq; + dq.push_back(1); + dq.push_back(2); + dq.push_back(3); + ::fast_io::deque dq2; + dq2.push_back(99); + dq2 = ::std::move(dq); + if (dq2.size() != 3) + return false; + if (dq2[0] != 1 || dq2[1] != 2 || dq2[2] != 3) + return false; + return true; +} +static_assert(test_move_assign()); + +// --- Test 13: swap --- +constexpr bool test_swap() +{ + ::fast_io::deque dq1; + dq1.push_back(1); + dq1.push_back(2); + ::fast_io::deque dq2; + dq2.push_back(10); + dq2.push_back(20); + dq2.push_back(30); + swap(dq1, dq2); + if (dq1.size() != 3 || dq2.size() != 2) + return false; + if (dq1[0] != 10 || dq1[1] != 20 || dq1[2] != 30) + return false; + if (dq2[0] != 1 || dq2[1] != 2) + return false; + return true; +} +static_assert(test_swap()); + +// --- Test 14: iterator operations --- +constexpr bool test_iterators() +{ + ::fast_io::deque dq; + dq.push_back(10); + dq.push_back(20); + dq.push_back(30); + auto it = dq.begin(); + if (*it != 10) + return false; + ++it; + if (*it != 20) + return false; + it += 1; + if (*it != 30) + return false; + --it; + if (*it != 20) + return false; + if (dq.end() - dq.begin() != 3) + return false; + auto rit = dq.rbegin(); + if (*rit != 30) + return false; + return true; +} +static_assert(test_iterators()); + +} // namespace + +int main() +{ + return 0; +} From ecb247465433bb80dde32c5c8be64cf0825013d8 Mon Sep 17 00:00:00 2001 From: Arendelle Date: Sun, 14 Jun 2026 16:42:41 +0800 Subject: [PATCH 2/6] guard __builtin_operator_new with FAST_IO_HAS_BUILTIN, fallback to ::operator new --- .../fast_io_core_impl/allocation/adapters.h | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/include/fast_io_core_impl/allocation/adapters.h b/include/fast_io_core_impl/allocation/adapters.h index c6ebd69db..a9a1589b1 100644 --- a/include/fast_io_core_impl/allocation/adapters.h +++ b/include/fast_io_core_impl/allocation/adapters.h @@ -145,7 +145,13 @@ class generic_allocator_adapter if (false) #endif { - auto p{__builtin_operator_new(n)}; + auto p{ +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + }; if (zero) { ::fast_io::freestanding::bytes_clear_n(static_cast<::std::byte *>(p), n); @@ -747,7 +753,13 @@ class generic_allocator_adapter if (false) #endif { - auto p{__builtin_operator_new(n)}; + auto p{ +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + }; if (zero) { ::fast_io::freestanding::bytes_clear_n(static_cast<::std::byte *>(p), n); @@ -847,7 +859,13 @@ class generic_allocator_adapter if (false) #endif { - return __builtin_operator_new(n); + return +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + ; } if constexpr (::fast_io::details::has_allocate_aligned_impl) { @@ -874,7 +892,13 @@ class generic_allocator_adapter if (false) #endif { - return __builtin_operator_new(n); + return +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + ; } if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) { @@ -904,7 +928,13 @@ class generic_allocator_adapter if (false) #endif { - return {__builtin_operator_new(n), n}; + return { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + , n}; } else { @@ -931,7 +961,13 @@ class generic_allocator_adapter if (false) #endif { - return {__builtin_operator_new(n), n}; + return { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(n) +#else + ::operator new(n) +#endif + , n}; } else { From abc9d0df2de4eb094926e1327f5da4ad5bc5c788 Mon Sep 17 00:00:00 2001 From: Arendelle Date: Sun, 14 Jun 2026 16:44:20 +0800 Subject: [PATCH 3/6] guard __builtin_operator_delete with FAST_IO_HAS_BUILTIN, fallback to ::operator delete --- include/fast_io_core_impl/allocation/adapters.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/fast_io_core_impl/allocation/adapters.h b/include/fast_io_core_impl/allocation/adapters.h index a9a1589b1..5e46502ea 100644 --- a/include/fast_io_core_impl/allocation/adapters.h +++ b/include/fast_io_core_impl/allocation/adapters.h @@ -688,7 +688,11 @@ class generic_allocator_adapter #if __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_delete) __builtin_operator_delete(p); +#else + ::operator delete(p); +#endif } else #endif @@ -710,7 +714,11 @@ class generic_allocator_adapter #if __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) { +#if FAST_IO_HAS_BUILTIN(__builtin_operator_delete) __builtin_operator_delete(p); +#else + ::operator delete(p); +#endif } else #endif From 3db5734bb58ca401b089696373e88e3896e45543 Mon Sep 17 00:00:00 2001 From: Arendelle Date: Sun, 14 Jun 2026 17:02:21 +0800 Subject: [PATCH 4/6] replace __builtin_is_constant_evaluated with #if __cpp_if_consteval / if consteval pattern --- include/fast_io_core_impl/freestanding/algorithm.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/fast_io_core_impl/freestanding/algorithm.h b/include/fast_io_core_impl/freestanding/algorithm.h index 59f63aaef..b52ee38a3 100644 --- a/include/fast_io_core_impl/freestanding/algorithm.h +++ b/include/fast_io_core_impl/freestanding/algorithm.h @@ -301,7 +301,8 @@ inline constexpr output_iter overlapped_copy_trivial(input_iter first, ::std::si { tempbufferptr[i] = first[i]; } - if (__builtin_is_constant_evaluated()) +#if __cpp_if_consteval >= 202106L + if consteval { for (::std::size_t i{}; i != n; ++i) { @@ -309,6 +310,7 @@ inline constexpr output_iter overlapped_copy_trivial(input_iter first, ::std::si } } else +#endif { for (::std::size_t i{}; i != n; ++i) { From be312367d74e2ee74bf8b5a4be90913e89927d57 Mon Sep 17 00:00:00 2001 From: Arendelle Date: Sun, 14 Jun 2026 17:44:14 +0800 Subject: [PATCH 5/6] [deque][test] guard constexpr test with #if for GCC>=15 / clang>=22 --- tests/0026.container/0003.deque/constexpr.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/0026.container/0003.deque/constexpr.cc b/tests/0026.container/0003.deque/constexpr.cc index 2ad3887e3..a9da08b9b 100644 --- a/tests/0026.container/0003.deque/constexpr.cc +++ b/tests/0026.container/0003.deque/constexpr.cc @@ -2,6 +2,8 @@ #include #include +#if (defined(__GNUC__) && __GNUC__ >= 15) || (defined(__clang_major__) && __clang_major__ >= 22) + namespace { @@ -274,6 +276,8 @@ static_assert(test_iterators()); } // namespace +#endif + int main() { return 0; From 44a1f6f39f0f59053fba952feb71b51462a053f8 Mon Sep 17 00:00:00 2001 From: Arendelle Date: Sun, 14 Jun 2026 22:18:20 +0800 Subject: [PATCH 6/6] Am I forgot to submit something? --- include/fast_io_dsal/impl/deque.h | 40 ++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index aa7333d66..b7efcd1fd 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -441,7 +441,11 @@ inline constexpr void deque_destroy_trivial_common_align(controllerblocktype &co } for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) { - delete[] *i; +#if FAST_IO_HAS_BUILTIN(__builtin_operator_delete) + __builtin_operator_delete(*i); +#else + ::operator delete(*i); +#endif } using controller_replacetype = typename controllerblocktype::replacetype; ::std::size_t const element_count{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1)}; @@ -607,11 +611,17 @@ inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltyp auto end_block_ptr{start_block_ptr + initial_allocated_block_counts}; #if __cpp_if_consteval >= 202106L - if consteval + if consteval { for (auto i{start_block_ptr}; i != end_block_ptr; ++i) { - ::std::construct_at(i, new replacetype[bytes]); + ::std::construct_at(i, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(bytes * sizeof(replacetype)) +#else + ::operator new(bytes * sizeof(replacetype)) +#endif + )); } } else @@ -705,7 +715,13 @@ inline constexpr void deque_allocate_init_blocks_dezeroing_impl(dequecontroltype { for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) { - ::std::construct_at(it, new replacetype[blockbytes]); + ::std::construct_at(it, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(blockbytes * sizeof(replacetype)) +#else + ::operator new(blockbytes * sizeof(replacetype)) +#endif + )); } } else @@ -1530,7 +1546,13 @@ inline constexpr void deque_reserve_back_blocks_impl_none_empty(dequecontroltype { for (auto e{pos + to_allocate_blocks}; pos != e; ++pos) { - ::std::construct_at(pos, new replacetype[blockbytes]); + ::std::construct_at(pos, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(blockbytes * sizeof(replacetype)) +#else + ::operator new(blockbytes * sizeof(replacetype)) +#endif + )); } } else @@ -1724,7 +1746,13 @@ inline constexpr void deque_reserve_front_blocks_none_empty_impl(dequecontroltyp { for (auto i{new_controller_start_reserved_ptr}; i != ed; ++i) { - ::std::construct_at(i, new replacetype[blockbytes]); + ::std::construct_at(i, static_cast( +#if FAST_IO_HAS_BUILTIN(__builtin_operator_new) + __builtin_operator_new(blockbytes * sizeof(replacetype)) +#else + ::operator new(blockbytes * sizeof(replacetype)) +#endif + )); } } else