[libcxx-commits] [libcxx] 2dce119 - [libc++] Fix constraint recursion in std::expected's operator== (#201455)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jun 4 13:51:48 PDT 2026


Author: Louis Dionne
Date: 2026-06-04T16:51:43-04:00
New Revision: 2dce1190b0aae797996023783731efbfb8e33a8f

URL: https://github.com/llvm/llvm-project/commit/2dce1190b0aae797996023783731efbfb8e33a8f
DIFF: https://github.com/llvm/llvm-project/commit/2dce1190b0aae797996023783731efbfb8e33a8f.diff

LOG: [libc++] Fix constraint recursion in std::expected's operator== (#201455)

The C++26 constraint added to operator==(const expected& x, const T2& v)
by P3379R0 evaluates *x == v as part of constraint satisfaction. When
ADL on a comparison reaches this hidden friend through a type whose
associated namespaces include std::expected -- for example std::pair<T,
std::expected<U, V>> -- the constraint check ends up considering the
same overload again with the original type as T2, producing a
"satisfaction of constraint depends on itself" error.

Parameterize the expected operand with an extra template parameter
constrained to be the same type as the enclosing expected's value type.
This is observationally equivalent but makes template argument deduction
fail for non-expected operands before the constraint is evaluated, so
the recursion never starts.

Fixes #160431
rdar://178226313

Assisted-by: Claude

Added: 
    

Modified: 
    libcxx/include/__expected/expected.h
    libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 24ae33d4e3af8..27bc086698399 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___EXPECTED_EXPECTED_H
 
 #include <__assert>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__expected/bad_expected_access.h>
 #include <__expected/unexpect.h>
@@ -1161,8 +1162,11 @@ class expected : private __expected_base<_Tp, _Err> {
     }
   }
 
-  template <class _T2>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v)
+  // The unusual signature avoids constraint recursion via ADL through
+  // std::expected, see https://llvm.org/PR160431. Note that this only
+  // triggers with compilers that implement https://wg21.link/CWG2369.
+  template <class _T2, same_as<_Tp> _Tp2>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected<_Tp2, _Err>& __x, const _T2& __v)
 #  if _LIBCPP_STD_VER >= 26
     requires(!__is_std_expected<_T2>::value) && requires {
       { *__x == __v } -> __core_convertible_to<bool>;

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp
index 16c6986ae670e..6a57dc4643354 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp
@@ -45,6 +45,16 @@ constexpr bool test() {
     assert(e1 != i3);
   }
 
+#if TEST_STD_VER >= 26
+  // Regression test for https://llvm.org/PR160431: the constraint of this overload
+  // would recurse when ADL on the comparison found the same hidden friend through a
+  // type whose associated namespaces reach std::expected.
+  {
+    std::pair<int, std::expected<int, int>> p1, p2;
+    assert(p1 == p2);
+  }
+#endif
+
   return true;
 }
 


        


More information about the libcxx-commits mailing list