diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -119,7 +119,7 @@ `3560 `__,"``ranges::equal`` and ``ranges::is_permutation`` should short-circuit for ``sized_ranges``","October 2021","","","|ranges|" `3561 `__,"Issue with internal counter in ``discard_block_engine``","October 2021","","" `3563 `__,"``keys_view`` example is broken","October 2021","","","|ranges|" -`3566 `__,"Constraint recursion for ``operator<=>(optional, U)``","October 2021","","","|spaceship|" +`3566 `__,"Constraint recursion for ``operator<=>(optional, U)``","October 2021","Complete","17.0","|spaceship|" `3567 `__,"Formatting move-only iterators take two","October 2021","|Complete|","16.0","|format| |ranges|" `3568 `__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","|Complete|","16.0","|ranges|" `3570 `__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","","" @@ -206,7 +206,7 @@ "`3738 `__","Missing preconditions for ``take_view`` constructor", "November 2022","|Complete|","16.0","|ranges|" "`3743 `__","``ranges::to``'s reserve may be ill-formed", "November 2022","","","|ranges|" "`3745 `__","``std::atomic_wait`` and its friends lack ``noexcept``", "November 2022","|Complete|","16.0","" -"`3746 `__","``optional``'s spaceship with ``U`` with a type derived from optional causes infinite constraint meta-recursion", "November 2022","","","|spaceship|" +"`3746 `__","``optional``'s spaceship with ``U`` with a type derived from optional causes infinite constraint meta-recursion", "November 2022","Complete","17.0","|spaceship|" "`3747 `__","``ranges::uninitialized_copy_n``, ``ranges::uninitialized_move_n``, and ``ranges::destroy_n`` should use ``std::move``", "November 2022","","","|ranges|" "`3750 `__","Too many papers bump ``__cpp_lib_format``", "November 2022","|Partial| [#note-LWG3750]_","","|format|" "`3751 `__","Missing feature macro for ``flat_set``", "November 2022","","","|flat_containers|" diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -22,7 +22,7 @@ "| `[optional.relops] `_ | `[optional.nullops] `_ | `[optional.comp.with.t] `_","| `optional `_ -| `nullopt `_",None,Hristo Hristov,|In Progress| +| `nullopt `_",None,Hristo Hristov,|Complete| "| `[variant.relops] `_ | `[variant.monostate.relops] `_","| `monostate `_ | `variant `_",None,Kent Ross,|Complete| diff --git a/libcxx/include/optional b/libcxx/include/optional --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -16,107 +16,126 @@ // C++1z namespace std { - // 23.6.3, optional for object types - template class optional; + // [optional.optional], class template optional + template + class optional; - // 23.6.4, no-value state indicator + template + concept is-derived-from-optional = requires(const T& t) { // exposition only + [](const optional&){ }(t); + }; + + // [optional.nullopt], no-value state indicator struct nullopt_t{see below }; inline constexpr nullopt_t nullopt(unspecified ); - // 23.6.5, class bad_optional_access + // [optional.bad.access], class bad_optional_access class bad_optional_access; - // 23.6.6, relational operators + // [optional.relops], relational operators template - constexpr bool operator==(const optional&, const optional&); + constexpr bool operator==(const optional&, const optional&); template - constexpr bool operator!=(const optional&, const optional&); + constexpr bool operator!=(const optional&, const optional&); template - constexpr bool operator<(const optional&, const optional&); + constexpr bool operator<(const optional&, const optional&); template - constexpr bool operator>(const optional&, const optional&); + constexpr bool operator>(const optional&, const optional&); template - constexpr bool operator<=(const optional&, const optional&); + constexpr bool operator<=(const optional&, const optional&); template - constexpr bool operator>=(const optional&, const optional&); - - // 23.6.7 comparison with nullopt - template constexpr bool operator==(const optional&, nullopt_t) noexcept; - template constexpr bool operator==(nullopt_t, const optional&) noexcept; - template constexpr bool operator!=(const optional&, nullopt_t) noexcept; - template constexpr bool operator!=(nullopt_t, const optional&) noexcept; - template constexpr bool operator<(const optional&, nullopt_t) noexcept; - template constexpr bool operator<(nullopt_t, const optional&) noexcept; - template constexpr bool operator<=(const optional&, nullopt_t) noexcept; - template constexpr bool operator<=(nullopt_t, const optional&) noexcept; - template constexpr bool operator>(const optional&, nullopt_t) noexcept; - template constexpr bool operator>(nullopt_t, const optional&) noexcept; - template constexpr bool operator>=(const optional&, nullopt_t) noexcept; - template constexpr bool operator>=(nullopt_t, const optional&) noexcept; - - // 23.6.8, comparison with T - template constexpr bool operator==(const optional&, const U&); - template constexpr bool operator==(const T&, const optional&); - template constexpr bool operator!=(const optional&, const U&); - template constexpr bool operator!=(const T&, const optional&); - template constexpr bool operator<(const optional&, const U&); - template constexpr bool operator<(const T&, const optional&); - template constexpr bool operator<=(const optional&, const U&); - template constexpr bool operator<=(const T&, const optional&); - template constexpr bool operator>(const optional&, const U&); - template constexpr bool operator>(const T&, const optional&); - template constexpr bool operator>=(const optional&, const U&); - template constexpr bool operator>=(const T&, const optional&); - - // 23.6.9, specialized algorithms - template void swap(optional&, optional&) noexcept(see below ); // constexpr in C++20 - template constexpr optional make_optional(T&&); - template + constexpr bool operator>=(const optional&, const optional&); + template U> + constexpr compare_three_way_result_t + operator<=>(const optional&, const optional&); // since C++20 + + // [optional.nullops], comparison with nullopt + template constexpr bool operator==(const optional&, nullopt_t) noexcept; + template constexpr bool operator==(nullopt_t, const optional&) noexcept; // until C++17 + template constexpr bool operator!=(const optional&, nullopt_t) noexcept; // until C++17 + template constexpr bool operator!=(nullopt_t, const optional&) noexcept; // until C++17 + template constexpr bool operator<(const optional&, nullopt_t) noexcept; // until C++17 + template constexpr bool operator<(nullopt_t, const optional&) noexcept; // until C++17 + template constexpr bool operator<=(const optional&, nullopt_t) noexcept; // until C++17 + template constexpr bool operator<=(nullopt_t, const optional&) noexcept; // until C++17 + template constexpr bool operator>(const optional&, nullopt_t) noexcept; // until C++17 + template constexpr bool operator>(nullopt_t, const optional&) noexcept; // until C++17 + template constexpr bool operator>=(const optional&, nullopt_t) noexcept; // until C++17 + template constexpr bool operator>=(nullopt_t, const optional&) noexcept; // until C++17 + template + constexpr strong_ordering operator<=>(const optional&, nullopt_t) noexcept; // since C++20 + + // [optional.comp.with.t], comparison with T + template constexpr bool operator==(const optional&, const U&); + template constexpr bool operator==(const T&, const optional&); + template constexpr bool operator!=(const optional&, const U&); + template constexpr bool operator!=(const T&, const optional&); + template constexpr bool operator<(const optional&, const U&); + template constexpr bool operator<(const T&, const optional&); + template constexpr bool operator<=(const optional&, const U&); + template constexpr bool operator<=(const T&, const optional&); + template constexpr bool operator>(const optional&, const U&); + template constexpr bool operator>(const T&, const optional&); + template constexpr bool operator>=(const optional&, const U&); + template constexpr bool operator>=(const T&, const optional&); + template + requires (!is-derived-from-optional) && three_way_comparable_with + constexpr compare_three_way_result_t + operator<=>(const optional&, const U&); // since C++20 + + // [optional.specalg], specialized algorithms + template + void swap(optional&, optional&) noexcept(see below ); // constexpr in C++20 + + template + constexpr optional make_optional(T&&); + template constexpr optional make_optional(Args&&... args); - template + template constexpr optional make_optional(initializer_list il, Args&&... args); - // 23.6.10, hash support - template struct hash; - template struct hash>; + // [optional.hash], hash support + template struct hash; + template struct hash>; - template class optional { + template + class optional { public: using value_type = T; - // 23.6.3.1, constructors + // [optional.ctor], constructors constexpr optional() noexcept; constexpr optional(nullopt_t) noexcept; constexpr optional(const optional &); constexpr optional(optional &&) noexcept(see below); - template constexpr explicit optional(in_place_t, Args &&...); - template + template + constexpr explicit optional(in_place_t, Args &&...); + template constexpr explicit optional(in_place_t, initializer_list, Args &&...); - template + template constexpr explicit(see-below) optional(U &&); - template - explicit(see-below) optional(const optional &); // constexpr in C++20 - template - explicit(see-below) optional(optional &&); // constexpr in C++20 + template + explicit(see-below) optional(const optional &); // constexpr in C++20 + template + explicit(see-below) optional(optional &&); // constexpr in C++20 - // 23.6.3.2, destructor + // [optional.dtor], destructor ~optional(); // constexpr in C++20 - // 23.6.3.3, assignment - optional &operator=(nullopt_t) noexcept; // constexpr in C++20 + // [optional.assign], assignment + optional &operator=(nullopt_t) noexcept; // constexpr in C++20 constexpr optional &operator=(const optional &); constexpr optional &operator=(optional &&) noexcept(see below); - template optional &operator=(U &&); // constexpr in C++20 - template optional &operator=(const optional &); // constexpr in C++20 - template optional &operator=(optional &&); // constexpr in C++20 - template T& emplace(Args &&...); // constexpr in C++20 - template - T& emplace(initializer_list, Args &&...); // constexpr in C++20 - - // 23.6.3.4, swap + template optional &operator=(U &&); // constexpr in C++20 + template optional &operator=(const optional &); // constexpr in C++20 + template optional &operator=(optional &&); // constexpr in C++20 + template T& emplace(Args &&...); // constexpr in C++20 + template T& emplace(initializer_list, Args &&...); // constexpr in C++20 + + // [optional.swap], swap void swap(optional &) noexcept(see below ); // constexpr in C++20 - // 23.6.3.5, observers + // [optional.observe], observers constexpr T const *operator->() const; constexpr T *operator->(); constexpr T const &operator*() const &; @@ -129,8 +148,8 @@ constexpr T &value() &; constexpr T &&value() &&; constexpr const T &&value() const &&; - template constexpr T value_or(U &&) const &; - template constexpr T value_or(U &&) &&; + template constexpr T value_or(U &&) const &; + template constexpr T value_or(U &&) &&; // [optional.monadic], monadic operations template constexpr auto and_then(F&& f) &; // since C++23 @@ -144,15 +163,15 @@ template constexpr optional or_else(F&& f) &&; // since C++23 template constexpr optional or_else(F&& f) const&; // since C++23 - // 23.6.3.6, modifiers - void reset() noexcept; // constexpr in C++20 + // [optional.mod], modifiers + void reset() noexcept; // constexpr in C++20 private: - T *val; // exposition only + T *val; // exposition only }; -template - optional(T) -> optional; + template + optional(T) -> optional; } // namespace std @@ -160,6 +179,8 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__availability> +#include <__compare/compare_three_way_result.h> +#include <__compare/three_way_comparable.h> #include <__concepts/invocable.h> #include <__config> #include <__functional/hash.h> @@ -633,6 +654,14 @@ template class optional; + +#if _LIBCPP_STD_VER >= 20 + +template +concept __is_derived_from_optional = requires(const _Tp& __t) { [](const optional<__Up>&) {}(__t); }; + +# endif // _LIBCPP_STD_VER >= 20 + template struct __is_std_optional : false_type {}; template struct __is_std_optional> : true_type {}; @@ -1288,6 +1317,18 @@ return *__x >= *__y; } +#if _LIBCPP_STD_VER >= 20 + +template _Up> +_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up> +operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) { + if (__x && __y) + return *__x <=> *__y; + return __x.has_value() <=> __y.has_value(); +} + +#endif // _LIBCPP_STD_VER >= 20 + // Comparisons with nullopt template _LIBCPP_INLINE_VISIBILITY constexpr @@ -1297,6 +1338,8 @@ return !static_cast(__x); } +#if _LIBCPP_STD_VER <= 17 + template _LIBCPP_INLINE_VISIBILITY constexpr bool @@ -1385,6 +1428,15 @@ return !static_cast(__x); } +#else // _LIBCPP_STD_VER <= 17 + +template +_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept { + return __x.has_value() <=> false; +} + +#endif // _LIBCPP_STD_VER <= 17 + // Comparisons with T template _LIBCPP_INLINE_VISIBILITY constexpr @@ -1530,6 +1582,17 @@ return static_cast(__x) ? __v >= *__x : true; } +#if _LIBCPP_STD_VER >= 20 + +template + requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up> +_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up> +operator<=>(const optional<_Tp>& __x, const _Up& __v) { + return __x.has_value() ? *__x <=> __v : strong_ordering::less; +} + +#endif // _LIBCPP_STD_VER >= 20 + template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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.comp.with.t], comparison with T + +// template +// requires (!is-derived-from-optional) && three_way_comparable_with +// constexpr compare_three_way_result_t +// operator<=>(const optional&, const U&); + +#include +#include +#include + +#include "test_comparisons.h" + +struct SomeInt { + int value_; + + constexpr explicit SomeInt(int value = 0) : value_(value) {} + + auto operator<=>(const SomeInt&) const = default; +}; + +template +concept HasSpaceship = requires(T t, U u) { t <=> u; }; + +// SFINAE tests. + +static_assert(std::three_way_comparable_with, std::optional>); +static_assert(HasSpaceship, std::optional>); + +static_assert(std::three_way_comparable_with, std::optional>); +static_assert(HasSpaceship, std::optional>); + +static_assert(!HasSpaceship, std::optional>); + +// Runtime and static tests. + +constexpr void test_custom_integral() { + { + SomeInt t{3}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::equal); + assert(testOrder(t, op, std::strong_ordering::equal)); + } + { + SomeInt t{2}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::less); + assert(testOrder(t, op, std::strong_ordering::less)); + } + { + SomeInt t{3}; + std::optional op{2}; + assert((t <=> op) == std::strong_ordering::greater); + assert(testOrder(t, op, std::strong_ordering::greater)); + } +} + +constexpr void test_int() { + { + int t{3}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::equal); + assert(testOrder(t, op, std::strong_ordering::equal)); + } + { + int t{2}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::less); + assert(testOrder(t, op, std::strong_ordering::less)); + } + { + int t{3}; + std::optional op{2}; + assert((t <=> op) == std::strong_ordering::greater); + assert(testOrder(t, op, std::strong_ordering::greater)); + } +} + +constexpr bool test() { + test_custom_integral(); + test_int(); + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp b/libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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.nullops], comparison with nullopt + +// template +// constexpr strong_ordering operator<=>(const optional&, nullopt_t) noexcept; + +#include +#include +#include + +#include "test_comparisons.h" + +constexpr bool test() { + { + std::optional op; + assert((std::nullopt <=> op) == std::strong_ordering::equal); + assert(testOrder(std::nullopt, op, std::strong_ordering::equal)); + assert((op <=> std::nullopt) == std::strong_ordering::equal); + assert(testOrder(op, std::nullopt, std::strong_ordering::equal)); + } + { + std::optional op{1}; + assert((std::nullopt <=> op) == std::strong_ordering::less); + assert(testOrder(std::nullopt, op, std::strong_ordering::less)); + } + { + std::optional op{1}; + assert((op <=> std::nullopt) == std::strong_ordering::greater); + assert(testOrder(op, std::nullopt, std::strong_ordering::greater)); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.relops/compare.three_way.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 + +// + +// [optional.relops], relational operators + +// template U> +// constexpr compare_three_way_result_t +// operator<=>(const optional&, const optional&); + +#include +#include +#include + +#include "test_comparisons.h" + +constexpr bool test() { + { + std::optional op1; + std::optional op2; + + assert((op1 <=> op2) == std::strong_ordering::equal); + assert(testOrder(op1, op2, std::strong_ordering::equal)); + } + { + std::optional op1{3}; + std::optional op2{3}; + assert((op1 <=> op1) == std::strong_ordering::equal); + assert(testOrder(op1, op1, std::strong_ordering::equal)); + assert((op1 <=> op2) == std::strong_ordering::equal); + assert(testOrder(op1, op2, std::strong_ordering::equal)); + assert((op2 <=> op1) == std::strong_ordering::equal); + assert(testOrder(op2, op1, std::strong_ordering::equal)); + } + { + std::optional op; + std::optional op1{2}; + std::optional op2{3}; + assert((op <=> op2) == std::strong_ordering::less); + assert(testOrder(op, op2, std::strong_ordering::less)); + assert((op1 <=> op2) == std::strong_ordering::less); + assert(testOrder(op1, op2, std::strong_ordering::less)); + } + { + std::optional op; + std::optional op1{3}; + std::optional op2{2}; + assert((op1 <=> op) == std::strong_ordering::greater); + assert(testOrder(op1, op, std::strong_ordering::greater)); + assert((op1 <=> op2) == std::strong_ordering::greater); + assert(testOrder(op1, op2, std::strong_ordering::greater)); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +}