diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -38,6 +38,7 @@ __ranges/access.h __ranges/concepts.h __ranges/enable_borrowed_range.h + __ranges/semiregular_box.h __ranges/view.h __ranges/size.h __split_buffer diff --git a/libcxx/include/__ranges/semiregular_box.h b/libcxx/include/__ranges/semiregular_box.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/semiregular_box.h @@ -0,0 +1,86 @@ +// -*- 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___RANGES_SEMIREGULAR_BOX_H +#define _LIBCPP___RANGES_SEMIREGULAR_BOX_H + +// These customization variables are used in and . The +// separate header is used to avoid including the entire header in +// and . + +#include <__config> +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_RANGES) + +// clang-format off + +namespace ranges { + template + requires is_object_v<_Tp> + class __semiregular_box : public optional<_Tp> { + public: + using optional<_Tp>::optional; + __semiregular_box() = default; + __semiregular_box(__semiregular_box&&) = default; + __semiregular_box(const __semiregular_box&) = default; + __semiregular_box& operator=(__semiregular_box&&) requires std::movable<_Tp> = default; + __semiregular_box& operator=(const __semiregular_box&) requires std::copyable<_Tp> = default; + + constexpr __semiregular_box() noexcept(is_nothrow_default_constructible_v<_Tp>) + requires default_initializable<_Tp> + : optional<_Tp>{in_place} // braces intentional + {} + + constexpr __semiregular_box& operator=(const __semiregular_box& other) + noexcept(is_nothrow_copy_constructible_v<_Tp>) + { + if (other) { + optional<_Tp>::emplace(*other); + } + else { + optional<_Tp>::reset(); + } + return *this; + } + + constexpr __semiregular_box& operator=(__semiregular_box&& other) + noexcept(is_nothrow_move_constructible_v<_Tp>) + { + if (other) { + optional<_Tp>::emplace(std::move(*other)); + } + else { + optional<_Tp>::reset(); + } + return *this; + } + }; +} // namespace ranges + +// clang-format on + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_SEMIREGULAR_BOX_H diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/copy_assign.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/copy_assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/copy_assign.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __semiregular_box& operator=(__semiregular_box const&) + +#include <__ranges/semiregular_box.h> + +#include +#include +#include + +#include "test_macros.h" + +template +struct no_move_assign { + constexpr no_move_assign(int x) : value(x) {} + no_move_assign(no_move_assign&&) = default; + + constexpr no_move_assign(no_move_assign const& other) noexcept(nothrow) : value(other.value) {} + + no_move_assign& operator=(no_move_assign&&) = delete; + no_move_assign& operator=(no_move_assign const&) = default; + + int value; + constexpr bool operator==(int const x) const { return value == x; } +}; + +template +struct no_copy_assign { + constexpr no_copy_assign(int x) : value(x) {} + no_copy_assign(no_copy_assign&&) = default; + + constexpr no_copy_assign(no_copy_assign const& other) noexcept(nothrow) : value(other.value) {} + + no_copy_assign& operator=(no_copy_assign&&) = default; + no_copy_assign& operator=(no_copy_assign const&) = delete; + + int value; + constexpr bool operator==(int const x) const { return value == x; } +}; + +struct copyable { + constexpr copyable(int x) : value(x) {} + + int value; + constexpr bool operator==(int const x) const { return value == x; } +}; + +template +constexpr bool check_copy_assign() { + using box_t = std::ranges::__semiregular_box; + static_assert(std::is_copy_assignable_v); + static_assert(std::is_nothrow_copy_assignable_v == nothrow); + + auto x = box_t(std::in_place, 5); + auto y = box_t(std::in_place, 10); + x = y; + assert(x.has_value()); + assert(y.has_value()); + assert(x.value() == 10); + + y.reset(); + x = y; + assert(!x.has_value()); + return true; +} + +int main() { + static_assert(check_copy_assign()); + check_copy_assign(); + + static_assert(check_copy_assign()); + check_copy_assign(); + + static_assert(check_copy_assign >()); + check_copy_assign >(); + + static_assert(check_copy_assign, false>()); + check_copy_assign, false>(); + + static_assert(check_copy_assign >()); + check_copy_assign >(); + + static_assert(check_copy_assign, false>()); + check_copy_assign, false>(); +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/default_initializable.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/default_initializable.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/default_initializable.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __semiregular_box::__semiregular_box() + +#include <__ranges/semiregular_box.h> + +#include +#include +#include + +#include "test_macros.h" + +struct aggregate { + double value = 16.0; + constexpr friend bool operator==(aggregate const x, aggregate const y) { return x.value == y.value; } +}; + +struct not_default_initializable { + not_default_initializable() = delete; + not_default_initializable(int) {} +}; + +using std::ranges::__semiregular_box; + +template +constexpr bool check_default_initialization() { + static_assert(!std::is_trivially_default_constructible_v<__semiregular_box >); + static_assert(std::is_nothrow_default_constructible_v<__semiregular_box >); + __semiregular_box const boxed; + T const value; + assert(boxed.has_value()); + assert(*boxed == value); + return true; +} + +template +constexpr bool check_default_initialization() { + static_assert(!std::is_trivially_default_constructible_v<__semiregular_box >); + static_assert(std::is_nothrow_default_constructible_v<__semiregular_box >); + __semiregular_box const boxed; + assert(!boxed.has_value()); + return true; +} + +int main(int, char**) { + static_assert(std::default_initializable<__semiregular_box >); + static_assert(!std::is_trivially_default_constructible_v<__semiregular_box >); + static_assert(std::is_nothrow_default_constructible_v<__semiregular_box >); + + static_assert(check_default_initialization()); + check_default_initialization(); + + check_default_initialization >(); + + static_assert(check_default_initialization()); + check_default_initialization(); + + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/move_assign.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/move_assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/move_assign.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __semiregular_box& operator=(__semiregular_box&&) + +#include <__ranges/semiregular_box.h> + +#include +#include +#include +#include + +#include "test_macros.h" + +template +struct no_move_assign { + constexpr no_move_assign(int x) : value(x) {} + + constexpr no_move_assign(no_move_assign&& other) noexcept(nothrow) : value(std::exchange(other.value, 0)) {} + + no_move_assign(no_move_assign const&) = default; + no_move_assign& operator=(no_move_assign&&) = delete; + no_move_assign& operator=(no_move_assign const&) = default; + + int value; + constexpr bool operator==(int const x) const { return value == x; } +}; + +struct movable { + constexpr movable(int x) : value(x) {} + + int value; + constexpr bool operator==(int const x) const { return value == x; } +}; + +template +constexpr bool check_move_assign() { + using box_t = std::ranges::__semiregular_box; + static_assert(std::is_move_assignable_v); + static_assert(std::is_nothrow_move_assignable_v == nothrow_expected); + + auto x = box_t(std::in_place, 5); + auto y = box_t(std::in_place, 10); + x = std::move(y); + assert(x.has_value()); + assert(y.has_value()); + assert(x.value() == 10); + assert(std::is_trivially_move_assignable_v || y.value() == 0); + + y.reset(); + x = std::move(y); + assert(!x.has_value()); + return true; +} + +int main() { + static_assert(check_move_assign()); + check_move_assign(); + + static_assert(check_move_assign()); + check_move_assign(); + + static_assert(check_move_assign >()); + check_move_assign >(); + + static_assert(check_move_assign, false>()); + check_move_assign, false>(); +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/properties.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/properties.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.semi.wrap/properties.compile.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __semiregular_box + +#include <__ranges/semiregular_box.h> + +#include +#include + +static_assert(std::derived_from, std::optional >); + +template +constexpr bool valid_semiregular_box = requires { + typename std::ranges::__semiregular_box; +}; + +static_assert(!valid_semiregular_box); +static_assert(!valid_semiregular_box); + +struct not_copy_constructible { + not_copy_constructible() = default; + not_copy_constructible(not_copy_constructible&&) = default; + not_copy_constructible(not_copy_constructible const&) = delete; + not_copy_constructible& operator=(not_copy_constructible&&) = default; + not_copy_constructible& operator=(not_copy_constructible const&) = default; +}; + +static_assert(!valid_semiregular_box);