diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -140,6 +140,7 @@ __ranges/access.h __ranges/all.h __ranges/concepts.h + __ranges/copyable_box.h __ranges/data.h __ranges/empty.h __ranges/empty_view.h diff --git a/libcxx/include/__ranges/copyable_box.h b/libcxx/include/__ranges/copyable_box.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/copyable_box.h @@ -0,0 +1,94 @@ +// -*- 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_COPYABLE_BOX_H +#define _LIBCPP___RANGES_COPYABLE_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 __copyable_box : public optional<_Tp> { + public: + using optional<_Tp>::optional; + + __copyable_box(__copyable_box&&) = default; + __copyable_box(const __copyable_box&) = default; + __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default; + __copyable_box& operator=(const __copyable_box&) requires copyable<_Tp> = default; + + constexpr __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) + requires default_initializable<_Tp> + : optional<_Tp>{in_place} // braces intentional + {} + + constexpr __copyable_box& operator=(const __copyable_box& __other) + noexcept(is_nothrow_copy_constructible_v<_Tp>) + { + if (this == _VSTD::addressof(__other)) { + return *this; + } + + if (__other) { + optional<_Tp>::emplace(*__other); + } + else { + optional<_Tp>::reset(); + } + return *this; + } + + constexpr __copyable_box& operator=(__copyable_box&& __other) + noexcept(is_nothrow_move_constructible_v<_Tp>) + { + if (this == _VSTD::addressof(__other)) { + return *this; + } + + if (__other) { + optional<_Tp>::emplace(_VSTD::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_COPYABLE_BOX_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -554,18 +554,19 @@ export * module __ranges { - module access { header "__ranges/access.h" } - module all { header "__ranges/all.h" } - module concepts { header "__ranges/concepts.h" } - module data { header "__ranges/data.h" } - module empty { header "__ranges/empty.h" } - module empty_view { header "__ranges/empty_view.h" } + module access { header "__ranges/access.h" } + module all { header "__ranges/all.h" } + module concepts { header "__ranges/concepts.h" } + module copyable_box { header "__ranges/copyable_box.h" export optional } + module data { header "__ranges/data.h" } + module empty { header "__ranges/empty.h" } + module empty_view { header "__ranges/empty_view.h" } module enable_borrowed_range { header "__ranges/enable_borrowed_range.h" } - module enable_view { header "__ranges/enable_view.h" } - module ref_view { header "__ranges/ref_view.h" } - module size { header "__ranges/size.h" } - module subrange { header "__ranges/subrange.h" } - module view_interface { header "__ranges/view_interface.h" } + module enable_view { header "__ranges/enable_view.h" } + module ref_view { header "__ranges/ref_view.h" } + module size { header "__ranges/size.h" } + module subrange { header "__ranges/subrange.h" } + module view_interface { header "__ranges/view_interface.h" } } } module ratio { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/base.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/base.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/base.compile.pass.cpp @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __copyable_box& operator=(__copyable_box const&) + +#include <__ranges/copyable_box.h> + +#include +#include + +static_assert(std::derived_from, std::optional>); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/copy_assign.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/copy_assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/copy_assign.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __copyable_box& operator=(__copyable_box const&) + +#include <__ranges/copyable_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::__copyable_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; +} + +constexpr bool check_self_assign() { + using box_t = std::ranges::__copyable_box; + + auto x = box_t(std::in_place, 5); +#pragma GCC diagnostic ignored "-Wself-assign-overloaded" + x = x; + assert(x.has_value()); + assert(x.value() == 5); + 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>(); + + static_assert(check_self_assign()); + check_self_assign(); +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/default_initializable.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/default_initializable.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.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 + +// __copyable_box::__copyable_box() + +#include <__ranges/copyable_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::__copyable_box; + +template +constexpr bool check_default_initialization() { + static_assert(!std::is_trivially_default_constructible_v<__copyable_box >); + static_assert(std::is_nothrow_default_constructible_v<__copyable_box >); + __copyable_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<__copyable_box >); + static_assert(std::is_nothrow_default_constructible_v<__copyable_box >); + __copyable_box const boxed; + assert(!boxed.has_value()); + return true; +} + +int main(int, char**) { + static_assert(std::default_initializable<__copyable_box >); + static_assert(!std::is_trivially_default_constructible_v<__copyable_box >); + static_assert(std::is_nothrow_default_constructible_v<__copyable_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.copy.wrap/move_assign.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/move_assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/move_assign.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// __copyable_box& operator=(__copyable_box&&) + +#include <__ranges/copyable_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::__copyable_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; +} + +constexpr bool check_self_assign() { + using box_t = std::ranges::__copyable_box; + + auto x = box_t(std::in_place, 5); +#pragma GCC diagnostic ignored "-Wself-move" + x = std::move(x); + assert(x.has_value()); + assert(x.value() == 5); + 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>(); + + static_assert(check_self_assign()); + check_self_assign(); +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.wrap/properties.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.copy.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 + +// __copyable_box + +#include <__ranges/copyable_box.h> + +#include +#include + +static_assert(std::derived_from, std::optional >); + +template +constexpr bool valid_copyable_box = requires { + typename std::ranges::__copyable_box; +}; + +static_assert(!valid_copyable_box); +static_assert(!valid_copyable_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_copyable_box);