[libcxx-commits] [libcxx] b9bc3c1 - [libc++][ranges] Implement `construct_at` and `destroy{, _at}`.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jan 12 16:01:47 PST 2022
Author: Konstantin Varlamov
Date: 2022-01-12T16:01:04-08:00
New Revision: b9bc3c107c6cee93cd1a7004142f11741e0225bf
URL: https://github.com/llvm/llvm-project/commit/b9bc3c107c6cee93cd1a7004142f11741e0225bf
DIFF: https://github.com/llvm/llvm-project/commit/b9bc3c107c6cee93cd1a7004142f11741e0225bf.diff
LOG: [libc++][ranges] Implement `construct_at` and `destroy{,_at}`.
Differential Revision: https://reviews.llvm.org/D116078
Added:
libcxx/include/__memory/ranges_construct_at.h
libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp
Modified:
libcxx/docs/Status/RangesAlgorithms.csv
libcxx/docs/Status/RangesPaper.csv
libcxx/include/CMakeLists.txt
libcxx/include/__memory/construct_at.h
libcxx/include/memory
libcxx/include/module.modulemap
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 2636fca2d838d..155e6b880fb7e 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -94,7 +94,7 @@ Uninitialised memory,uninitialized_default_construct,Konstantin Varlamov,`D11531
Uninitialised memory,uninitialized_default_construct_n,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
Uninitialised memory,uninitialized_value_construct,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
Uninitialised memory,uninitialized_value_construct_n,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
-Uninitialised memory,destroy,Konstantin Varlamov,n/a,Not started
-Uninitialised memory,destroy_n,Konstantin Varlamov,n/a,Not started
-Uninitialised memory,destroy_at,Konstantin Varlamov,n/a,Not started
-Uninitialised memory,construct_at,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,destroy,Konstantin Varlamov,`D116078 <https://llvm.org/D116078>`_,✅
+Uninitialised memory,destroy_n,Konstantin Varlamov,`D116078 <https://llvm.org/D116078>`_,✅
+Uninitialised memory,destroy_at,Konstantin Varlamov,`D116078 <https://llvm.org/D116078>`_,✅
+Uninitialised memory,construct_at,Konstantin Varlamov,`D116078 <https://llvm.org/D116078>`_,✅
diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index ef6aacc0ac523..be82f3fa6d4c1 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -28,10 +28,10 @@ Section,Description,Dependencies,Assignee,Complete
| `ranges::uninitialized_move_n <https://llvm.org/D116023>`_
| `ranges::uninitialized_fill <https://llvm.org/D115626>`_
| `ranges::uninitialized_fill_n <https://llvm.org/D115626>`_
-| ranges::construct_at
-| ranges::destroy
-| ranges::destroy_at
-| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,In progress
+| `ranges::construct_at <https://llvm.org/D116078>`_
+| `ranges::destroy <https://llvm.org/D116078>`_
+| `ranges::destroy_at <https://llvm.org/D116078>`_
+| `ranges::destroy_n <https://llvm.org/D116078>`_",[special.mem.concepts],Konstantin Varlamov,✅
[strings],Adds begin/end and updates const_iterator.,[iterator.concepts],Unassigned,Not started
[views.span],Same as [strings],[iterator.concepts],Unassigned,Not started
`[iterator.cust.move] <http://wg21.link/iterator.cust.move>`_,`ranges::iter_move <https://llvm.org/D99873>`_,,Zoe Carver,✅
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 7efe0383d80b6..30708599751cd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -249,6 +249,7 @@ set(files
__memory/concepts.h
__memory/construct_at.h
__memory/pointer_traits.h
+ __memory/ranges_construct_at.h
__memory/ranges_uninitialized_algorithms.h
__memory/raw_storage_iterator.h
__memory/shared_ptr.h
diff --git a/libcxx/include/__memory/construct_at.h b/libcxx/include/__memory/construct_at.h
index 3b58451c5009f..580ce781f482d 100644
--- a/libcxx/include/__memory/construct_at.h
+++ b/libcxx/include/__memory/construct_at.h
@@ -14,6 +14,7 @@
#include <__debug>
#include <__iterator/access.h>
#include <__memory/addressof.h>
+#include <__memory/voidify.h>
#include <__utility/forward.h>
#include <type_traits>
#include <utility>
@@ -31,10 +32,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template<class _Tp, class ..._Args, class = decltype(
::new (declval<void*>()) _Tp(declval<_Args>()...)
)>
-_LIBCPP_INLINE_VISIBILITY
+_LIBCPP_HIDE_FROM_ABI
constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) {
_LIBCPP_ASSERT(__location, "null pointer given to construct_at");
- return ::new ((void*)__location) _Tp(_VSTD::forward<_Args>(__args)...);
+ return ::new (_VSTD::__voidify(*__location)) _Tp(_VSTD::forward<_Args>(__args)...);
}
#endif
@@ -46,7 +47,7 @@ constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) {
template <class _ForwardIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
-void __destroy(_ForwardIterator, _ForwardIterator);
+_ForwardIterator __destroy(_ForwardIterator, _ForwardIterator);
template <class _Tp, typename enable_if<!is_array<_Tp>::value, int>::type = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -66,9 +67,10 @@ void __destroy_at(_Tp* __loc) {
template <class _ForwardIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
-void __destroy(_ForwardIterator __first, _ForwardIterator __last) {
+_ForwardIterator __destroy(_ForwardIterator __first, _ForwardIterator __last) {
for (; __first != __last; ++__first)
_VSTD::__destroy_at(_VSTD::addressof(*__first));
+ return __first;
}
#if _LIBCPP_STD_VER > 14
@@ -90,7 +92,7 @@ void destroy_at(_Tp* __loc) {
template <class _ForwardIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
void destroy(_ForwardIterator __first, _ForwardIterator __last) {
- _VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last));
+ (void)_VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last));
}
template <class _ForwardIterator, class _Size>
diff --git a/libcxx/include/__memory/ranges_construct_at.h b/libcxx/include/__memory/ranges_construct_at.h
new file mode 100644
index 0000000000000..99ae299089d30
--- /dev/null
+++ b/libcxx/include/__memory/ranges_construct_at.h
@@ -0,0 +1,144 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MEMORY_RANGES_CONSTRUCT_AT_H
+#define _LIBCPP___MEMORY_RANGES_CONSTRUCT_AT_H
+
+#include <__concepts/destructible.h>
+#include <__config>
+#include <__function_like.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/readable_traits.h>
+#include <__memory/concepts.h>
+#include <__memory/construct_at.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.h>
+#include <__utility/declval.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_HAS_NO_RANGES)
+namespace ranges {
+
+// construct_at
+
+namespace __construct_at {
+
+struct __fn final : private __function_like {
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+ template<class _Tp, class... _Args, class = decltype(
+ ::new (declval<void*>()) _Tp(declval<_Args>()...)
+ )>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _Tp* operator()(_Tp* __location, _Args&& ...__args) const {
+ return _VSTD::construct_at(__location, _VSTD::forward<_Args>(__args)...);
+ }
+
+};
+
+} // namespace __construct_at
+
+inline namespace __cpo {
+inline constexpr auto construct_at = __construct_at::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// destroy_at
+
+namespace __destroy_at {
+
+struct __fn final : private __function_like {
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+ template <destructible _Tp>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr void operator()(_Tp* __location) const noexcept {
+ _VSTD::destroy_at(__location);
+ }
+
+};
+
+} // namespace __destroy_at
+
+inline namespace __cpo {
+inline constexpr auto destroy_at = __destroy_at::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// destroy
+
+namespace __destroy {
+
+struct __fn final : private __function_like {
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+ template <__nothrow_input_iterator _InputIterator, __nothrow_sentinel_for<_InputIterator> _Sentinel>
+ requires destructible<iter_value_t<_InputIterator>>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _InputIterator operator()(_InputIterator __first, _Sentinel __last) const noexcept {
+ return _VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last));
+ }
+
+ template <__nothrow_input_range _InputRange>
+ requires destructible<range_value_t<_InputRange>>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr borrowed_iterator_t<_InputRange> operator()(_InputRange&& __range) const noexcept {
+ return (*this)(ranges::begin(__range), ranges::end(__range));
+ }
+
+};
+
+} // namespace __destroy
+
+inline namespace __cpo {
+inline constexpr auto destroy = __destroy::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// destroy_n
+
+namespace __destroy_n {
+
+struct __fn final : private __function_like {
+
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+ template <__nothrow_input_iterator _InputIterator>
+ requires destructible<iter_value_t<_InputIterator>>
+ _LIBCPP_HIDE_FROM_ABI
+ constexpr _InputIterator operator()(_InputIterator __first, iter_
diff erence_t<_InputIterator> __n) const noexcept {
+ return _VSTD::destroy_n(_VSTD::move(__first), __n);
+ }
+
+};
+
+} // namespace __destroy_n
+
+inline namespace __cpo {
+inline constexpr auto destroy_n = __destroy_n::__fn(__function_like::__tag());
+} // namespace __cpo
+
+} // namespace ranges
+#endif // !defined(_LIBCPP_HAS_NO_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_RANGES_CONSTRUCT_AT_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index 244b40fe7720d..db59936707e40 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -244,15 +244,40 @@ ForwardIterator uninitialized_fill_n(ForwardIterator first, iter_
diff erence_t<Fo
template <class T, class ...Args>
constexpr T* construct_at(T* location, Args&& ...args); // since C++20
+namespace ranges {
+ template<class T, class... Args>
+ constexpr T* construct_at(T* location, Args&&... args); // since C++20
+}
+
template <class T>
void destroy_at(T* location); // constexpr in C++20
+namespace ranges {
+ template<destructible T>
+ constexpr void destroy_at(T* location) noexcept; // since C++20
+}
+
template <class ForwardIterator>
void destroy(ForwardIterator first, ForwardIterator last); // constexpr in C++20
+namespace ranges {
+ template<nothrow-input-iterator InputIterator, nothrow-sentinel-for<InputIterator> Sentinel>
+ requires destructible<iter_value_t<InputIterator>>
+ constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20
+ template<nothrow-input-range InputRange>
+ requires destructible<range_value_t<InputRange>>
+ constexpr borrowed_iterator_t<InputRange> destroy(InputRange&& range) noexcept; // since C++20
+}
+
template <class ForwardIterator, class Size>
ForwardIterator destroy_n(ForwardIterator first, Size n); // constexpr in C++20
+namespace ranges {
+ template<nothrow-input-iterator InputIterator>
+ requires destructible<iter_value_t<InputIterator>>
+ constexpr InputIterator destroy_n(InputIterator first, iter_
diff erence_t<InputIterator> n) noexcept; // since C++20
+}
+
template <class InputIterator, class ForwardIterator>
ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result);
@@ -790,6 +815,7 @@ void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
#include <__memory/concepts.h>
#include <__memory/construct_at.h>
#include <__memory/pointer_traits.h>
+#include <__memory/ranges_construct_at.h>
#include <__memory/ranges_uninitialized_algorithms.h>
#include <__memory/raw_storage_iterator.h>
#include <__memory/shared_ptr.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 305fe1bc9d35f..828c9d55495fd 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -661,6 +661,10 @@ module std [system] {
module concepts { private header "__memory/concepts.h" }
module construct_at { private header "__memory/construct_at.h" }
module pointer_traits { private header "__memory/pointer_traits.h" }
+ module ranges_construct_at {
+ private header "__memory/ranges_construct_at.h"
+ export __function_like
+ }
module ranges_uninitialized_algorithms {
private header "__memory/ranges_uninitialized_algorithms.h"
export __function_like
diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp
new file mode 100644
index 0000000000000..1aca1e47f6ddc
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/memory/ranges_construct_at.module.verify.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__memory/ranges_construct_at.h'}}
+#include <__memory/ranges_construct_at.h>
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
index 06c6d0ef1db17..e7fd797966710 100644
--- 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
@@ -18,7 +18,9 @@
#include <memory>
#include <cassert>
+#include <utility>
+#include "test_iterators.h"
struct Foo {
int a;
@@ -97,15 +99,23 @@ constexpr bool test()
return true;
}
-// Make sure std::construct_at SFINAEs out based on the validity of calling
-// the constructor, instead of hard-erroring.
-template <typename T, typename = decltype(
- std::construct_at((T*)nullptr, 1, 2) // missing arguments for Foo(...)
-)>
-constexpr bool test_sfinae(int) { return false; }
-template <typename T>
-constexpr bool test_sfinae(...) { return true; }
-static_assert(test_sfinae<Foo>(int()));
+template <class ...Args, class = decltype(std::construct_at(std::declval<Args>()...))>
+constexpr bool can_construct_at(Args&&...) { return true; }
+
+template <class ...Args>
+constexpr bool can_construct_at(...) { return false; }
+
+// Check that SFINAE works.
+static_assert( can_construct_at((int*)nullptr, 42));
+static_assert( can_construct_at((Foo*)nullptr, 1, '2', 3.0));
+static_assert(!can_construct_at((Foo*)nullptr, 1, '2'));
+static_assert(!can_construct_at((Foo*)nullptr, 1, '2', 3.0, 4));
+static_assert(!can_construct_at(nullptr, 1, '2', 3.0));
+static_assert(!can_construct_at((int*)nullptr, 1, '2', 3.0));
+static_assert(!can_construct_at(contiguous_iterator<Foo*>(), 1, '2', 3.0));
+// Can't construct function pointers.
+static_assert(!can_construct_at((int(*)())nullptr));
+static_assert(!can_construct_at((int(*)())nullptr, nullptr));
int main(int, char**)
{
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp
new file mode 100644
index 0000000000000..11a78f3e2cd07
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+//
+// namespace ranges {
+// template<class T, class... Args>
+// constexpr T* construct_at(T* location, Args&&... args); // since C++20
+// }
+
+#include <cassert>
+#include <initializer_list>
+#include <memory>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// TODO(varconst): consolidate the ADL checks into a single file.
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::construct_at)>);
+
+struct Foo {
+ int x = 0;
+ int y = 0;
+
+ constexpr Foo() = default;
+ constexpr explicit Foo(int set_x, int set_y) : x(set_x), y(set_y) {}
+ constexpr Foo(std::initializer_list<int>);
+
+ void operator&() const = delete;
+ void operator,(auto&&) const = delete;
+};
+
+ASSERT_SAME_TYPE(decltype(std::ranges::construct_at((int*)nullptr)), int*);
+ASSERT_SAME_TYPE(decltype(std::ranges::construct_at((Foo*)nullptr)), Foo*);
+
+struct Counted {
+ int& count;
+
+ constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
+ constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
+ constexpr ~Counted() { --count; }
+};
+
+constexpr bool test() {
+ // Value initialization.
+ {
+ int x = 1;
+
+ int* result = std::ranges::construct_at(&x);
+ assert(result == &x);
+ assert(x == 0);
+ }
+
+ // Copy initialization.
+ {
+ int x = 1;
+
+ int* result = std::ranges::construct_at(&x, 42);
+ assert(result == &x);
+ assert(x == 42);
+ }
+
+ // Explicit multiargument constructor; also checks that the initializer list constructor is not invoked.
+ {
+ Foo f;
+
+ Foo* result = std::ranges::construct_at(std::addressof(f), 42, 123);
+ assert(result == std::addressof(f));
+ assert(f.x == 42);
+ assert(f.y == 123);
+ }
+
+ // Works with buffers of uninitialized memory.
+ {
+ std::allocator<Counted> alloc;
+ Counted* out = alloc.allocate(2);
+ int count = 0;
+
+ Counted* result = std::ranges::construct_at(out, count);
+ assert(result == out);
+ assert(count == 1);
+
+ result = std::ranges::construct_at(out + 1, count);
+ assert(result == out + 1);
+ assert(count == 2);
+
+ std::destroy(out, out + 1);
+ alloc.deallocate(out, 2);
+ }
+
+ // Works with const pointers.
+ {
+ int x = 1;
+ const int* ptr = &x;
+
+ const int* result = std::ranges::construct_at(ptr, 42);
+ assert(result == ptr);
+ assert(x == 42);
+ }
+
+ return true;
+}
+
+constexpr bool can_construct_at(auto&&... args)
+ requires requires { std::ranges::construct_at(decltype(args)(args)...); }
+ { return true; }
+
+constexpr bool can_construct_at(auto&&...) { return false; }
+
+// Check that SFINAE works.
+static_assert( can_construct_at((Foo*)nullptr, 1, 2));
+static_assert(!can_construct_at((Foo*)nullptr, 1));
+static_assert(!can_construct_at((Foo*)nullptr, 1, 2, 3));
+static_assert(!can_construct_at(nullptr, 1, 2));
+static_assert(!can_construct_at((int*)nullptr, 1, 2));
+static_assert(!can_construct_at(contiguous_iterator<Foo*>(), 1, 2));
+// Can't construct function pointers.
+static_assert(!can_construct_at((int(*)())nullptr));
+static_assert(!can_construct_at((int(*)())nullptr, nullptr));
+// TODO(varconst): check that array types work once D114649 implementing LWG3639 lands.
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ 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
index f67c76ed003a0..b655a234e9957 100644
--- 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
@@ -109,7 +109,7 @@ int main(int, char**) {
test_arrays();
static_assert(tests());
// TODO: Until std::construct_at has support for arrays, it's impossible to test this
- // in a constexpr context.
+ // in a constexpr context (see https://reviews.llvm.org/D114903).
// static_assert(test_arrays());
#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
index 8b56cb9cd1c1f..894b0554cb5fe 100644
--- 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
@@ -30,13 +30,12 @@ struct VirtualCounted {
int* counter_;
TEST_CONSTEXPR VirtualCounted(int* counter) : counter_(counter) { ++*counter_; }
TEST_CONSTEXPR_CXX20 virtual ~VirtualCounted() { --*counter_; }
- friend void operator&(VirtualCounted) = delete;
+ void operator&() const = delete;
};
struct DerivedCounted : VirtualCounted {
TEST_CONSTEXPR DerivedCounted(int* counter) : VirtualCounted(counter) { }
TEST_CONSTEXPR_CXX20 ~DerivedCounted() override { }
- friend void operator&(DerivedCounted) = delete;
};
#if TEST_STD_VER > 17
@@ -135,7 +134,7 @@ int main(int, char**) {
test_arrays();
static_assert(test());
// TODO: Until std::construct_at has support for arrays, it's impossible to test this
- // in a constexpr context.
+ // in a constexpr context (see https://reviews.llvm.org/D114903).
// static_assert(test_arrays());
#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
index 80421cddb0da7..4eb719a94e4fe 100644
--- 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
@@ -112,7 +112,7 @@ int main(int, char**) {
test_arrays();
static_assert(tests());
// TODO: Until std::construct_at has support for arrays, it's impossible to test this
- // in a constexpr context.
+ // in a constexpr context (see https://reviews.llvm.org/D114903).
// static_assert(test_arrays());
#endif
return 0;
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp
new file mode 100644
index 0000000000000..46e70458dd9dd
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp
@@ -0,0 +1,222 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+//
+// namespace ranges {
+// template<nothrow-input-iterator InputIterator, nothrow-sentinel-for<InputIterator> Sentinel>
+// requires destructible<iter_value_t<InputIterator>>
+// constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20
+// template<nothrow-input-range InputRange>
+// requires destructible<range_value_t<InputRange>>
+// constexpr borrowed_iterator_t<InputRange> destroy(InputRange&& range) noexcept; // since C++20
+// }
+
+#include <cassert>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// TODO(varconst): consolidate the ADL checks into a single file.
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy)>);
+
+struct NotNothrowDtrable {
+ ~NotNothrowDtrable() noexcept(false) {}
+};
+static_assert(!std::is_invocable_v<decltype(std::ranges::destroy), NotNothrowDtrable*, NotNothrowDtrable*>);
+
+struct Counted {
+ int& count;
+
+ constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
+ constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
+ constexpr ~Counted() { --count; }
+
+ friend void operator&(Counted) = delete;
+};
+
+template <class Iterator>
+constexpr void test() {
+ // (iterator + sentinel) overload.
+ {
+ constexpr int N = 5;
+ std::allocator<Counted> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Counted* out = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Traits::construct(alloc, out + i, counter);
+ }
+ assert(counter == N);
+
+ std::ranges::destroy(Iterator(out), Iterator(out + N));
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, out, N);
+ }
+
+ // (range) overload.
+ {
+ constexpr int N = 5;
+ std::allocator<Counted> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Counted* out = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Traits::construct(alloc, out + i, counter);
+ }
+ assert(counter == N);
+
+ auto range = std::ranges::subrange(Iterator(out), Iterator(out + N));
+ std::ranges::destroy(range);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, out, N);
+ }
+}
+
+constexpr bool tests() {
+ test<Counted*>();
+ test<forward_iterator<Counted*>>();
+
+ return true;
+}
+
+constexpr bool test_arrays() {
+ // One-dimensional array, (iterator + sentinel) overload.
+ {
+ constexpr int N = 5;
+ constexpr int M = 3;
+
+ using Array = Counted[M];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* buffer = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Array& array_ref = *(buffer + i);
+ for (int j = 0; j != M; ++j) {
+ Traits::construct(alloc, std::addressof(array_ref[j]), counter);
+ }
+ }
+ assert(counter == N * M);
+
+ std::ranges::destroy(buffer, buffer + N);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, N);
+ }
+
+ // One-dimensional array, (range) overload.
+ {
+ constexpr int N = 5;
+ constexpr int A = 3;
+
+ using Array = Counted[A];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* buffer = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Array& array_ref = *(buffer + i);
+ for (int j = 0; j != A; ++j) {
+ Traits::construct(alloc, std::addressof(array_ref[j]), counter);
+ }
+ }
+ assert(counter == N * A);
+
+ auto range = std::ranges::subrange(buffer, buffer + N);
+ std::ranges::destroy(range);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, N);
+ }
+
+ // Multidimensional array, (iterator + sentinel ) overload.
+ {
+ constexpr int N = 5;
+ constexpr int A = 3;
+ constexpr int B = 3;
+
+ using Array = Counted[A][B];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* buffer = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Array& array_ref = *(buffer + i);
+ for (int j = 0; j != A; ++j) {
+ for (int k = 0; k != B; ++k) {
+ Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
+ }
+ }
+ }
+ assert(counter == N * A * B);
+
+ std::ranges::destroy(buffer, buffer + N);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, N);
+ }
+
+ // Multidimensional array, (range) overload.
+ {
+ constexpr int N = 5;
+ constexpr int A = 3;
+ constexpr int B = 3;
+
+ using Array = Counted[A][B];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* buffer = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Array& array_ref = *(buffer + i);
+ for (int j = 0; j != A; ++j) {
+ for (int k = 0; k != B; ++k) {
+ Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
+ }
+ }
+ }
+ assert(counter == N * A * B);
+
+ std::ranges::destroy(buffer, buffer + N);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, N);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ test_arrays();
+
+ static_assert(tests());
+ // TODO: Until std::construct_at has support for arrays, it's impossible to test this
+ // in a constexpr context (see https://reviews.llvm.org/D114903).
+ // static_assert(test_arrays());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp
new file mode 100644
index 0000000000000..3ab1d15d576e8
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp
@@ -0,0 +1,160 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+//
+// namespace ranges {
+// template<destructible T>
+// constexpr void destroy_at(T* location) noexcept; // since C++20
+// }
+
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// TODO(varconst): consolidate the ADL checks into a single file.
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy_at)>);
+
+struct NotNothrowDtrable {
+ ~NotNothrowDtrable() noexcept(false) {}
+};
+static_assert(!std::is_invocable_v<decltype(std::ranges::destroy_at), NotNothrowDtrable*>);
+
+struct Counted {
+ int& count;
+
+ constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
+ constexpr ~Counted() { --count; }
+
+ friend void operator&(Counted) = delete;
+};
+
+struct VirtualCountedBase {
+ int& count;
+
+ constexpr VirtualCountedBase(int& count_ref) : count(count_ref) { ++count; }
+ constexpr virtual ~VirtualCountedBase() { --count; }
+
+ void operator&() const = delete;
+};
+
+struct VirtualCountedDerived : VirtualCountedBase {
+ constexpr VirtualCountedDerived(int& count_ref) : VirtualCountedBase(count_ref) {}
+
+ // Without a definition, GCC gives an error when the destructor is invoked in a constexpr context (see
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93413).
+ constexpr ~VirtualCountedDerived() override {}
+};
+
+constexpr bool test() {
+ // Destroying a "trivial" object.
+ {
+ std::allocator<Counted> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Counted* buffer = Traits::allocate(alloc, 2);
+ Traits::construct(alloc, buffer, counter);
+ Traits::construct(alloc, buffer + 1, counter);
+ assert(counter == 2);
+
+ std::ranges::destroy_at(buffer);
+ assert(counter == 1);
+ std::ranges::destroy_at(buffer + 1);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, 2);
+ }
+
+ // Destroying a derived object with a virtual destructor.
+ {
+ std::allocator<VirtualCountedDerived> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ VirtualCountedDerived* buffer = Traits::allocate(alloc, 2);
+ Traits::construct(alloc, buffer, counter);
+ Traits::construct(alloc, buffer + 1, counter);
+ assert(counter == 2);
+
+ std::ranges::destroy_at(buffer);
+ assert(counter == 1);
+ std::ranges::destroy_at(buffer + 1);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, 2);
+ }
+
+ return true;
+}
+
+constexpr bool test_arrays() {
+ // Pointer to an array.
+ {
+ using Array = Counted[3];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* array = Traits::allocate(alloc, 1);
+ Array& array_ref = *array;
+ for (int i = 0; i != 3; ++i) {
+ Traits::construct(alloc, std::addressof(array_ref[i]), counter);
+ }
+ assert(counter == 3);
+
+ std::ranges::destroy_at(array);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, array, 1);
+ }
+
+ // Pointer to a two-dimensional array.
+ {
+ using Array = Counted[3][2];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* array = Traits::allocate(alloc, 1);
+ Array& array_ref = *array;
+ for (int i = 0; i != 3; ++i) {
+ for (int j = 0; j != 2; ++j) {
+ Traits::construct(alloc, std::addressof(array_ref[i][j]), counter);
+ }
+ }
+ assert(counter == 3 * 2);
+
+ std::ranges::destroy_at(array);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, array, 1);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ test_arrays();
+
+ static_assert(test());
+ // TODO: Until std::construct_at has support for arrays, it's impossible to test this
+ // in a constexpr context (see https://reviews.llvm.org/D114903).
+ // static_assert(test_arrays());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp
new file mode 100644
index 0000000000000..13e3e7a639f21
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_n.pass.cpp
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+//
+// namespace ranges {
+// template<nothrow-input-iterator InputIterator>
+// requires destructible<iter_value_t<InputIterator>>
+// constexpr InputIterator destroy_n(InputIterator first, iter_
diff erence_t<InputIterator> n) noexcept; // since C++20
+// }
+
+#include <cassert>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// TODO(varconst): consolidate the ADL checks into a single file.
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy_n)>);
+
+struct NotNothrowDtrable {
+ ~NotNothrowDtrable() noexcept(false) {}
+};
+static_assert(!std::is_invocable_v<decltype(std::ranges::destroy_n), NotNothrowDtrable*, int>);
+
+struct Counted {
+ int& count;
+
+ constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
+ constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
+ constexpr ~Counted() { --count; }
+
+ friend void operator&(Counted) = delete;
+};
+
+template <class Iterator>
+constexpr void test() {
+ {
+ constexpr int N = 5;
+ std::allocator<Counted> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Counted* out = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Traits::construct(alloc, out + i, counter);
+ }
+ assert(counter == N);
+
+ std::ranges::destroy_n(Iterator(out), N);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, out, N);
+ }
+}
+
+constexpr bool tests() {
+ test<Counted*>();
+ test<forward_iterator<Counted*>>();
+
+ return true;
+}
+
+constexpr bool test_arrays() {
+ // One-dimensional array.
+ {
+ constexpr int N = 5;
+ constexpr int M = 3;
+
+ using Array = Counted[M];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* buffer = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Array& array_ref = *(buffer + i);
+ for (int j = 0; j != M; ++j) {
+ Traits::construct(alloc, std::addressof(array_ref[j]), counter);
+ }
+ }
+ assert(counter == N * M);
+
+ std::ranges::destroy_n(buffer, N);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, N);
+ }
+
+ // Multidimensional array.
+ {
+ constexpr int N = 5;
+ constexpr int A = 3;
+ constexpr int B = 3;
+
+ using Array = Counted[A][B];
+ std::allocator<Array> alloc;
+ using Traits = std::allocator_traits<decltype(alloc)>;
+ int counter = 0;
+
+ Array* buffer = Traits::allocate(alloc, N);
+ for (int i = 0; i != N; ++i) {
+ Array& array_ref = *(buffer + i);
+ for (int j = 0; j != A; ++j) {
+ for (int k = 0; k != B; ++k) {
+ Traits::construct(alloc, std::addressof(array_ref[j][k]), counter);
+ }
+ }
+ }
+ assert(counter == N * A * B);
+
+ std::ranges::destroy_n(buffer, N);
+ assert(counter == 0);
+
+ Traits::deallocate(alloc, buffer, N);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ test_arrays();
+
+ static_assert(tests());
+ // TODO: Until std::construct_at has support for arrays, it's impossible to test this
+ // in a constexpr context (see https://reviews.llvm.org/D114903).
+ // static_assert(test_arrays());
+
+ return 0;
+}
More information about the libcxx-commits
mailing list