Skip to content

Commit 0785e21

Browse files
frederick-vs-jaSterling-Augustine
authored andcommitted
[libc++] P2167R3: Improved Proposed Wording for LWG 2114 (llvm#109102)
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.
1 parent c519c58 commit 0785e21

File tree

7 files changed

+191
-32
lines changed

7 files changed

+191
-32
lines changed

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
"`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","",""
9797
"`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9.0",""
9898
"`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","",""
99-
"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","","",""
99+
"`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."
100100
"`P2396R1 <https://wg21.link/P2396R1>`__","Concurrency TS 2 fixes ","2022-11 (Kona)","","",""
101101
"`P2505R5 <https://wg21.link/P2505R5>`__","Monadic Functions for ``std::expected``","2022-11 (Kona)","|Complete|","17.0",""
102102
"`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",""

libcxx/include/__compare/compare_partial_order_fallback.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__compare/ordering.h>
1313
#include <__compare/partial_order.h>
14+
#include <__concepts/boolean_testable.h>
1415
#include <__config>
1516
#include <__type_traits/decay.h>
1617
#include <__type_traits/is_same.h>
@@ -37,18 +38,16 @@ struct __fn {
3738
}
3839

3940
template <class _Tp, class _Up>
40-
requires is_same_v<decay_t<_Tp>, decay_t<_Up>>
41-
_LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
42-
std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
43-
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
44-
: std::forward<_Up>(__u) < std::forward<_Tp>(__t)
45-
? partial_ordering::greater
46-
: partial_ordering::unordered))
47-
-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
48-
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
49-
: std::forward<_Up>(__u) < std::forward<_Tp>(__t)
50-
? partial_ordering::greater
51-
: partial_ordering::unordered) {
41+
requires is_same_v<decay_t<_Tp>, decay_t<_Up>> && requires(_Tp&& __t, _Up&& __u) {
42+
{ std::forward<_Tp>(__t) == std::forward<_Up>(__u) } -> __boolean_testable;
43+
{ std::forward<_Tp>(__t) < std::forward<_Up>(__u) } -> __boolean_testable;
44+
{ std::forward<_Up>(__u) < std::forward<_Tp>(__t) } -> __boolean_testable;
45+
}
46+
_LIBCPP_HIDE_FROM_ABI static constexpr partial_ordering __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(
47+
noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
48+
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
49+
: std::forward<_Up>(__u) < std::forward<_Tp>(__t) ? partial_ordering::greater
50+
: partial_ordering::unordered)) {
5251
return std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? partial_ordering::equivalent
5352
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? partial_ordering::less
5453
: std::forward<_Up>(__u) < std::forward<_Tp>(__t)

libcxx/include/__compare/compare_strong_order_fallback.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__compare/ordering.h>
1313
#include <__compare/strong_order.h>
14+
#include <__concepts/boolean_testable.h>
1415
#include <__config>
1516
#include <__type_traits/decay.h>
1617
#include <__type_traits/is_same.h>
@@ -37,16 +38,14 @@ struct __fn {
3738
}
3839

3940
template <class _Tp, class _Up>
40-
requires is_same_v<decay_t<_Tp>, decay_t<_Up>>
41-
_LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
42-
std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
43-
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
44-
? strong_ordering::less
45-
: strong_ordering::greater))
46-
-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
47-
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
48-
? strong_ordering::less
49-
: strong_ordering::greater) {
41+
requires is_same_v<decay_t<_Tp>, decay_t<_Up>> && requires(_Tp&& __t, _Up&& __u) {
42+
{ std::forward<_Tp>(__t) == std::forward<_Up>(__u) } -> __boolean_testable;
43+
{ std::forward<_Tp>(__t) < std::forward<_Up>(__u) } -> __boolean_testable;
44+
}
45+
_LIBCPP_HIDE_FROM_ABI static constexpr strong_ordering __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(
46+
noexcept(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
47+
: std::forward<_Tp>(__t) < std::forward<_Up>(__u) ? strong_ordering::less
48+
: strong_ordering::greater)) {
5049
return std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? strong_ordering::equal
5150
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
5251
? strong_ordering::less

libcxx/include/__compare/compare_weak_order_fallback.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__compare/ordering.h>
1313
#include <__compare/weak_order.h>
14+
#include <__concepts/boolean_testable.h>
1415
#include <__config>
1516
#include <__type_traits/decay.h>
1617
#include <__type_traits/is_same.h>
@@ -37,16 +38,15 @@ struct __fn {
3738
}
3839

3940
template <class _Tp, class _Up>
40-
requires is_same_v<decay_t<_Tp>, decay_t<_Up>>
41-
_LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
41+
requires is_same_v<decay_t<_Tp>, decay_t<_Up>> && requires(_Tp&& __t, _Up&& __u) {
42+
{ std::forward<_Tp>(__t) == std::forward<_Up>(__u) } -> __boolean_testable;
43+
{ std::forward<_Tp>(__t) < std::forward<_Up>(__u) } -> __boolean_testable;
44+
}
45+
_LIBCPP_HIDE_FROM_ABI static constexpr weak_ordering __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) noexcept(noexcept(
4246
std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? weak_ordering::equivalent
4347
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
4448
? weak_ordering::less
45-
: weak_ordering::greater))
46-
-> decltype(std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? weak_ordering::equivalent
47-
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
48-
? weak_ordering::less
49-
: weak_ordering::greater) {
49+
: weak_ordering::greater)) {
5050
return std::forward<_Tp>(__t) == std::forward<_Up>(__u) ? weak_ordering::equivalent
5151
: std::forward<_Tp>(__t) < std::forward<_Up>(__u)
5252
? weak_ordering::less

libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,44 @@ namespace N2 {
267267
friend bool operator<(const VC2&, VC2&);
268268
friend bool operator<(VC2&, const VC2&);
269269
};
270+
271+
enum class comparison_result_kind : bool {
272+
convertible_bool,
273+
boolean_testable,
274+
};
275+
276+
template <comparison_result_kind K>
277+
struct comparison_result {
278+
bool value;
279+
280+
constexpr operator bool() const noexcept { return value; }
281+
282+
constexpr auto operator!() const noexcept {
283+
if constexpr (K == comparison_result_kind::boolean_testable) {
284+
return comparison_result{!value};
285+
}
286+
}
287+
};
288+
289+
template <comparison_result_kind EqKind, comparison_result_kind LeKind>
290+
struct boolean_tested_type {
291+
friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
292+
return comparison_result<EqKind>{true};
293+
}
294+
295+
friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
296+
return comparison_result<LeKind>{false};
297+
}
298+
};
299+
300+
using test_only_convertible =
301+
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
302+
using test_eq_boolean_testable =
303+
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
304+
using test_le_boolean_testable =
305+
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
306+
using test_boolean_testable =
307+
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
270308
}
271309

