[libcxx-commits] [libcxx] [libc++] Cooperation between `std::optional` and other standard types (PR #93672)
Mital Ashok via libcxx-commits
libcxx-commits at lists.llvm.org
Wed May 29 05:40:16 PDT 2024
https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/93672
>From b063ea3a65d1674dcad639194c052668bc3b2fd3 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sat, 25 May 2024 07:57:09 +0100
Subject: [PATCH] [libc++] Cooperation between std::optional and other standard
types
As a proof of concept, support std::optional<std::reference_wrapper<T>> and make it have a similar ABI to T*.
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__configuration/abi.h | 3 +
.../include/__functional/reference_wrapper.h | 30 ++++
libcxx/include/__optional/cooperate.h | 60 +++++++
libcxx/include/module.modulemap | 2 +
libcxx/include/optional | 155 +++++++++++++++++-
.../optional.object.observe/bool.pass.cpp | 48 ++++++
.../dereference.pass.cpp | 44 +++++
.../optional.object.swap/swap.pass.cpp | 109 ++++++++++++
.../optional.object/triviality.pass.cpp | 72 ++++++++
.../cooperate/optional.object/types.pass.cpp | 32 ++++
.../optional/cooperate/optional_size.pass.cpp | 43 +++++
12 files changed, 593 insertions(+), 6 deletions(-)
create mode 100644 libcxx/include/__optional/cooperate.h
create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cfe1f44777bca..f0310e30f2c10 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -572,6 +572,7 @@ set(files
__numeric/transform_exclusive_scan.h
__numeric/transform_inclusive_scan.h
__numeric/transform_reduce.h
+ __optional/cooperate.h
__ostream/basic_ostream.h
__ostream/print.h
__pstl/backends/libdispatch.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index c570b9aaba48d..dbd66c4ef0f68 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -89,6 +89,9 @@
// requires code not to make these assumptions.
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
# define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
+// Cooperative definition of std::optional<T> for some standard types T
+// such that std::optional<T> holds a T and nothing else
+# define _LIBCPP_ABI_COOPERATIVE_OPTIONAL 1
#elif _LIBCPP_ABI_VERSION == 1
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
// Enable compiling copies of now inline methods into the dylib to support
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index ab5d7c7cee115..3bfdb5951c520 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -16,6 +16,7 @@
#include <__functional/invoke.h>
#include <__functional/weak_result_type.h>
#include <__memory/addressof.h>
+#include <__optional/cooperate.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_const.h>
#include <__type_traits/remove_cvref.h>
@@ -115,6 +116,14 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
}
#endif // _LIBCPP_STD_VER >= 26
+
+#if _LIBCPP_STD_VER >= 17
+ template <class>
+ friend struct __optional::__cooperate;
+
+ constexpr explicit reference_wrapper(__optional::__disengaged_construct, __optional::__disengaged_construct) noexcept
+ : __f_(nullptr) {}
+#endif
};
#if _LIBCPP_STD_VER >= 17
@@ -149,6 +158,27 @@ void ref(const _Tp&&) = delete;
template <class _Tp>
void cref(const _Tp&&) = delete;
+#if _LIBCPP_STD_VER >= 17
+
+template <class _Tp>
+struct __optional::__cooperate<reference_wrapper<_Tp>> {
+ static constexpr bool __do_cooperate() { return true; }
+
+ static constexpr bool __is_engaged(const reference_wrapper<_Tp>& __v) { return __v.__f_ != nullptr; }
+
+ template <class... _Args>
+ static constexpr void __construct_over(reference_wrapper<_Tp>& __v, _Args&&... __args) {
+ __v = reference_wrapper<_Tp>(std::forward<_Args>(__args)...);
+ }
+
+ template <class _Up>
+ static constexpr void __disengage(reference_wrapper<_Tp>& __v) {
+ __v.__f_ = nullptr;
+ }
+};
+
+#endif // _LIBCPP_STD_VER >= 17
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
diff --git a/libcxx/include/__optional/cooperate.h b/libcxx/include/__optional/cooperate.h
new file mode 100644
index 0000000000000..fd1b09b3cfb96
--- /dev/null
+++ b/libcxx/include/__optional/cooperate.h
@@ -0,0 +1,60 @@
+// -*- 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___OPTIONAL_COOPERATE_H
+#define _LIBCPP___OPTIONAL_COOPERATE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 17
+
+namespace __optional {
+
+struct __disengaged_construct {
+ constexpr explicit __disengaged_construct() = default;
+# if _LIBCPP_STD_VER < 20
+
+private:
+ explicit __disengaged_construct(int, int) {} // This is not an aggregate
+# endif
+};
+
+/// To cooperate with std::optional<T>, specialize std::__optional::__cooperate and implement the member functions as
+/// documented. Must be constructible from _Tp(__disengaged_construct, __disengaged_construct) to be in a "disengaged"
+/// state. An object in a disengaged state never has its destructor called.
+template <class _Tp>
+struct __cooperate {
+ // Return "true" if the cooperated layout should be used
+ static constexpr bool __do_cooperate() { return false; }
+
+ // Return if __v is not in a disengaged state
+ static constexpr bool __is_engaged(const _Tp& /*__v*/) { return false; }
+
+ // Given a __v where !__is_engaged(__v), act as if destroy_at(addressof(__v)); construct_at(addressof(__v), __args...)
+ template <class... _Args>
+ static constexpr void __construct_over(_Tp& /*__v*/, _Args&&... /*__args*/) {}
+
+ // Given a __v, make it so !__is_engaged(__v). __v may already be disengaged
+ template <class _Up>
+ static constexpr void __disengage(_Tp& /*__v*/) {}
+};
+
+} // namespace __optional
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___OPTIONAL_COOPERATE_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 48391b2a12095..fdaf368f483fa 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1580,6 +1580,8 @@ module std_private_numeric_transform_exclusive_scan [system] { header "__numeric
module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
module std_private_numeric_transform_reduce [system] { header "__numeric/transform_reduce.h" }
+module std_private_optional_cooperate [system] { header "__optional/cooperate.h" }
+
module std_private_pstl_backends_libdispatch [system] { header "__pstl/backends/libdispatch.h" }
module std_private_pstl_backends_serial [system] { header "__pstl/backends/serial.h" }
module std_private_pstl_backends_std_thread [system] { header "__pstl/backends/std_thread.h" }
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 622e150f7a9f7..81e53ea0dbbc0 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -189,6 +189,7 @@ namespace std {
#include <__fwd/functional.h>
#include <__memory/addressof.h>
#include <__memory/construct_at.h>
+#include <__optional/cooperate.h>
#include <__tuple/sfinae_helpers.h>
#include <__type_traits/add_pointer.h>
#include <__type_traits/conditional.h>
@@ -274,11 +275,20 @@ inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secre
struct __optional_construct_from_invoke_tag {};
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp, bool = is_trivially_destructible<_Tp>::value, bool = __optional::__cooperate<_Tp>::__do_cooperate()>
+#else
template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
struct __optional_destruct_base;
+#endif
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp, bool _Cooperate>
+struct __optional_destruct_base<_Tp, false, false> {
+#else
template <class _Tp>
struct __optional_destruct_base<_Tp, false> {
+#endif
typedef _Tp value_type;
static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
union {
@@ -311,10 +321,17 @@ struct __optional_destruct_base<_Tp, false> {
__engaged_ = false;
}
}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
};
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true, false> {
+#else
template <class _Tp>
struct __optional_destruct_base<_Tp, true> {
+#endif
typedef _Tp value_type;
static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
union {
@@ -341,15 +358,126 @@ struct __optional_destruct_base<_Tp, true> {
__engaged_ = false;
}
}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
};
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp>
+struct __optional_destruct_base<_Tp, false, true> {
+ using value_type = _Tp;
+ static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+ union {
+ value_type __val_;
+ value_type __disengaged_state_;
+ };
+
+#if __has_builtin(__builtin_is_within_lifetime)
+ _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __disengaged_state_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __disengaged_state_; }
+#else
+ _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __val_; }
+#endif
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
+ if (has_value())
+ __val_.~value_type();
+ }
+
+#if __has_builtin(__builtin_is_within_lifetime)
+ _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __disengaged_state_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#else
+ _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __val_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#endif
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+ : __val_(std::forward<_Args>(__args)...) {}
+
+# if _LIBCPP_STD_VER >= 23
+ template <class _Fp, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+ __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+ : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)) {}
+# endif
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+ if (has_value()) {
+ __val_.~value_type();
+ std::construct_at(std::addressof(__get_disengaged()), __optional::__disengaged_construct(), __optional::__disengaged_construct());
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept {
+#if __has_builtin(__builtin_is_within_lifetime)
+ if consteval {
+ return __builtin_is_within_lifetime(__val_);
+ }
+#endif
+ return __optional::__cooperate<_Tp>::__is_engaged(*std::launder(std::addressof(__val_)));
+ }
+};
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true, true> {
+ using value_type = _Tp;
+ static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+ union {
+ value_type __disengaged_val_;
+ value_type __val_;
+ };
+
+#if __has_builtin(__builtin_is_within_lifetime)
+ _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __disengaged_state_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __disengaged_state_; }
+#else
+ _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __val_; }
+#endif
+
+#if __has_builtin(__builtin_is_within_lifetime)
+ _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __disengaged_state_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#else
+ _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __val_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#endif
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+ : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+# if _LIBCPP_STD_VER >= 23
+ template <class _Fp, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+ __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+ : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+# endif
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+ if (has_value()) {
+ __val_.~value_type();
+ std::construct_at(std::addressof(__get_disengaged()), __optional::__disengaged_construct(), __optional::__disengaged_construct());
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept {
+#if __has_builtin(__builtin_is_within_lifetime)
+ if consteval {
+ return __builtin_is_within_lifetime(__val_);
+ }
+#endif
+ return __optional::__cooperate<_Tp>::__is_engaged(*std::launder(std::addressof(__val_)));
+ }
+};
+#endif
+
template <class _Tp, bool = is_reference<_Tp>::value>
struct __optional_storage_base : __optional_destruct_base<_Tp> {
using __base = __optional_destruct_base<_Tp>;
using value_type = _Tp;
using __base::__base;
- _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __base::has_value(); }
_LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
_LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
@@ -359,8 +487,23 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
_LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
- std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
- this->__engaged_ = true;
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+ if constexpr (__optional::__cooperate<_Tp>::__do_cooperate()) {
+#if __has_builtin(__builtin_is_within_lifetime)
+ if consteval {
+ // No operations are ever done on a disengaged value. Ensure it is not destroyed.
+ std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+ return;
+ }
+#endif
+ __optional::__cooperate<_Tp>::__construct_over(*std::launder(std::addressof(this->__val_)), std::forward<_Args>(__args)...);
+ } else {
+#endif
+ std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+ this->__engaged_ = true;
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+ }
+#endif
}
template <class _That>
@@ -371,11 +514,11 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
template <class _That>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
- if (this->__engaged_ == __opt.has_value()) {
- if (this->__engaged_)
+ if (this->has_value() == __opt.has_value()) {
+ if (this->has_value())
this->__val_ = std::forward<_That>(__opt).__get();
} else {
- if (this->__engaged_)
+ if (this->has_value())
this->reset();
else
__construct(std::forward<_That>(__opt).__get());
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp
new file mode 100644
index 0000000000000..f03deda22bcc0
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// <optional>
+
+// constexpr explicit optional<T>::operator bool() const noexcept;
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <typename T, typename F>
+constexpr bool test(F init) {
+ {
+ const optional<T> opt;
+ (void)opt;
+ ASSERT_NOEXCEPT(bool(opt));
+ static_assert(!std::is_convertible_v<optional<T>, bool>);
+ }
+ {
+ constexpr optional<T> opt;
+ static_assert(!opt);
+ }
+ {
+ constexpr optional<T> opt(init());
+ static_assert(opt);
+ }
+ return true;
+}
+
+int f() { return 0; }
+
+int main(int, char**) {
+ static int i;
+ test<std::reference_wrapper<int>>([]() -> auto& { return i; });
+ test<std::reference_wrapper<int()>>([]() -> auto& { return f; });
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp
new file mode 100644
index 0000000000000..d0ffc216ab4e5
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// <optional>
+
+// constexpr T& optional<T>::operator*() &;
+
+#include <optional>
+#include <type_traits>
+#include <functional>
+#include <cassert>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <typename T, typename G>
+constexpr auto test(T init, G test) {
+ {
+ optional<T> opt;
+ (void)opt;
+ ASSERT_SAME_TYPE(decltype(*opt), T&);
+ LIBCPP_STATIC_ASSERT(noexcept(*opt));
+ }
+
+ optional<T> opt(init);
+ return test(*opt);
+}
+
+int main(int, char**) {
+ {
+ static int i;
+ assert(test<std::reference_wrapper<int>>(i, [](std::reference_wrapper<int>& r) { return &r.get(); }) == &i);
+#if TEST_STD_VER > 17
+ static_assert(test<std::reference_wrapper<int>>(i, [](std::reference_wrapper<int>& r) { return &r.get(); }) == &i);
+#endif
+ }
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp
new file mode 100644
index 0000000000000..c056ebbe9ba4f
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.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
+// <optional>
+
+// void swap(optional&)
+// noexcept(is_nothrow_move_constructible<T>::value &&
+// is_nothrow_swappable<T>::value)
+
+#include <optional>
+#include <type_traits>
+#include <functional>
+#include <cassert>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <typename T>
+struct equal_to {
+ constexpr bool operator()(T& l, T& r) const noexcept { return l == r; }
+};
+
+template <typename T>
+struct equal_to<std::reference_wrapper<T>> {
+ constexpr bool operator()(std::reference_wrapper<T>& l, std::reference_wrapper<T>& r) const noexcept {
+ return std::addressof(l.get()) == std::addressof(r.get());
+ }
+};
+
+template <class T>
+TEST_CONSTEXPR_CXX20 bool check_swap(T left, T right) {
+ constexpr equal_to<T> is_equal;
+ {
+ optional<T> opt1;
+ optional<T> opt2;
+ static_assert(
+ noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+ assert(static_cast<bool>(opt1) == false);
+ assert(static_cast<bool>(opt2) == false);
+ opt1.swap(opt2);
+ assert(static_cast<bool>(opt1) == false);
+ assert(static_cast<bool>(opt2) == false);
+ }
+ {
+ optional<T> opt1(left);
+ optional<T> opt2;
+ static_assert(
+ noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+ assert(static_cast<bool>(opt1) == true);
+ assert(is_equal(*opt1, left));
+ assert(static_cast<bool>(opt2) == false);
+ opt1.swap(opt2);
+ assert(static_cast<bool>(opt1) == false);
+ assert(static_cast<bool>(opt2) == true);
+ assert(is_equal(*opt2, left));
+ }
+ {
+ optional<T> opt1;
+ optional<T> opt2(right);
+ static_assert(
+ noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+ assert(static_cast<bool>(opt1) == false);
+ assert(static_cast<bool>(opt2) == true);
+ assert(is_equal(*opt2, right));
+ opt1.swap(opt2);
+ assert(static_cast<bool>(opt1) == true);
+ assert(is_equal(*opt1, right));
+ assert(static_cast<bool>(opt2) == false);
+ }
+ {
+ optional<T> opt1(left);
+ optional<T> opt2(right);
+ static_assert(
+ noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+ assert(static_cast<bool>(opt1) == true);
+ assert(is_equal(*opt1, left));
+ assert(static_cast<bool>(opt2) == true);
+ assert(is_equal(*opt2, right));
+ opt1.swap(opt2);
+ assert(static_cast<bool>(opt1) == true);
+ assert(is_equal(*opt1, right));
+ assert(static_cast<bool>(opt2) == true);
+ assert(is_equal(*opt2, left));
+ }
+ return true;
+}
+
+int f() noexcept { return 0; }
+int g() noexcept { return 0; }
+
+int main(int, char**) {
+ int i, j;
+ check_swap<std::reference_wrapper<int>>(i, j);
+ check_swap<std::reference_wrapper<const int>>(i, j);
+ check_swap<std::reference_wrapper<int()>>(f, g);
+ check_swap<std::reference_wrapper<int() noexcept>>(f, g);
+#if TEST_STD_VER > 17
+ static int ii, jj;
+ static_assert(check_swap<std::reference_wrapper<const int>>(ii, jj));
+ static_assert(check_swap<std::reference_wrapper<int()>>(f, g));
+#endif
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp
new file mode 100644
index 0000000000000..5b18e0fbc026a
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <optional>
+
+// The following special member functions should propagate the triviality of
+// the element held in the optional (see P0602R4):
+//
+// constexpr optional(const optional& rhs);
+// constexpr optional(optional&& rhs) noexcept(see below);
+// constexpr optional<T>& operator=(const optional& rhs);
+// constexpr optional<T>& operator=(optional&& rhs) noexcept(see below);
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+
+#include "archetypes.h"
+
+#include "test_macros.h"
+
+constexpr bool implies(bool p, bool q) { return !p || q; }
+
+template <class T>
+struct SpecialMemberTest {
+ using O = std::optional<T>;
+
+ static_assert(implies(std::is_trivially_copy_constructible_v<T>, std::is_trivially_copy_constructible_v<O>),
+ "optional<T> is trivially copy constructible if T is trivially copy constructible.");
+
+ static_assert(implies(std::is_trivially_move_constructible_v<T>, std::is_trivially_move_constructible_v<O>),
+ "optional<T> is trivially move constructible if T is trivially move constructible");
+
+ static_assert(implies(std::is_trivially_copy_constructible_v<T> && std::is_trivially_copy_assignable_v<T> &&
+ std::is_trivially_destructible_v<T>,
+
+ std::is_trivially_copy_assignable_v<O>),
+ "optional<T> is trivially copy assignable if T is "
+ "trivially copy constructible, "
+ "trivially copy assignable, and "
+ "trivially destructible");
+
+ static_assert(implies(std::is_trivially_move_constructible_v<T> && std::is_trivially_move_assignable_v<T> &&
+ std::is_trivially_destructible_v<T>,
+
+ std::is_trivially_move_assignable_v<O>),
+ "optional<T> is trivially move assignable if T is "
+ "trivially move constructible, "
+ "trivially move assignable, and"
+ "trivially destructible.");
+};
+
+template <class... Args>
+static void sink(Args&&...) {}
+
+template <class... TestTypes>
+struct DoTestsMetafunction {
+ DoTestsMetafunction() { sink(SpecialMemberTest<TestTypes>{}...); }
+};
+
+int main(int, char**) {
+ DoTestsMetafunction< std::reference_wrapper<int>,
+ std::reference_wrapper<int()>,
+ std::reference_wrapper<int() noexcept> >{};
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp
new file mode 100644
index 0000000000000..d012e9157f3fb
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// <optional>
+
+// template <class T>
+// class optional
+// {
+// public:
+// typedef T value_type;
+// ...
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <class Opt, class T>
+void test() {
+ static_assert(std::is_same<typename Opt::value_type, T>::value, "");
+}
+
+int main(int, char**) { test<optional<std::reference_wrapper<int>>, std::reference_wrapper<int>>(); }
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp
new file mode 100644
index 0000000000000..786187ac53a9b
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <optional>
+
+// template <class T> class optional;
+
+#include <optional>
+#include <functional>
+
+template <class T>
+struct type_with_bool {
+ T value;
+ bool has_value;
+};
+
+template <class T>
+using cooperative_type =
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+ T;
+#else
+ type_with_bool<T>;
+#endif
+
+int main(int, char**) {
+ static_assert(
+ sizeof(std::optional<std::reference_wrapper<int>>) == sizeof(cooperative_type<std::reference_wrapper<int>>));
+ static_assert(sizeof(std::optional<const std::reference_wrapper<int>>) ==
+ sizeof(type_with_bool<const std::reference_wrapper<int>>));
+ static_assert(
+ sizeof(std::optional<std::reference_wrapper<int()>>) == sizeof(cooperative_type<std::reference_wrapper<int()>>));
+ static_assert(sizeof(std::optional<const std::reference_wrapper<int()>>) ==
+ sizeof(type_with_bool<const std::reference_wrapper<int()>>));
+
+ return 0;
+}
More information about the libcxx-commits
mailing list