[libc++] Fix constraint recursion in std::expected's operator==#201455
Conversation
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
|
@llvm/pr-subscribers-libcxx Author: Louis Dionne (ldionne) ChangesThe 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:
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;
}
|
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