https://gcc.gnu.org/g:230dc6e74f73553cf96ae5db7b6f0ab6e1cbb97b

commit r16-5551-g230dc6e74f73553cf96ae5db7b6f0ab6e1cbb97b
Author: Jonathan Wakely <[email protected]>
Date:   Thu Nov 20 12:19:54 2025 +0000

    libstdc++: Implement LWG 4370 for std::optional comparisons
    
    This modifies the relational comparisons for std::optional so that they
    do not use logical expressions with && or || that involve the
    comparisons on the contained values, because x && (*y == *z) might do
    the wrong thing if *y == *z does not return bool.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/optional (operator==, operator!=, operator>)
            (operator>, operator<=, operator>=): Do not use logical
            && and || with operands of unknown types.
            * testsuite/20_util/optional/relops/lwg4370.cc: New test.
    
    Reviewed-by: Tomasz KamiƄski <[email protected]>

Diff:
---
 libstdc++-v3/include/std/optional                  | 110 +++++++++++++++++----
 .../testsuite/20_util/optional/relops/lwg4370.cc   |  55 +++++++++++
 2 files changed, 145 insertions(+), 20 deletions(-)

diff --git a/libstdc++-v3/include/std/optional 
b/libstdc++-v3/include/std/optional
index f54dda6cd237..8b9fa9244213 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -1855,8 +1855,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator==(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
     -> __optional_eq_t<_Tp, _Up>
     {
-      return static_cast<bool>(__lhs) == static_cast<bool>(__rhs)
-            && (!__lhs || *__lhs == *__rhs);
+      if (__lhs.has_value() != __rhs.has_value())
+       return false;
+      if (!__lhs.has_value())
+       return true;
+      return *__lhs == *__rhs;
     }
 
   template<typename _Tp, typename _Up>
@@ -1864,8 +1867,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator!=(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
     -> __optional_ne_t<_Tp, _Up>
     {
-      return static_cast<bool>(__lhs) != static_cast<bool>(__rhs)
-       || (static_cast<bool>(__lhs) && *__lhs != *__rhs);
+      if (__lhs.has_value() != __rhs.has_value())
+       return true;
+      if (!__lhs.has_value())
+       return false;
+      return *__lhs != *__rhs;
     }
 
   template<typename _Tp, typename _Up>
@@ -1873,7 +1879,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator<(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
     -> __optional_lt_t<_Tp, _Up>
     {
-      return static_cast<bool>(__rhs) && (!__lhs || *__lhs < *__rhs);
+      if (!__rhs.has_value())
+       return false;
+      if (!__lhs.has_value())
+       return true;
+      return *__lhs < *__rhs;
     }
 
   template<typename _Tp, typename _Up>
@@ -1881,7 +1891,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator>(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
     -> __optional_gt_t<_Tp, _Up>
     {
-      return static_cast<bool>(__lhs) && (!__rhs || *__lhs > *__rhs);
+      if (!__lhs.has_value())
+       return false;
+      if (!__rhs.has_value())
+       return true;
+      return *__lhs > *__rhs;
     }
 
   template<typename _Tp, typename _Up>
@@ -1889,7 +1903,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator<=(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
     -> __optional_le_t<_Tp, _Up>
     {
-      return !__lhs || (static_cast<bool>(__rhs) && *__lhs <= *__rhs);
+      if (!__lhs.has_value())
+       return true;
+      if (!__rhs.has_value())
+       return false;
+      return *__lhs <= *__rhs;
     }
 
   template<typename _Tp, typename _Up>
@@ -1897,7 +1915,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator>=(const optional<_Tp>& __lhs, const optional<_Up>& __rhs)
     -> __optional_ge_t<_Tp, _Up>
     {
-      return !__rhs || (static_cast<bool>(__lhs) && *__lhs >= *__rhs);
+      if (!__rhs.has_value())
+       return true;
+      if (!__lhs.has_value())
+       return false;
+      return *__lhs >= *__rhs;
     }
 
 #ifdef __cpp_lib_three_way_comparison
@@ -1994,84 +2016,132 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr auto
     operator== [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
     -> __optional_eq_t<_Tp, _Up>
-    { return __lhs && *__lhs == __rhs; }
+    {
+      if (__lhs.has_value())
+       return *__lhs == __rhs;
+      return false;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Tp)
     constexpr auto
     operator== [[nodiscard]] (const _Tp& __lhs, const optional<_Up>& __rhs)
     -> __optional_eq_t<_Tp, _Up>
-    { return __rhs && __lhs == *__rhs; }
+    {
+      if (__rhs.has_value())
+       return __lhs == *__rhs;
+      return false;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Up)
     constexpr auto
     operator!= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
     -> __optional_ne_t<_Tp, _Up>
-    { return !__lhs || *__lhs != __rhs; }
+    {
+      if (__lhs.has_value())
+       return *__lhs != __rhs;
+      return true;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Tp)
     constexpr auto
     operator!= [[nodiscard]] (const _Tp& __lhs, const optional<_Up>& __rhs)
     -> __optional_ne_t<_Tp, _Up>
-    { return !__rhs || __lhs != *__rhs; }
+    {
+      if (__rhs.has_value())
+       return __lhs != *__rhs;
+      return true;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Up)
     constexpr auto
     operator< [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
     -> __optional_lt_t<_Tp, _Up>
-    { return !__lhs || *__lhs < __rhs; }
+    {
+      if (__lhs.has_value())
+       return *__lhs < __rhs;
+      return true;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Tp)
     constexpr auto
     operator< [[nodiscard]] (const _Tp& __lhs, const optional<_Up>& __rhs)
     -> __optional_lt_t<_Tp, _Up>
-    { return __rhs && __lhs < *__rhs; }
+    {
+      if (__rhs.has_value())
+       return __lhs < *__rhs;
+      return false;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Up)
     constexpr auto
     operator> [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
     -> __optional_gt_t<_Tp, _Up>
-    { return __lhs && *__lhs > __rhs; }
+    {
+      if (__lhs.has_value())
+       return *__lhs > __rhs;
+      return false;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Tp)
     constexpr auto
     operator> [[nodiscard]] (const _Tp& __lhs, const optional<_Up>& __rhs)
     -> __optional_gt_t<_Tp, _Up>
-    { return !__rhs || __lhs > *__rhs; }
+    {
+      if (__rhs.has_value())
+       return __lhs > *__rhs;
+      return true;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Up)
     constexpr auto
     operator<= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
     -> __optional_le_t<_Tp, _Up>
-    { return !__lhs || *__lhs <= __rhs; }
+    {
+      if (__lhs.has_value())
+       return *__lhs <= __rhs;
+      return true;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Tp)
     constexpr auto
     operator<= [[nodiscard]] (const _Tp& __lhs, const optional<_Up>& __rhs)
     -> __optional_le_t<_Tp, _Up>
-    { return __rhs && __lhs <= *__rhs; }
+    {
+      if (__rhs.has_value())
+       return __lhs <= *__rhs;
+      return false;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Up)
     constexpr auto
     operator>= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
     -> __optional_ge_t<_Tp, _Up>
-    { return __lhs && *__lhs >= __rhs; }
+    {
+      if (__lhs.has_value())
+       return *__lhs >= __rhs;
+      return false;
+    }
 
   template<typename _Tp, typename _Up>
     _REQUIRES_NOT_OPTIONAL(_Tp)
     constexpr auto
     operator>= [[nodiscard]] (const _Tp& __lhs, const optional<_Up>& __rhs)
     -> __optional_ge_t<_Tp, _Up>
-    { return !__rhs || __lhs >= *__rhs; }
+    {
+      if (__rhs.has_value())
+       return __lhs >= *__rhs;
+      return true;
+    }
 
 #ifdef __cpp_lib_three_way_comparison
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/lwg4370.cc 
b/libstdc++-v3/testsuite/20_util/optional/relops/lwg4370.cc
new file mode 100644
index 000000000000..a292cbe6b404
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/relops/lwg4370.cc
@@ -0,0 +1,55 @@
+// { dg-do compile { target c++17 } }
+
+// LWG 4370. Comparison of optional<T> to T may be ill-formed
+
+#include <optional>
+#include <testsuite_hooks.h>
+
+struct Bool
+{
+  Bool(bool);
+  operator bool() const;
+};
+
+template<typename T> void operator&&(Bool, T) = delete;
+template<typename T> void operator&&(T, Bool) = delete;
+template<typename T> void operator||(Bool, T) = delete;
+template<typename T> void operator||(T, Bool) = delete;
+
+struct S
+{
+  Bool operator==(S) const;
+  Bool operator!=(S) const;
+  Bool operator<(S) const;
+  Bool operator>(S) const;
+  Bool operator<=(S) const;
+  Bool operator>=(S) const;
+};
+
+void
+test_lwg4370()
+{
+  std::optional<S> o;
+  (void)(o == o);
+  (void)(o != o);
+  (void)(o < o);
+  (void)(o > o);
+  (void)(o <= o);
+  (void)(o >= o);
+
+  S s;
+  (void)(o == s);
+  (void)(s == o);
+  (void)(o != s);
+  (void)(s != o);
+  (void)(o < s);
+  (void)(s < o);
+  (void)(o > s);
+  (void)(s > o);
+  (void)(o <= s);
+  (void)(s <= o);
+  (void)(o >= s);
+  (void)(s >= o);
+}
+
+

Reply via email to