[libcxx-commits] [libcxx] [libc++] Avoid -Wzero-as-null-pointer-constant in operator<=> (PR #79465)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jan 29 07:57:24 PST 2024
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/79465
>From 07fa9189c77a635a4c8c740519d69545f57c85b7 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 25 Jan 2024 10:34:32 -0500
Subject: [PATCH 1/3] [libc++] Avoid -Wzero-as-null-pointer-constant in
operator<=>
Issue #43670 describes a situation where the following comparison
will issue a warning when -Wzero-as-null-pointer-constant is enabled:
#include <compare>
auto b = (1 <=> 2) < 0;
This code uses operator<(strong_ordering, Unspecified), which is
specified by the Standard to only work with a literal 0. In the
library, this is achieved by constructing Unspecified from a pointer,
which works but has the downside of triggering the warning.
This patch uses an alternative implementation where we require that
the operator is used exactly with an int of value 0 (known at compile-time),
however that value can technically be an expression like `1 - 1`, which
makes us a bit less strict than what's specified in the Standard.
Fixes #43670
---
libcxx/include/__compare/ordering.h | 20 +++---
.../reject-other-than-literal-zero.verify.cpp | 70 +++++++++++++++++++
.../cmp.categories.pre/zero_type.verify.cpp | 57 ---------------
3 files changed, 82 insertions(+), 65 deletions(-)
create mode 100644 libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
delete mode 100644 libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp
diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h
index 2995d381304f0e4..778cf5dab4ce567 100644
--- a/libcxx/include/__compare/ordering.h
+++ b/libcxx/include/__compare/ordering.h
@@ -30,14 +30,17 @@ class partial_ordering;
class weak_ordering;
class strong_ordering;
-template <class _Tp, class... _Args>
-inline constexpr bool __one_of_v = (is_same_v<_Tp, _Args> || ...);
-
struct _CmpUnspecifiedParam {
- _LIBCPP_HIDE_FROM_ABI constexpr _CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) noexcept {}
-
- template <class _Tp, class = enable_if_t<!__one_of_v<_Tp, int, partial_ordering, weak_ordering, strong_ordering>>>
- _CmpUnspecifiedParam(_Tp) = delete;
+ template <class _Tp, class = __enable_if_t<is_same_v<_Tp, int>>>
+ _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept {
+ // If anything other than 0 is provided, the behavior is undefined.
+ // We catch this case by making this function consteval and making the program ill-formed if
+ // a value other than 0 is provided. This technically also accepts things that are not
+ // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be
+ // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`.
+ if (__zero != 0)
+ __builtin_trap();
+ }
};
class partial_ordering {
@@ -269,7 +272,8 @@ inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater)
/// The types partial_ordering, weak_ordering, and strong_ordering are
/// collectively termed the comparison category types.
template <class _Tp>
-concept __comparison_category = __one_of_v<_Tp, partial_ordering, weak_ordering, strong_ordering>;
+concept __comparison_category =
+ is_same_v<_Tp, partial_ordering> || is_same_v<_Tp, weak_ordering> || is_same_v<_Tp, strong_ordering>;
#endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
new file mode 100644
index 000000000000000..f2b30284d42a60c
--- /dev/null
+++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// In MSVC mode, there's a slightly different number of errors printed for
+// each of these, so it doesn't add up to the exact expected count of 18.
+// XFAIL: msvc
+
+// <compare>
+
+// Ensure we reject all cases where an argument other than a literal 0 is used
+// for a comparison against a comparison category type.
+
+// Also ensure that we don't warn about providing a null pointer constant when
+// comparing an ordering type against literal 0, since one of the common
+// implementation strategies is to use a pointer as the "unspecified type".
+// ADDITIONAL_COMPILE_FLAGS: -Wzero-as-null-pointer-constant
+
+#include <compare>
+
+#include "test_macros.h"
+
+#define TEST_FAIL(v, op) \
+ do { \
+ void(v op 0L); \
+ void(0L op v); \
+ void(v op 1); \
+ void(1 op v); \
+ void(v op nullptr); \
+ void(nullptr op v); \
+ } while (false)
+
+#define TEST_PASS(v, op) \
+ do { \
+ void(v op 0); \
+ void(0 op v); \
+ LIBCPP_ONLY(void(v op(1 - 1))); \
+ LIBCPP_ONLY(void((1 - 1) op v)); \
+ } while (false)
+
+template <typename T>
+void test_category(T v) {
+ TEST_FAIL(v, ==); // expected-error 18 {{}}
+ TEST_FAIL(v, !=); // expected-error 18 {{}}
+ TEST_FAIL(v, <); // expected-error 18 {{}}
+ TEST_FAIL(v, <=); // expected-error 18 {{}}
+ TEST_FAIL(v, >); // expected-error 18 {{}}
+ TEST_FAIL(v, >=); // expected-error 18 {{}}
+ TEST_FAIL(v, <=>); // expected-error 18 {{}}
+
+ TEST_PASS(v, ==);
+ TEST_PASS(v, !=);
+ TEST_PASS(v, <);
+ TEST_PASS(v, >);
+ TEST_PASS(v, <=);
+ TEST_PASS(v, >=);
+ TEST_PASS(v, <=>);
+}
+
+void f() {
+ test_category(std::strong_ordering::equivalent);
+ test_category(std::weak_ordering::equivalent);
+ test_category(std::partial_ordering::equivalent);
+}
diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp
deleted file mode 100644
index 8e3c793de4b92ae..000000000000000
--- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/zero_type.verify.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In MSVC mode, there's a slightly different number of errors printed for
-// each of these, so it doesn't add up to the exact expected count of 18.
-// XFAIL: msvc
-
-// <compare>
-
-// Ensure we reject all cases where an argument other than a literal 0 is used
-// for a comparison against a comparison category type.
-
-#include <compare>
-
-#define TEST_FAIL(v, op) \
- void(v op 0L); \
- void(0L op v); \
- void(v op nullptr); \
- void(nullptr op v); \
- void(v op(1 - 1)); \
- void((1 - 1) op v)
-
-#define TEST_PASS(v, op) \
- void(v op 0); \
- void(0 op v)
-
-template <typename T>
-void test_category(T v) {
- TEST_FAIL(v, ==); // expected-error 18 {{}}
- TEST_FAIL(v, !=); // expected-error 18 {{}}
- TEST_FAIL(v, <); // expected-error 18 {{}}
- TEST_FAIL(v, <=); // expected-error 18 {{}}
- TEST_FAIL(v, >); // expected-error 18 {{}}
- TEST_FAIL(v, >=); // expected-error 18 {{}}
- TEST_FAIL(v, <=>); // expected-error 18 {{}}
-
- TEST_PASS(v, ==);
- TEST_PASS(v, !=);
- TEST_PASS(v, <);
- TEST_PASS(v, >);
- TEST_PASS(v, <=);
- TEST_PASS(v, >=);
- TEST_PASS(v, <=>);
-}
-
-void f() {
- test_category(std::strong_ordering::equivalent);
- test_category(std::weak_ordering::equivalent);
- test_category(std::partial_ordering::equivalent);
-}
>From 66eecafcb9bd8115cd0b3351ab0b83a95e5da006 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 25 Jan 2024 15:28:09 -0500
Subject: [PATCH 2/3] Use diagnose_if and improve tests
---
libcxx/include/__compare/ordering.h | 19 ++++---
libcxx/include/__config | 6 +++
.../reject-other-than-literal-zero.verify.cpp | 49 +++++++++++++++----
3 files changed, 54 insertions(+), 20 deletions(-)
diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h
index 778cf5dab4ce567..274cff61d2b2888 100644
--- a/libcxx/include/__compare/ordering.h
+++ b/libcxx/include/__compare/ordering.h
@@ -31,16 +31,15 @@ class weak_ordering;
class strong_ordering;
struct _CmpUnspecifiedParam {
- template <class _Tp, class = __enable_if_t<is_same_v<_Tp, int>>>
- _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept {
- // If anything other than 0 is provided, the behavior is undefined.
- // We catch this case by making this function consteval and making the program ill-formed if
- // a value other than 0 is provided. This technically also accepts things that are not
- // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be
- // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`.
- if (__zero != 0)
- __builtin_trap();
- }
+ // If anything other than a literal 0 is provided, the behavior is undefined by the Standard.
+ //
+ // We handle this by making this function consteval and making the program ill-formed if
+ // a value other than 0 is provided. This technically also accepts things that are not
+ // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be
+ // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`.
+ template <class _Tp, class = __enable_if_t<is_same_v<_Tp, int> > >
+ _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept _LIBCPP_DIAGNOSE_ERROR(
+ __zero != 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types") {}
};
class partial_ordering {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index ed6ba7d44c3af35..cb114878fdf1551 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1482,6 +1482,12 @@ __sanitizer_verify_double_ended_contiguous_container(const void*, const void*, c
# define _LIBCPP_DIAGNOSE_WARNING(...)
# endif
+# if __has_attribute(__diagnose_if__)
+# define _LIBCPP_DIAGNOSE_ERROR(...) __attribute__((__diagnose_if__(__VA_ARGS__, "error")))
+# else
+# define _LIBCPP_DIAGNOSE_ERROR(...)
+# endif
+
// Use a function like macro to imply that it must be followed by a semicolon
# if __has_cpp_attribute(fallthrough)
# define _LIBCPP_FALLTHROUGH() [[fallthrough]]
diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
index f2b30284d42a60c..e0010665909389a 100644
--- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
+++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
@@ -26,16 +26,29 @@
#include "test_macros.h"
-#define TEST_FAIL(v, op) \
+#define TEST_FAIL_INVALID_VALUE(v, op) \
do { \
- void(v op 0L); \
- void(0L op v); \
void(v op 1); \
void(1 op v); \
+ } while (false)
+
+#define TEST_FAIL_INVALID_TYPE(v, op) \
+ do { \
+ void(v op 0L); \
+ void(0L op v); \
+ void(v op 0.0); \
+ void(0.0 op v); \
void(v op nullptr); \
void(nullptr op v); \
} while (false)
+#define TEST_FAIL_NOT_COMPILE_TIME(v, op) \
+ do { \
+ int i = 0; \
+ void(v op i); \
+ void(i op v); \
+ } while (false)
+
#define TEST_PASS(v, op) \
do { \
void(v op 0); \
@@ -46,13 +59,29 @@
template <typename T>
void test_category(T v) {
- TEST_FAIL(v, ==); // expected-error 18 {{}}
- TEST_FAIL(v, !=); // expected-error 18 {{}}
- TEST_FAIL(v, <); // expected-error 18 {{}}
- TEST_FAIL(v, <=); // expected-error 18 {{}}
- TEST_FAIL(v, >); // expected-error 18 {{}}
- TEST_FAIL(v, >=); // expected-error 18 {{}}
- TEST_FAIL(v, <=>); // expected-error 18 {{}}
+ TEST_FAIL_INVALID_TYPE(v, ==); // expected-error 18 {{invalid operands to binary expression}}
+ TEST_FAIL_INVALID_TYPE(v, !=); // expected-error 18 {{invalid operands to binary expression}}
+ TEST_FAIL_INVALID_TYPE(v, <); // expected-error 18 {{invalid operands to binary expression}}
+ TEST_FAIL_INVALID_TYPE(v, <=); // expected-error 18 {{invalid operands to binary expression}}
+ TEST_FAIL_INVALID_TYPE(v, >); // expected-error 18 {{invalid operands to binary expression}}
+ TEST_FAIL_INVALID_TYPE(v, >=); // expected-error 18 {{invalid operands to binary expression}}
+ TEST_FAIL_INVALID_TYPE(v, <=>); // expected-error 18 {{invalid operands to binary expression}}
+
+ TEST_FAIL_INVALID_VALUE(v, ==); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+ TEST_FAIL_INVALID_VALUE(v, !=); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+ TEST_FAIL_INVALID_VALUE(v, <); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+ TEST_FAIL_INVALID_VALUE(v, <=); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+ TEST_FAIL_INVALID_VALUE(v, >); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+ TEST_FAIL_INVALID_VALUE(v, >=); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+ TEST_FAIL_INVALID_VALUE(v, <=>); // expected-error 6 {{Only literal 0 is allowed as the operand}}
+
+ TEST_FAIL_NOT_COMPILE_TIME(v, ==); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL_NOT_COMPILE_TIME(v, !=); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL_NOT_COMPILE_TIME(v, <); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL_NOT_COMPILE_TIME(v, <=); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL_NOT_COMPILE_TIME(v, >); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL_NOT_COMPILE_TIME(v, >=); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL_NOT_COMPILE_TIME(v, <=>); // expected-error 6 {{call to consteval function}}
TEST_PASS(v, ==);
TEST_PASS(v, !=);
>From 578ae70545f2eeef4774f1dbb2f4f36783cb9e7a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jan 2024 10:56:41 -0500
Subject: [PATCH 3/3] Use __enable_if__ attribute
---
libcxx/include/__compare/ordering.h | 15 +++---
libcxx/include/__config | 6 ---
.../reject-other-than-literal-zero.verify.cpp | 47 +++++--------------
3 files changed, 22 insertions(+), 46 deletions(-)
diff --git a/libcxx/include/__compare/ordering.h b/libcxx/include/__compare/ordering.h
index 274cff61d2b2888..bea15456b89bae5 100644
--- a/libcxx/include/__compare/ordering.h
+++ b/libcxx/include/__compare/ordering.h
@@ -33,13 +33,16 @@ class strong_ordering;
struct _CmpUnspecifiedParam {
// If anything other than a literal 0 is provided, the behavior is undefined by the Standard.
//
- // We handle this by making this function consteval and making the program ill-formed if
- // a value other than 0 is provided. This technically also accepts things that are not
- // literal 0s like `1 - 1`. The alternative is to use the fact that a pointer can be
- // constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`.
+ // The alternative to the `__enable_if__` attribute would be to use the fact that a pointer
+ // can be constructed from literal 0, but this conflicts with `-Wzero-as-null-pointer-constant`.
template <class _Tp, class = __enable_if_t<is_same_v<_Tp, int> > >
- _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept _LIBCPP_DIAGNOSE_ERROR(
- __zero != 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types") {}
+ _LIBCPP_HIDE_FROM_ABI consteval _CmpUnspecifiedParam(_Tp __zero) noexcept
+# if __has_attribute(__enable_if__)
+ __attribute__((__enable_if__(
+ __zero == 0, "Only literal 0 is allowed as the operand of a comparison with one of the ordering types")))
+# endif
+ {
+ }
};
class partial_ordering {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index cb114878fdf1551..ed6ba7d44c3af35 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1482,12 +1482,6 @@ __sanitizer_verify_double_ended_contiguous_container(const void*, const void*, c
# define _LIBCPP_DIAGNOSE_WARNING(...)
# endif
-# if __has_attribute(__diagnose_if__)
-# define _LIBCPP_DIAGNOSE_ERROR(...) __attribute__((__diagnose_if__(__VA_ARGS__, "error")))
-# else
-# define _LIBCPP_DIAGNOSE_ERROR(...)
-# endif
-
// Use a function like macro to imply that it must be followed by a semicolon
# if __has_cpp_attribute(fallthrough)
# define _LIBCPP_FALLTHROUGH() [[fallthrough]]
diff --git a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
index e0010665909389a..f59f6804b54e9bb 100644
--- a/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
+++ b/libcxx/test/std/language.support/cmp/cmp.categories.pre/reject-other-than-literal-zero.verify.cpp
@@ -26,24 +26,19 @@
#include "test_macros.h"
-#define TEST_FAIL_INVALID_VALUE(v, op) \
- do { \
- void(v op 1); \
- void(1 op v); \
- } while (false)
-
-#define TEST_FAIL_INVALID_TYPE(v, op) \
+#define TEST_FAIL(v, op) \
do { \
+ /* invalid types */ \
void(v op 0L); \
void(0L op v); \
void(v op 0.0); \
void(0.0 op v); \
void(v op nullptr); \
void(nullptr op v); \
- } while (false)
-
-#define TEST_FAIL_NOT_COMPILE_TIME(v, op) \
- do { \
+ /* invalid value */ \
+ void(v op 1); \
+ void(1 op v); \
+ /* value not known at compile-time */ \
int i = 0; \
void(v op i); \
void(i op v); \
@@ -59,29 +54,13 @@
template <typename T>
void test_category(T v) {
- TEST_FAIL_INVALID_TYPE(v, ==); // expected-error 18 {{invalid operands to binary expression}}
- TEST_FAIL_INVALID_TYPE(v, !=); // expected-error 18 {{invalid operands to binary expression}}
- TEST_FAIL_INVALID_TYPE(v, <); // expected-error 18 {{invalid operands to binary expression}}
- TEST_FAIL_INVALID_TYPE(v, <=); // expected-error 18 {{invalid operands to binary expression}}
- TEST_FAIL_INVALID_TYPE(v, >); // expected-error 18 {{invalid operands to binary expression}}
- TEST_FAIL_INVALID_TYPE(v, >=); // expected-error 18 {{invalid operands to binary expression}}
- TEST_FAIL_INVALID_TYPE(v, <=>); // expected-error 18 {{invalid operands to binary expression}}
-
- TEST_FAIL_INVALID_VALUE(v, ==); // expected-error 6 {{Only literal 0 is allowed as the operand}}
- TEST_FAIL_INVALID_VALUE(v, !=); // expected-error 6 {{Only literal 0 is allowed as the operand}}
- TEST_FAIL_INVALID_VALUE(v, <); // expected-error 6 {{Only literal 0 is allowed as the operand}}
- TEST_FAIL_INVALID_VALUE(v, <=); // expected-error 6 {{Only literal 0 is allowed as the operand}}
- TEST_FAIL_INVALID_VALUE(v, >); // expected-error 6 {{Only literal 0 is allowed as the operand}}
- TEST_FAIL_INVALID_VALUE(v, >=); // expected-error 6 {{Only literal 0 is allowed as the operand}}
- TEST_FAIL_INVALID_VALUE(v, <=>); // expected-error 6 {{Only literal 0 is allowed as the operand}}
-
- TEST_FAIL_NOT_COMPILE_TIME(v, ==); // expected-error 6 {{call to consteval function}}
- TEST_FAIL_NOT_COMPILE_TIME(v, !=); // expected-error 6 {{call to consteval function}}
- TEST_FAIL_NOT_COMPILE_TIME(v, <); // expected-error 6 {{call to consteval function}}
- TEST_FAIL_NOT_COMPILE_TIME(v, <=); // expected-error 6 {{call to consteval function}}
- TEST_FAIL_NOT_COMPILE_TIME(v, >); // expected-error 6 {{call to consteval function}}
- TEST_FAIL_NOT_COMPILE_TIME(v, >=); // expected-error 6 {{call to consteval function}}
- TEST_FAIL_NOT_COMPILE_TIME(v, <=>); // expected-error 6 {{call to consteval function}}
+ TEST_FAIL(v, ==); // expected-error 30 {{invalid operands to binary expression}}
+ TEST_FAIL(v, !=); // expected-error 30 {{invalid operands to binary expression}}
+ TEST_FAIL(v, <); // expected-error 30 {{invalid operands to binary expression}}
+ TEST_FAIL(v, <=); // expected-error 30 {{invalid operands to binary expression}}
+ TEST_FAIL(v, >); // expected-error 30 {{invalid operands to binary expression}}
+ TEST_FAIL(v, >=); // expected-error 30 {{invalid operands to binary expression}}
+ TEST_FAIL(v, <=>); // expected-error 30 {{invalid operands to binary expression}}
TEST_PASS(v, ==);
TEST_PASS(v, !=);
More information about the libcxx-commits
mailing list