272310
constexpr bool test_2()
@@ -306,6 +344,21 @@ constexpr bool test_2()
306344
assert( has_partial_order(cvc, vc));
307345
assert(!has_partial_order(vc, cvc));
308346
}
347+
{
348+
// P2167R3 as modified by the intent of LWG3465:
349+
// All of decltype(e == f), decltype(e < f), and decltype(f < e) need to be well-formed and boolean-testable.
350+
N2::test_only_convertible tc;
351+
N2::test_eq_boolean_testable teq;
352+
N2::test_le_boolean_testable tle;
353+
N2::test_boolean_testable tbt;
354+
355+
assert(!has_partial_order(tc, tc));
356+
assert(!has_partial_order(teq, teq));
357+
assert(!has_partial_order(tle, tle));
358+
assert(has_partial_order(tbt, tbt));
359+
360+
assert(std::compare_partial_order_fallback(tbt, tbt) == std::partial_ordering::equivalent);
361+
}
309362
return true;
310363
}
311364

libcxx/test/std/language.support/cmp/cmp.alg/compare_strong_order_fallback.pass.cpp

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,44 @@ namespace N2 {
473473
friend bool operator<(const VC2&, VC2&);
474474
friend bool operator<(VC2&, const VC2&);
475475
};
476+
477+
enum class comparison_result_kind : bool {
478+
convertible_bool,
479+
boolean_testable,
480+
};
481+
482+
template <comparison_result_kind K>
483+
struct comparison_result {
484+
bool value;
485+
486+
constexpr operator bool() const noexcept { return value; }
487+
488+
constexpr auto operator!() const noexcept {
489+
if constexpr (K == comparison_result_kind::boolean_testable) {
490+
return comparison_result{!value};
491+
}
492+
}
493+
};
494+
495+
template <comparison_result_kind EqKind, comparison_result_kind LeKind>
496+
struct boolean_tested_type {
497+
friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
498+
return comparison_result<EqKind>{true};
499+
}
500+
501+
friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
502+
return comparison_result<LeKind>{false};
503+
}
504+
};
505+
506+
using test_only_convertible =
507+
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
508+
using test_eq_boolean_testable =
509+
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
510+
using test_le_boolean_testable =
511+
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
512+
using test_boolean_testable =
513+
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
476514
}
477515

