Skip to content

Commit

Permalink
[libc++] P2167R3: Improved Proposed Wording for LWG 2114 (llvm#109102)
Browse files Browse the repository at this point in the history
Only the [cmp.alg] part (for `comparison_meow_fallback` CPOs) in the
paper required changes. Other parts merely fixed preconditions of some
standard library functions.

I strongly feel that P2167R3 should be a DR despite that it is not a DR
officially: CPOs -> C++20; remain parts -> C++98/11 (except that
_`boolean-testable`_ should be transformed into the original
_BooleanTestable_ requirements in the old resolution of LWG2114).

Note that P2167R3 damaged the resolution of LWG3465: the type of `F < E`
was left underconstrained. I've tried to submit an LWG issue for this,
which is now LWG4157.

Drive-by change:
- enable some test coverages in `compare_strong_order_fallback.pass.cpp`
when `TEST_LONG_DOUBLE_IS_DOUBLE`, following up llvm#106742

Closes llvm#105241.
  • Loading branch information
frederick-vs-ja authored and EricWF committed Oct 2, 2024
1 parent c29f1e6 commit 40225c7
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 32 deletions.
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","",""
"`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9.0",""
"`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","",""
"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","","",""
"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20.0","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20. MSVC STL does the same. Other parts are Nothing To Do."
"`P2396R1 <https://wg21.link/P2396R1>`__","Concurrency TS 2 fixes ","2022-11 (Kona)","","",""
"`P2505R5 <https://wg21.link/P2505R5>`__","Monadic Functions for ``std::expected``","2022-11 (Kona)","|Complete|","17.0",""
"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","2022-11 (Kona)","|Complete|","18.0",""
Expand Down
23 changes: 11 additions & 12 deletions libcxx/include/__compare/compare_partial_order_fallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <__compare/ordering.h>
#include <__compare/partial_order.h>
#include <__concepts/boolean_testable.h>
#include <__config>
#include <__type_traits/decay.h>
#include <__type_traits/is_same.h>
Expand All @@ -37,18 +38,16 @@ struct __fn {
}

template <class _Tp, class _Up>
requires is_same_v<decay_t<_Tp>, decay_t<_Up>>
_LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
: std::forward<_Up>(__u) < std::forward<_Tp>(__t)
? partial_ordering::greater
: partial_ordering::unordered))
-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
: std::forward<_Up>(__u) < std::forward<_Tp>(__t)
? partial_ordering::greater
: partial_ordering::unordered) {
requires is_same_v<decay_t<_Tp>, decay_t<_Up>> && requires(_Tp&& __t, _Up&& __u) {
{ std::forward<_Tp>(__t) == std::forward<_Up>(__u) } -> __boolean_testable;
{ std::forward<_Tp>(__t) < std::forward<_Up>(__u) } -> __boolean_testable;
{ std::forward<_Up>(__u) < std::forward<_Tp>(__t) } -> __boolean_testable;
}
_LIBCPP_HIDE_FROM_ABI static constexpr partial_ordering __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(
noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
: std::forward<_Up>(__u) < std::forward<_Tp>(__t) ? partial_ordering::greater
: partial_ordering::unordered)) {
return std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
: std::forward<_Up>(__u) < std::forward<_Tp>(__t)
Expand Down
19 changes: 9 additions & 10 deletions libcxx/include/__compare/compare_strong_order_fallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <__compare/ordering.h>
#include <__compare/strong_order.h>
#include <__concepts/boolean_testable.h>
#include <__config>
#include <__type_traits/decay.h>
#include <__type_traits/is_same.h>
Expand All @@ -37,16 +38,14 @@ struct __fn {
}

template <class _Tp, class _Up>
requires is_same_v<decay_t<_Tp>, decay_t<_Up>>
_LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
? strong_ordering::less
: strong_ordering::greater))
-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
? strong_ordering::less
: strong_ordering::greater) {
requires is_same_v<decay_t<_Tp>, decay_t<_Up>> && requires(_Tp&& __t, _Up&& __u) {
{ std::forward<_Tp>(__t) == std::forward<_Up>(__u) } -> __boolean_testable;
{ std::forward<_Tp>(__t) < std::forward<_Up>(__u) } -> __boolean_testable;
}
_LIBCPP_HIDE_FROM_ABI static constexpr strong_ordering __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(
noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? strong_ordering::less
: strong_ordering::greater)) {
return std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
? strong_ordering::less
Expand Down
14 changes: 7 additions & 7 deletions libcxx/include/__compare/compare_weak_order_fallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <__compare/ordering.h>
#include <__compare/weak_order.h>
#include <__concepts/boolean_testable.h>
#include <__config>
#include <__type_traits/decay.h>
#include <__type_traits/is_same.h>
Expand All @@ -37,16 +38,15 @@ struct __fn {
}

template <class _Tp, class _Up>
requires is_same_v<decay_t<_Tp>, decay_t<_Up>>
_LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
requires is_same_v<decay_t<_Tp>, decay_t<_Up>> && requires(_Tp&& __t, _Up&& __u) {
{ std::forward<_Tp>(__t) == std::forward<_Up>(__u) } -> __boolean_testable;
{ std::forward<_Tp>(__t) < std::forward<_Up>(__u) } -> __boolean_testable;
}
_LIBCPP_HIDE_FROM_ABI static constexpr weak_ordering __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? weak_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
? weak_ordering::less
: weak_ordering::greater))
-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? weak_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
? weak_ordering::less
: weak_ordering::greater) {
: weak_ordering::greater)) {
return std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? weak_ordering::equivalent
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
? weak_ordering::less
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,44 @@ namespace N2 {
friend bool operator<(const VC2&, VC2&);
friend bool operator<(VC2&, const VC2&);
};

enum class comparison_result_kind : bool {
convertible_bool,
boolean_testable,
};

template <comparison_result_kind K>
struct comparison_result {
bool value;

constexpr operator bool() const noexcept { return value; }

constexpr auto operator!() const noexcept {
if constexpr (K == comparison_result_kind::boolean_testable) {
return comparison_result{!value};
}
}
};

template <comparison_result_kind EqKind, comparison_result_kind LeKind>
struct boolean_tested_type {
friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
return comparison_result<EqKind>{true};
}

friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
return comparison_result<LeKind>{false};
}
};

