diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -12,14 +12,14 @@ | `iter_difference_t `_",,Christopher Di Bella,✅ `[iterator.traits] `_,`Updates to iterator_traits `_,"| indirectly_readable_traits | incrementable_traits",Christopher Di Bella,✅ -`[special.mem.concepts] `_,"| *no-throw-input-iterator* -| *no-throw-sentinel-for* -| *no-throw-input-range* -| *no-throw-forward-iterator* -| *no-throw-forward-range*","| [iterator.concepts] +`[special.mem.concepts] `_,"| *nothrow-input-iterator* +| *nothrow-sentinel-for* +| *nothrow-input-range* +| *nothrow-forward-iterator* +| *nothrow-forward-range*","| [iterator.concepts] | [range.refinements]",Konstantin Varlamov,✅ -`[specialized.algorithms] `_,"| ranges::uninitialized_default_construct -| ranges::uninitialized_default_construct_n +`[specialized.algorithms] `_,"| `ranges::uninitialized_default_construct() ` +| `ranges::uninitialized_default_construct_n() ` | ranges::uninitialized_value_construct | ranges::uninitialized_value_construct_n | ranges::uninitialized_copy @@ -31,7 +31,7 @@ | ranges::construct_at | ranges::destroy | ranges::destroy_at -| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,Not started +| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,In progress [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] `_,`ranges::iter_move `_,,Zoe Carver,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -231,6 +231,7 @@ __memory/concepts.h __memory/construct_at.h __memory/pointer_traits.h + __memory/ranges_uninitialized_algorithms.h __memory/raw_storage_iterator.h __memory/shared_ptr.h __memory/temporary_buffer.h diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h @@ -0,0 +1,80 @@ +// -*- 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_UNINITIALIZED_ALGORITHMS_H +#define _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H + +#include <__config> +#include <__function_like.h> +#include <__memory/concepts.h> +#include <__memory/uninitialized_algorithms.h> +#include <__ranges/dangling.h> +#include +#include + +#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 { + +// uninitialized_default_construct + +struct __uninitialized_default_construct_fn final : private __function_like { + constexpr explicit __uninitialized_default_construct_fn(__tag __x) noexcept : + __function_like(__x) {} + + template <__nothrow_forward_iterator _ForwardIterator, + __nothrow_sentinel_for<_ForwardIterator> _Sentinel> + requires default_initializable> + _ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last) const { + using _ValueType = remove_reference_t>; + return _VSTD::__uninitialized_default_construct<_ValueType>(__first, __last); + } + + template <__nothrow_forward_range _ForwardRange> + requires default_initializable> + borrowed_iterator_t<_ForwardRange> operator()(_ForwardRange &&__range) const { + return (*this)(ranges::begin(__range), ranges::end(__range)); + } + +}; + +inline constexpr auto uninitialized_default_construct = + __uninitialized_default_construct_fn(__function_like::__tag()); + +// uninitialized_default_construct_n + +struct __uninitialized_default_construct_n_fn final : private __function_like { + + constexpr explicit __uninitialized_default_construct_n_fn(__tag __x) noexcept : + __function_like(__x) {} + + template <__nothrow_forward_iterator _ForwardIterator> + requires default_initializable> + _ForwardIterator operator()(_ForwardIterator __first, + iter_difference_t<_ForwardIterator> __n) const { + using _ValueType = remove_reference_t>; + return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n); + } + +}; + +inline constexpr auto uninitialized_default_construct_n = + __uninitialized_default_construct_n_fn(__function_like::__tag()); + +} // namespace ranges +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -119,43 +119,61 @@ #if _LIBCPP_STD_VER > 14 -template -inline _LIBCPP_INLINE_VISIBILITY -void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) { - using _Vt = typename iterator_traits<_ForwardIterator>::value_type; +// uninitialized_default_construct + +template +inline _LIBCPP_HIDE_FROM_ABI +_ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) { auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { #endif for (; __idx != __last; ++__idx) - ::new ((void*)_VSTD::addressof(*__idx)) _Vt; + ::new ((void*)_VSTD::addressof(*__idx)) _ValueType; #ifndef _LIBCPP_NO_EXCEPTIONS } catch (...) { _VSTD::destroy(__first, __idx); throw; } #endif + + return __idx; } -template +template inline _LIBCPP_INLINE_VISIBILITY -_ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { - using _Vt = typename iterator_traits<_ForwardIterator>::value_type; +void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) { + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; + (void)_VSTD::__uninitialized_default_construct<_ValueType>(__first, __last); +} + +// uninitialized_default_construct_n + +template +inline _LIBCPP_INLINE_VISIBILITY +_ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { auto __idx = __first; #ifndef _LIBCPP_NO_EXCEPTIONS try { #endif for (; __n > 0; ++__idx, (void) --__n) - ::new ((void*)_VSTD::addressof(*__idx)) _Vt; - return __idx; + ::new ((void*)_VSTD::addressof(*__idx)) _ValueType; #ifndef _LIBCPP_NO_EXCEPTIONS } catch (...) { _VSTD::destroy(__first, __idx); throw; } #endif + + return __idx; } +template +inline _LIBCPP_INLINE_VISIBILITY +_ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) { + using _ValueType = typename iterator_traits<_ForwardIterator>::value_type; + return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n); +} template inline _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -219,9 +219,30 @@ template void uninitialized_default_construct(ForwardIterator first, ForwardIterator last); +template Sentinel> + requires default_initializable> + ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); // Since C++20 + +template + requires default_initializable> + borrowed_iterator_t ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); // Since C++20 + template ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Size n); +template + requires default_initializable> + ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, iter_difference_t n); // Since C++20 + +namespace ranges { + template + constexpr void advance(I& i, iter_difference_t n); // since C++20 + template S> + constexpr void advance(I& i, S bound); // since C++20 + template S> + constexpr iter_difference_t advance(I& i, iter_difference_t n, S bound); // since C++20 +} + template struct auto_ptr_ref {}; // deprecated in C++11, removed in C++17 template @@ -672,6 +693,7 @@ #include <__memory/concepts.h> #include <__memory/construct_at.h> #include <__memory/pointer_traits.h> +#include <__memory/ranges_uninitialized_algorithms.h> #include <__memory/raw_storage_iterator.h> #include <__memory/shared_ptr.h> #include <__memory/temporary_buffer.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -640,6 +640,7 @@ 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_uninitialized_algorithms { private header "__memory/ranges_uninitialized_algorithms.h" } module raw_storage_iterator { private header "__memory/raw_storage_iterator.h" } module shared_ptr { private header "__memory/shared_ptr.h" } module temporary_buffer { private header "__memory/temporary_buffer.h" } diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: gcc-10 + +// + +// template Sentinel> +// requires default_initializable> +// ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); +// +// template +// requires default_initializable> +// borrowed_iterator_t ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +namespace ranges = std::ranges; + +struct Counted { + static int current_objects; + static int total_objects; + static int throw_on; + + explicit Counted() { + if (throw_on == total_objects) { + TEST_THROW(1); + } + + ++current_objects; + ++total_objects; + } + + ~Counted() { --current_objects; } + + static void reset() { + current_objects = total_objects = 0; + throw_on = -1; + } + + Counted(Counted const&) = delete; + friend void operator&(Counted) = delete; +}; +int Counted::current_objects = 0; +int Counted::total_objects = 0; +int Counted::throw_on = -1; + +void test_empty() { + alignas(Counted) char buffer[sizeof(Counted) * 1] = {}; + auto* as_array = reinterpret_cast(buffer); + + ranges::uninitialized_default_construct(as_array, as_array); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); + + auto range = ranges::subrange(as_array, as_array); + ranges::uninitialized_default_construct(range); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); +} + +void test_several() { + constexpr int N = 5; + alignas(Counted) char buffer[sizeof(Counted) * N] = {}; + auto* as_array = reinterpret_cast(buffer); + + { + ranges::uninitialized_default_construct(as_array, as_array + N); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(as_array, as_array + N); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + Counted::reset(); + } + + { + auto range = ranges::subrange(as_array, as_array + N); + ranges::uninitialized_default_construct(range); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(as_array, as_array + N); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + Counted::reset(); + } +} + +void test_already_initialized() { + int array[] = {42, 42, 42}; + + ranges::uninitialized_default_construct(std::begin(array), std::end(array)); + assert(array[0] == 42); + assert(array[1] == 42); + assert(array[2] == 42); + + auto range = ranges::subrange(std::begin(array), std::end(array)); + ranges::uninitialized_default_construct(range); + assert(array[0] == 42); + assert(array[1] == 42); + assert(array[2] == 42); +} + +void test_exception_during_construction() { +#ifndef TEST_HAS_NO_EXCEPTIONS + constexpr int N = 5; + alignas(Counted) char buffer[sizeof(Counted) * N] = {}; + auto* as_array = reinterpret_cast(buffer); + + { + Counted::throw_on = 3; // When constructing the fourth object (counting from one). + try { + ranges::uninitialized_default_construct(as_array, as_array + N); + } catch(...) {} + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 3); + } + Counted::reset(); + + { + Counted::throw_on = 3; // When constructing the fourth object. + try { + auto range = ranges::subrange(as_array, as_array + N); + ranges::uninitialized_default_construct(range); + } catch(...) {} + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 3); + } + Counted::reset(); +#endif // TEST_HAS_NO_EXCEPTIONS +} + +int main() { + test_empty(); + test_several(); + test_already_initialized(); + test_exception_during_construction(); +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: gcc-10 + +// + +// template +// requires default_initializable> +// ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, +// iter_difference_t n); + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +namespace ranges = std::ranges; + +struct Counted { + static int current_objects; + static int total_objects; + static int throw_on; + + explicit Counted() { + if (throw_on == total_objects) { + TEST_THROW(1); + } + + ++current_objects; + ++total_objects; + } + + ~Counted() { --current_objects; } + + static void reset() { + current_objects = total_objects = 0; + throw_on = -1; + } + + Counted(Counted const&) = delete; + friend void operator&(Counted) = delete; +}; +int Counted::current_objects = 0; +int Counted::total_objects = 0; +int Counted::throw_on = -1; + +void test_empty() { + alignas(Counted) char buffer[sizeof(Counted) * 1] = {}; + auto* as_array = reinterpret_cast(buffer); + + ranges::uninitialized_default_construct_n(as_array, 0); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 0); +} + +void test_several() { + constexpr int N = 5; + alignas(Counted) char buffer[sizeof(Counted) * N] = {}; + auto* as_array = reinterpret_cast(buffer); + + ranges::uninitialized_default_construct_n(as_array, N); + assert(Counted::current_objects == N); + assert(Counted::total_objects == N); + + std::destroy(as_array, as_array + N); + assert(Counted::current_objects == 0); + assert(Counted::total_objects == N); + Counted::reset(); +} + +void test_already_initialized() { + int array[] = {42, 42, 42}; + + ranges::uninitialized_default_construct_n(std::begin(array), std::size(array)); + assert(array[0] == 42); + assert(array[1] == 42); + assert(array[2] == 42); +} + +void test_exception_during_construction() { +#ifndef TEST_HAS_NO_EXCEPTIONS + constexpr int N = 5; + alignas(Counted) char buffer[sizeof(Counted) * N] = {}; + auto* as_array = reinterpret_cast(buffer); + + { + Counted::throw_on = 3; // When constructing the fourth object (counting from one). + try { + ranges::uninitialized_default_construct_n(as_array, N); + } catch(...) {} + assert(Counted::current_objects == 0); + assert(Counted::total_objects == 3); + } + Counted::reset(); +#endif // TEST_HAS_NO_EXCEPTIONS +} + +int main() { + test_empty(); + test_several(); + test_already_initialized(); + test_exception_during_construction(); +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/special_function.compile.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/special_function.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/special_function.compile.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// ranges::uninitialized_default_construct + +#include + +#include "test_iterators.h" +#include "test_range.h" +#include "test_standard_function.h" + +static_assert(is_function_like()); +static_assert(is_function_like()); + +namespace found_by_adl { + +template +struct adl_forward_iterator : forward_iterator { + // To satisfy `weakly_incrementable`, the increment operators must return the same type as the + // class. + adl_forward_iterator& operator++(); + adl_forward_iterator operator++(int); +}; + +struct adl_forward_range : test_range { +}; + +void uninitialized_default_construct(adl_forward_iterator<>, adl_forward_iterator<>) = delete; +void uninitialized_default_construct(adl_forward_range) = delete; +void uninitialized_default_construct_n(adl_forward_iterator<>, size_t) = delete; + +} // namespace found_by_adl + +// When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a +// function call ([expr.call]), they inhibit argument-dependent name lookup. +void check_adl_is_inhibited() { + found_by_adl::adl_forward_iterator<> i; + found_by_adl::adl_forward_range r; + + using std::ranges::uninitialized_default_construct; + uninitialized_default_construct(i, i); + uninitialized_default_construct(r); + + using std::ranges::uninitialized_default_construct_n; + uninitialized_default_construct_n(i, 0); +} diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp --- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp +++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp @@ -23,7 +23,6 @@ struct Counted { static int count; static int constructed; - static void reset() { count = constructed = 0; } explicit Counted() { ++count; ++constructed; } Counted(Counted const&) { assert(false); } ~Counted() { --count; } @@ -37,7 +36,6 @@ static int count; static int constructed; static int throw_after; - static void reset() { throw_after = count = constructed = 0; } explicit ThrowsCounted() { ++constructed; if (throw_after > 0 && --throw_after == 0) { @@ -67,7 +65,7 @@ assert(false); } catch (...) {} assert(ThrowsCounted::count == 0); - assert(ThrowsCounted::constructed == 4); // forth construction throws + assert(ThrowsCounted::constructed == 4); // Fourth construction throws #endif }