478516
constexpr bool test_2()
@@ -506,6 +544,20 @@ constexpr bool test_2()
506544
assert( has_strong_order(cvc, vc));
507545
assert(!has_strong_order(vc, cvc));
508546
}
547+
{
548+
// P2167R3: Both decltype(e == f) and decltype(e < f) need to be well-formed and boolean-testable.
549+
N2::test_only_convertible tc;
550+
N2::test_eq_boolean_testable teq;
551+
N2::test_le_boolean_testable tle;
552+
N2::test_boolean_testable tbt;
553+
554+
assert(!has_strong_order(tc, tc));
555+
assert(!has_strong_order(teq, teq));
556+
assert(!has_strong_order(tle, tle));
557+
assert(has_strong_order(tbt, tbt));
558+
559+
assert(std::compare_strong_order_fallback(tbt, tbt) == std::strong_ordering::equal);
560+
}
509561
return true;
510562
}
511563

@@ -515,13 +567,17 @@ int main(int, char**)
515567
test_1_2();
516568
test_1_3<float>();
517569
test_1_3<double>();
518-
// test_1_3<long double>(); // UNIMPLEMENTED
570+
#ifdef TEST_LONG_DOUBLE_IS_DOUBLE
571+
test_1_3<long double>(); // UNIMPLEMENTED when long double is a distinct type
572+
#endif
519573
test_1_4();
520574
test_2();
521575

522576
static_assert(test_1_3<float>());
523577
static_assert(test_1_3<double>());
524-
// static_assert(test_1_3<long double>()); // UNIMPLEMENTED
578+
#ifdef TEST_LONG_DOUBLE_IS_DOUBLE
579+
static_assert(test_1_3<long double>()); // UNIMPLEMENTED when long double is a distinct type
580+
#endif
525581
static_assert(test_1_4());
526582
static_assert(test_2());
527583

libcxx/test/std/language.support/cmp/cmp.alg/compare_weak_order_fallback.pass.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,44 @@ namespace N2 {
520520
friend bool operator<(const VC2&, VC2&);
521521
friend bool operator<(VC2&, const VC2&);
522522
};
523+
524+
enum class comparison_result_kind : bool {
525+
convertible_bool,
526+
boolean_testable,
527+
};
528+
529+
template <comparison_result_kind K>
530+
struct comparison_result {
531+
bool value;
532+
533+
constexpr operator bool() const noexcept { return value; }
534+
535+
constexpr auto operator!() const noexcept {
536+
if constexpr (K == comparison_result_kind::boolean_testable) {
537+
return comparison_result{!value};
538+
}
539+
}
540+
};
541+
542+
template <comparison_result_kind EqKind, comparison_result_kind LeKind>
543+
struct boolean_tested_type {
544+
friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
545+
return comparison_result<EqKind>{true};
546+
}
547+
548+
friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
549+
return comparison_result<LeKind>{false};
550+
}
551+
};
552+
553+
using test_only_convertible =
554+
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
555+
using test_eq_boolean_testable =
556+
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
557+
using test_le_boolean_testable =
558+
boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
559+
using test_boolean_testable =
560+
boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
523561
}
524562

525563
constexpr bool test_2()
@@ -553,6 +591,20 @@ constexpr bool test_2()
553591
assert( has_weak_order(cvc, vc));
554592
assert(!has_weak_order(vc, cvc));
555593
}
594+
{
595+
// P2167R3: Both decltype(e == f) and decltype(e < f) need to be well-formed and boolean-testable.
596+
N2::test_only_convertible tc;
597+
N2::test_eq_boolean_testable teq;
598+
N2::test_le_boolean_testable tle;
599+
N2::test_boolean_testable tbt;
600+
601+
assert(!has_weak_order(tc, tc));
602+
assert(!has_weak_order(teq, teq));
603+
assert(!has_weak_order(tle, tle));
604+
assert(has_weak_order(tbt, tbt));
605+
606+
assert(std::compare_weak_order_fallback(tbt, tbt) == std::weak_ordering::equivalent);
607+
}
556608
return true;
557609
}
558610

0 commit comments

Comments
 (0)