Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Merged
ldionne merged 2 commits into
llvm:mainfrom
ldionne:review/expected-operator-eq-recursion
Jun 4, 2026
Merged

[libc++] Fix constraint recursion in std::expected's operator==#201455
ldionne merged 2 commits into
llvm:mainfrom
ldionne:review/expected-operator-eq-recursion

Conversation

@ldionne
Copy link
Copy Markdown
Member

@ldionne ldionne commented Jun 3, 2026

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

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 llvm#160431

rdar://178226313

Assisted-by: Claude
@ldionne ldionne requested a review from a team as a code owner June 3, 2026 21:28
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jun 3, 2026
@llvmorg-github-actions
Copy link
Copy Markdown

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/201455.diff

2 Files Affected:

  • (modified) libcxx/include/__expected/expected.h (+5-2)
  • (modified) libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp (+11)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 24ae33d4e3af8..063f1c2f7e630 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -1161,8 +1161,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://github.com/llvm/llvm-project/issues/160431.
+  template <class _T2, class _Tp2>
+    requires _IsSame<_Tp2, _Tp>::value
+  _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..53451be96144e 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,17 @@ constexpr bool test() {
     assert(e1 != i3);
   }
 
+#if TEST_STD_VER >= 26
+  // Regression test for https://github.com/llvm/llvm-project/issues/160431:
+  // 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;
 }
 

Comment thread libcxx/include/__expected/expected.h Outdated
Comment thread libcxx/include/__expected/expected.h
Comment thread libcxx/include/__expected/expected.h
@ldionne ldionne merged commit 2dce119 into llvm:main Jun 4, 2026
80 checks passed
@ldionne ldionne deleted the review/expected-operator-eq-recursion branch June 4, 2026 20:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clang++/libc++ 21, c++26 operator==(pair<T, expected<U, V>>, pair<T, expected<U, V>>) compilation error

3 participants