[libcxx-commits] [libcxx] [libc++] Fixes LWG4072 (PR #209968)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 15 22:52:23 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Dan Shan (HumanThe2nd)
<details>
<summary>Changes</summary>
Resolves #<!-- -->118345 (LWG4072: std::optional comparisons: constrain harder)
The heterogeneous operators (==, !=, <, <=, >, >=) for arguments T and U don't check that T or U are not a std::optional themselves.
This allowed the operators to cause ambiguous overload resolution instead of falling back to optional's own operators.
Added `!__is_std_optional<_Up>::value` (and the `_Tp` equivalent for the reversed) to all twelve operators.
As required by the LLVM Project's AI use policy:
- The fix and test processes were revised and verified with AI assistance.
---
Full diff: https://github.com/llvm/llvm-project/pull/209968.diff
7 Files Affected:
- (modified) libcxx/include/optional (+24-12)
- (modified) libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp (+3)
- (modified) libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp (+3)
- (modified) libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp (+3)
- (modified) libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp (+3)
- (modified) libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp (+3)
- (modified) libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp (+3)
``````````diff
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2499479348892..eb93af6857751 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -1543,7 +1543,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>&
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Up>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -1554,7 +1555,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Tp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -1565,7 +1567,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Up>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -1576,7 +1579,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Tp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -1586,7 +1590,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_
template < class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Up>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -1596,7 +1601,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _
template < class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Tp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -1607,7 +1613,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_U
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Up>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -1618,7 +1625,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Tp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -1628,7 +1636,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_
template < class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Up>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -1638,7 +1647,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _
template < class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Tp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
@@ -1649,7 +1659,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_U
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Up>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
if (__x.has_value())
@@ -1660,7 +1671,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const
template <
class _Tp,
class _Up,
- enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+ enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool> &&
+ !__is_std_optional<_Tp>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
if (__x.has_value())
diff --git a/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp
index 6915b604d4ef4..dba9cbd78146c 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp
@@ -29,6 +29,9 @@ static_assert(HasOperatorEqual<std::optional<EqualityComparable>, std::optional<
static_assert(!HasOperatorEqual<std::optional<NonComparable>>);
static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, std::optional<NonComparable>>);
+static_assert(!HasOperatorEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp
index d14e7df6abc80..b31f5047f1090 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>>);
static_assert(!HasOperatorGreaterThanEqual<std::optional<EqualityComparable>>);
static_assert(!HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp
index f19ea23e8ae78..dd0e039d1d342 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>>);
static_assert(!HasOperatorGreaterThan<std::optional<EqualityComparable>>);
static_assert(!HasOperatorGreaterThan<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
+static_assert(!HasOperatorGreaterThan<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorGreaterThan<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp
index bd81a79f1265e..9e472e158e3b5 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>>);
static_assert(!HasOperatorLessThanEqual<std::optional<EqualityComparable>>);
static_assert(!HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
+static_assert(!HasOperatorLessThanEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorLessThanEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp
index c87bfd6f73e54..9c231dff56f51 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorLessThan<std::optional<NonComparable>>);
static_assert(!HasOperatorLessThan<std::optional<EqualityComparable>>);
static_assert(!HasOperatorLessThan<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
+static_assert(!HasOperatorLessThan<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorLessThan<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp
index 1b7d2621c9193..6507a61426f87 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp
@@ -29,6 +29,9 @@ static_assert(HasOperatorNotEqual<std::optional<EqualityComparable>, std::option
static_assert(!HasOperatorNotEqual<std::optional<NonComparable>>);
static_assert(!HasOperatorNotEqual<std::optional<EqualityComparable>, std::optional<NonComparable>>);
+static_assert(!HasOperatorNotEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorNotEqual<std::optional<int>, std::optional<void*>>);
+
#endif
using std::optional;
``````````
</details>
https://github.com/llvm/llvm-project/pull/209968
More information about the libcxx-commits
mailing list