libstdc++: Fix constraint recursion in basic_const_iterator relops [PR112490]

Here for

  using RCI = reverse_iterator<basic_const_iterator<vector<int>::iterator>>
  static_assert(std::totally_ordered<RCI>);

we effectively need to check the requirement

  requires (RCI x) { x RELOP x; }  for each RELOP in {<, >, <=, >=}

which we expect to be straightforwardly satisfied by reverse_iterator's
namespace-scope relops.  But due to ADL we find ourselves also
considering the basic_const_iterator relop friends, which before CWG
2369 would be quickly discarded since RCI clearly isn't convertible to
basic_const_iterator.  After CWG 2369 though we must first check these
relops' constraints (with _It = vector<int>::iterator and _It2 = RCI),
which entails checking totally_ordered<RCI> recursively.

This patch fixes this by turning the problematic non-dependent function
parameters of type basic_const_iterator<_It> into dependent ones of
type basic_const_iterator<_It3> where _It3 is constrained to match _It.
Thus the basic_const_iterator relop friends now get quickly discarded
during deduction and before the constraint check if the second operand
isn't a specialization of basic_const_iterator (or derived from one)
like before CWG 2369.

	PR libstdc++/112490

libstdc++-v3/ChangeLog:

	* include/bits/stl_iterator.h (basic_const_iterator::operator<):
	Replace non-dependent basic_const_iterator function parameter with
	a dependent one of type basic_const_iterator<_It3> where _It3
	matches _It.
	(basic_const_iterator::operator>): Likewise.
	(basic_const_iterator::operator<=): Likewise.
	(basic_const_iterator::operator>=): Likewise.
	* testsuite/24_iterators/const_iterator/112490.cc: New test.

Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
This commit is contained in:
Patrick Palka 2025-02-28 09:39:57 -05:00
parent 7eb8ec1856
commit 4342c50ca8
2 changed files with 20 additions and 8 deletions

View file

@ -2881,30 +2881,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
&& three_way_comparable_with<_It, _It2>
{ return _M_current <=> __y; }
template<__detail::__not_a_const_iterator _It2>
template<__detail::__not_a_const_iterator _It2, same_as<_It> _It3>
friend constexpr bool
operator<(const _It2& __x, const basic_const_iterator& __y)
operator<(const _It2& __x, const basic_const_iterator<_It3>& __y)
noexcept(noexcept(__x < __y._M_current))
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
{ return __x < __y._M_current; }
template<__detail::__not_a_const_iterator _It2>
template<__detail::__not_a_const_iterator _It2, same_as<_It> _It3>
friend constexpr bool
operator>(const _It2& __x, const basic_const_iterator& __y)
operator>(const _It2& __x, const basic_const_iterator<_It3>& __y)
noexcept(noexcept(__x > __y._M_current))
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
{ return __x > __y._M_current; }
template<__detail::__not_a_const_iterator _It2>
template<__detail::__not_a_const_iterator _It2, same_as<_It> _It3>
friend constexpr bool
operator<=(const _It2& __x, const basic_const_iterator& __y)
operator<=(const _It2& __x, const basic_const_iterator<_It3>& __y)
noexcept(noexcept(__x <= __y._M_current))
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
{ return __x <= __y._M_current; }
template<__detail::__not_a_const_iterator _It2>
template<__detail::__not_a_const_iterator _It2, same_as<_It> _It3>
friend constexpr bool
operator>=(const _It2& __x, const basic_const_iterator& __y)
operator>=(const _It2& __x, const basic_const_iterator<_It3>& __y)
noexcept(noexcept(__x >= __y._M_current))
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
{ return __x >= __y._M_current; }

View file

@ -0,0 +1,12 @@
// { dg-do compile { target c++23 } }
// PR libstdc++/112490 - infinite meta error in
// reverse_iterator<basic_const_iterator<vector<int>::iterator>>
#include <iterator>
#include <vector>
using I = std::vector<int>::iterator;
using CI = std::basic_const_iterator<I>;
using RCI = std::reverse_iterator<CI>;
static_assert(std::totally_ordered<RCI>);