diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -3198,6 +3198,17 @@ {__alloc_traits::deallocate(__alloc_, __p, __s_);} }; +#if _LIBCPP_STD_VER > 17 + +template +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC +auto construct_at(_Tp* __p, _Args&&... __args) + -> decltype(::new (_VSTD::declval()) _Tp(_VSTD::forward<_Args>(__args)...)) { + return ::new ((void*)__p) _Tp(_VSTD::forward<_Args>(__args)...); +} + +#endif + template _ForwardIterator uninitialized_copy(_InputIterator __f, _InputIterator __l, _ForwardIterator __r) @@ -3296,21 +3307,21 @@ #if _LIBCPP_STD_VER > 14 template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC void destroy_at(_Tp* __loc) { _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at"); __loc->~_Tp(); } template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC void destroy(_ForwardIterator __first, _ForwardIterator __last) { for (; __first != __last; ++__first) _VSTD::destroy_at(_VSTD::addressof(*__first)); } template -inline _LIBCPP_INLINE_VISIBILITY +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC _ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) { for (; __n > 0; (void)++__first, --__n) _VSTD::destroy_at(_VSTD::addressof(*__first)); diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 + +// + +// template +// constexpr T* construct_at(T* location, Args&&... args); + +#include +#include +#include + +#include "test_macros.h" + +struct Counted { + int& count_; + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count; } + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; } +}; + +TEST_CONSTEXPR_DYNAMIC_ALLOC bool test() +{ + { + std::allocator a; + Counted* p = a.allocate(2); + int count = 0; + std::construct_at(p, count); + assert(count == 1); + std::construct_at(p+1, count); + assert(count == 2); + (p+1)->~Counted(); + assert(count == 1); + p->~Counted(); + assert(count == 0); + a.deallocate(p, 2); + } + + { + std::allocator a; + const Counted* p = a.allocate(2); + int count = 0; + std::construct_at(p, count); + assert(count == 1); + std::construct_at(p+1, count); + assert(count == 2); + (p+1)->~Counted(); + assert(count == 1); + p->~Counted(); + assert(count == 0); + a.deallocate(p, 2); + } + + return true; +} + +int main(int, char**) +{ + test(); + +#if defined(__cpp_constexpr_dynamic_alloc) + static_assert(test()); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp @@ -11,7 +11,7 @@ // // template -// void destroy(ForwardIt, ForwardIt); +// constexpr void destroy(ForwardIt, ForwardIt); #include #include @@ -21,28 +21,45 @@ #include "test_iterators.h" struct Counted { - static int count; - static void reset() { count = 0; } - Counted() { ++count; } - Counted(Counted const&) { ++count; } - ~Counted() { --count; } + int& count_; + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count; } + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; } friend void operator&(Counted) = delete; }; -int Counted::count = 0; -int main(int, char**) +TEST_CONSTEXPR_DYNAMIC_ALLOC bool +test() { using It = forward_iterator; const int N = 5; - alignas(Counted) char pool[sizeof(Counted)*N] = {}; - Counted* p = (Counted*)pool; - std::uninitialized_fill(p, p+N, Counted()); - assert(Counted::count == 5); + int count = 0; + std::allocator a; + Counted* p = a.allocate(N); + for (Counted* iter = p; iter < p+N; ++iter) { +#if TEST_STD_VER > 17 + std::construct_at(iter, count); +#else + ::new ((void*)iter) Counted(count); +#endif + } + assert(count == 5); std::destroy(p, p+1); - p += 1; - assert(Counted::count == 4); - std::destroy(It(p), It(p + 4)); - assert(Counted::count == 0); + assert(count == 4); + std::destroy(It(p+1), It(p+5)); + assert(count == 0); + a.deallocate(p, N); + + return true; +} + +int main(int, char**) +{ + test(); + +#if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc) + static_assert(test()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp @@ -11,7 +11,7 @@ // // template -// void destroy_at(_Tp*); +// constexpr void destroy_at(_Tp*); #include #include @@ -20,62 +20,84 @@ #include "test_macros.h" struct Counted { - static int count; - static void reset() { count = 0; } - Counted() { ++count; } - Counted(Counted const&) { ++count; } - ~Counted() { --count; } + int& count_; + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; } friend void operator&(Counted) = delete; }; -int Counted::count = 0; struct VCounted { - static int count; - static void reset() { count = 0; } - VCounted() { ++count; } - VCounted(VCounted const&) { ++count; } - virtual ~VCounted() { --count; } + int& count_; + TEST_CONSTEXPR_DYNAMIC_ALLOC VCounted(int& count) : count_(count) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC VCounted(VCounted const& that) : count_(that.count_) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC virtual ~VCounted() { --count_; } friend void operator&(VCounted) = delete; }; -int VCounted::count = 0; struct DCounted : VCounted { + using VCounted::VCounted; friend void operator&(DCounted) = delete; }; -int main(int, char**) +TEST_CONSTEXPR_DYNAMIC_ALLOC bool +test() { { - void* mem1 = std::malloc(sizeof(Counted)); - void* mem2 = std::malloc(sizeof(Counted)); + int count = 0; + std::allocator a; + Counted* mem1 = a.allocate(1); + Counted* mem2 = a.allocate(1); assert(mem1 && mem2); - assert(Counted::count == 0); - Counted* ptr1 = ::new(mem1) Counted(); - Counted* ptr2 = ::new(mem2) Counted(); - assert(Counted::count == 2); + assert(count == 0); +#if TEST_STD_VER > 17 + Counted* ptr1 = std::construct_at(mem1, count); + Counted* ptr2 = std::construct_at(mem2, count); +#else + Counted* ptr1 = ::new ((void*)mem1) Counted(count); + Counted* ptr2 = ::new ((void*)mem2) Counted(count); +#endif + assert(count == 2); std::destroy_at(ptr1); - assert(Counted::count == 1); + assert(count == 1); std::destroy_at(ptr2); - assert(Counted::count == 0); - std::free(mem1); - std::free(mem2); + assert(count == 0); + a.deallocate(mem1, 1); + a.deallocate(mem2, 1); } { - void* mem1 = std::malloc(sizeof(DCounted)); - void* mem2 = std::malloc(sizeof(DCounted)); + int count = 0; + std::allocator a; + DCounted* mem1 = a.allocate(1); + DCounted* mem2 = a.allocate(1); assert(mem1 && mem2); - assert(DCounted::count == 0); - DCounted* ptr1 = ::new(mem1) DCounted(); - DCounted* ptr2 = ::new(mem2) DCounted(); - assert(DCounted::count == 2); - assert(VCounted::count == 2); + assert(count == 0); +#if TEST_STD_VER > 17 + DCounted* ptr1 = std::construct_at(mem1, count); + DCounted* ptr2 = std::construct_at(mem2, count); +#else + DCounted* ptr1 = ::new ((void*)mem1) DCounted(count); + DCounted* ptr2 = ::new ((void*)mem2) DCounted(count); +#endif + assert(count == 2); std::destroy_at(ptr1); - assert(VCounted::count == 1); + assert(count == 1); std::destroy_at(ptr2); - assert(VCounted::count == 0); - std::free(mem1); - std::free(mem2); + assert(count == 0); + a.deallocate(mem1, 1); + a.deallocate(mem2, 1); } + return true; +} + +int main(int, char**) +{ + test(); + +#if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc) + static_assert(test()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp @@ -11,7 +11,7 @@ // // template -// ForwardIt destroy_n(ForwardIt, Size s); +// constexpr ForwardIt destroy_n(ForwardIt, Size s); #include #include @@ -21,30 +21,47 @@ #include "test_iterators.h" struct Counted { - static int count; - static void reset() { count = 0; } - Counted() { ++count; } - Counted(Counted const&) { ++count; } - ~Counted() { --count; } + int& count_; + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; } + TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; } friend void operator&(Counted) = delete; }; -int Counted::count = 0; -int main(int, char**) +TEST_CONSTEXPR_DYNAMIC_ALLOC bool +test() { using It = forward_iterator; const int N = 5; - alignas(Counted) char pool[sizeof(Counted)*N] = {}; - Counted* p = (Counted*)pool; - std::uninitialized_fill(p, p+N, Counted()); - assert(Counted::count == 5); + int count = 0; + std::allocator a; + Counted* p = a.allocate(N); + for (Counted* iter = p; iter < p+N; ++iter) { +#if TEST_STD_VER > 17 + std::construct_at(iter, count); +#else + ::new ((void*)iter) Counted(count); +#endif + } + assert(count == 5); Counted* np = std::destroy_n(p, 1); assert(np == p+1); - assert(Counted::count == 4); - p += 1; - It it = std::destroy_n(It(p), 4); - assert(it == It(p+4)); - assert(Counted::count == 0); + assert(count == 4); + It it = std::destroy_n(It(p+1), 4); + assert(it == It(p+5)); + assert(count == 0); + a.deallocate(p, N); + + return true; +} + +int main(int, char**) +{ + test(); + +#if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc) + static_assert(test()); +#endif return 0; } diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -125,6 +125,11 @@ # else # define TEST_THROW_SPEC(...) throw(__VA_ARGS__) # endif +# if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc) +# define TEST_CONSTEXPR_DYNAMIC_ALLOC constexpr +# else +# define TEST_CONSTEXPR_DYNAMIC_ALLOC +# endif # if TEST_STD_VER > 17 # define TEST_CONSTEXPR_CXX20 constexpr # else