using test_only_convertible =
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
using test_eq_boolean_testable =
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
using test_le_boolean_testable =
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
using test_boolean_testable =
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
}

constexpr bool test_2()
Expand Down Expand Up @@ -306,6 +344,21 @@ constexpr bool test_2()
assert( has_partial_order(cvc, vc));
assert(!has_partial_order(vc, cvc));
}
{
// P2167R3 as modified by the intent of LWG3465:
// All of decltype(e == f), decltype(e < f), and decltype(f < e) need to be well-formed and boolean-testable.
N2::test_only_convertible tc;
N2::test_eq_boolean_testable teq;
N2::test_le_boolean_testable tle;
N2::test_boolean_testable tbt;

assert(!has_partial_order(tc, tc));
assert(!has_partial_order(teq, teq));
assert(!has_partial_order(tle, tle));
assert(has_partial_order(tbt, tbt));

assert(std::compare_partial_order_fallback(tbt, tbt) == std::partial_ordering::equivalent);
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,44 @@ namespace N2 {
friend bool operator<(const VC2&, VC2&);
friend bool operator<(VC2&, const VC2&);
};

enum class comparison_result_kind : bool {
convertible_bool,
boolean_testable,
};

template <comparison_result_kind K>
struct comparison_result {
bool value;

constexpr operator bool() const noexcept { return value; }

constexpr auto operator!() const noexcept {
if constexpr (K == comparison_result_kind::boolean_testable) {
return comparison_result{!value};
}
}
};

template <comparison_result_kind EqKind, comparison_result_kind LeKind>
struct boolean_tested_type {
friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
return comparison_result<EqKind>{true};
}

friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
return comparison_result<LeKind>{false};
}
};

using test_only_convertible =
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
using test_eq_boolean_testable =
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
using test_le_boolean_testable =
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
using test_boolean_testable =
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
}

constexpr bool test_2()
Expand Down Expand Up @@ -506,6 +544,20 @@ constexpr bool test_2()
assert( has_strong_order(cvc, vc));
assert(!has_strong_order(vc, cvc));
}
{
// P2167R3: Both decltype(e == f) and decltype(e < f) need to be well-formed and boolean-testable.
N2::test_only_convertible tc;
N2::test_eq_boolean_testable teq;
N2::test_le_boolean_testable tle;
N2::test_boolean_testable tbt;

assert(!has_strong_order(tc, tc));
assert(!has_strong_order(teq, teq));
assert(!has_strong_order(tle, tle));
assert(has_strong_order(tbt, tbt));

assert(std::compare_strong_order_fallback(tbt, tbt) == std::strong_ordering::equal);
}
return true;
}

Expand All @@ -515,13 +567,17 @@ int main(int, char**)
test_1_2();
test_1_3<float>();
test_1_3<double>();
// test_1_3<long double>(); // UNIMPLEMENTED
#ifdef TEST_LONG_DOUBLE_IS_DOUBLE
test_1_3<long double>(); // UNIMPLEMENTED when long double is a distinct type
#endif
test_1_4();
test_2();

static_assert(test_1_3<float>());
static_assert(test_1_3<double>());
// static_assert(test_1_3<long double>()); // UNIMPLEMENTED
#ifdef TEST_LONG_DOUBLE_IS_DOUBLE
static_assert(test_1_3<long double>()); // UNIMPLEMENTED when long double is a distinct type
#endif
static_assert(test_1_4());
static_assert(test_2());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,44 @@ namespace N2 {
friend bool operator<(const VC2&, VC2&);
friend bool operator<(VC2&, const VC2&);
};

enum class comparison_result_kind : bool {
convertible_bool,
boolean_testable,
};

template <comparison_result_kind K>
struct comparison_result {
bool value;

constexpr operator bool() const noexcept { return value; }

constexpr auto operator!() const noexcept {
if constexpr (K == comparison_result_kind::boolean_testable) {
return comparison_result{!value};
}
}
};

template <comparison_result_kind EqKind, comparison_result_kind LeKind>
struct boolean_tested_type {
friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
return comparison_result<EqKind>{true};
}

friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
return comparison_result<LeKind>{false};
}
};

using test_only_convertible =
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
using test_eq_boolean_testable =
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
using test_le_boolean_testable =
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
using test_boolean_testable =
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
}

constexpr bool test_2()
Expand Down Expand Up @@ -553,6 +591,20 @@ constexpr bool test_2()
assert( has_weak_order(cvc, vc));
assert(!has_weak_order(vc, cvc));
}
{
// P2167R3: Both decltype(e == f) and decltype(e < f) need to be well-formed and boolean-testable.
N2::test_only_convertible tc;
N2::test_eq_boolean_testable teq;
N2::test_le_boolean_testable tle;
N2::test_boolean_testable tbt;

assert(!has_weak_order(tc, tc));
assert(!has_weak_order(teq, teq));
assert(!has_weak_order(tle, tle));
assert(has_weak_order(tbt, tbt));

assert(std::compare_weak_order_fallback(tbt, tbt) == std::weak_ordering::equivalent);
}
return true;
}

Expand Down

0 comments on commit 40225c7

Please sign in to comment.