diff mbox series

[committed] libstdc++: Implement LWG 3746 for std::optional

Message ID 20240823124512.966125-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Implement LWG 3746 for std::optional | expand

Commit Message

Jonathan Wakely Aug. 23, 2024, 12:44 p.m. UTC
Tested x86_64-linux. Pushed to trunk.

-- >8 --

This avoids constraint recursion in operator<=> for std::optional.
The resolution was approved in Kona 2022.

libstdc++-v3/ChangeLog:

	* include/std/optional (__is_derived_from_optional): New
	concept.
	(operator<=>): Use __is_derived_from_optional.
	* testsuite/20_util/optional/relops/lwg3746.cc: New test.
---
 libstdc++-v3/include/std/optional             | 12 +++++++++--
 .../20_util/optional/relops/lwg3746.cc        | 20 +++++++++++++++++++
 2 files changed, 30 insertions(+), 2 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/optional/relops/lwg3746.cc
diff mbox series

Patch

diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional
index 6651686cd1d..933a5b15e56 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -1581,9 +1581,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return !__rhs; }
 #endif // three-way-comparison
 
+#if __cpp_lib_concepts
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
   // 4072. std::optional comparisons: constrain harder
-#if __cpp_lib_concepts
 # define _REQUIRES_NOT_OPTIONAL(T) requires (!__is_optional_v<T>)
 #else
 # define _REQUIRES_NOT_OPTIONAL(T)
@@ -1675,8 +1675,16 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return !__rhs || __lhs >= *__rhs; }
 
 #ifdef __cpp_lib_three_way_comparison
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // 3746. optional's spaceship with U with a type derived from optional
+  // causes infinite constraint meta-recursion
+  template<typename _Tp>
+    concept __is_derived_from_optional = requires (const _Tp& __t) {
+      []<typename _Up>(const optional<_Up>&){ }(__t);
+    };
+
   template<typename _Tp, typename _Up>
-    requires (!__is_optional_v<_Up>)
+    requires (!__is_derived_from_optional<_Up>)
       && three_way_comparable_with<_Up, _Tp>
     constexpr compare_three_way_result_t<_Tp, _Up>
     operator<=> [[nodiscard]] (const optional<_Tp>& __x, const _Up& __v)
diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/lwg3746.cc b/libstdc++-v3/testsuite/20_util/optional/relops/lwg3746.cc
new file mode 100644
index 00000000000..46065f8e901
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/relops/lwg3746.cc
@@ -0,0 +1,20 @@ 
+// { dg-do compile { target c++20 } }
+
+// LWG 3746. optional's spaceship with U with a type derived from optional
+// causes infinite constraint meta-recursion
+
+#include <optional>
+
+struct S : std::optional<char>
+{
+    bool operator==(const S&) const;
+    bool operator<(const S&) const;
+    bool operator>(const S&) const;
+    bool operator<=(const S&) const;
+    bool operator>=(const S&) const;
+};
+
+auto cmp(const S& s, const std::optional<char>& o)
+{
+  return s <=> o;
+}