https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112490

Barry Revzin <barry.revzin at gmail dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |barry.revzin at gmail dot com

--- Comment #3 from Barry Revzin <barry.revzin at gmail dot com> ---
I ran into this also in a different direction, have been trying to reduce:

#include <compare>

template <class T, class U> concept my_partially_ordered_with = requires (T t,
U u) { t < u; };
template <class T> concept my_totally_ordered = my_partially_ordered_with<T,
T>;
template <class T, class U> concept my_totally_ordered_with =
my_totally_ordered<T> && my_totally_ordered<U> && my_partially_ordered_with<T,
U>;

template<class _It> class basic_const_iterator;

namespace __detail
{
template<typename _Tp>
    inline constexpr bool __is_const_iterator = false;

template<typename _It>
    inline constexpr bool __is_const_iterator<basic_const_iterator<_It>> =
true;

template<typename _Tp>
    concept __not_a_const_iterator = !__is_const_iterator<_Tp>;

} // namespace detail

template<class _It>
class basic_const_iterator
{
#ifdef MAKE_THIS_PUBLIC
public:
#endif
    int m;

public:
    template <__detail::__not_a_const_iterator _It2>
    friend bool operator<(const _It2& __x, const basic_const_iterator& __y)
requires my_totally_ordered_with<_It, _It2> { return true; }
};

template <class Iter>
struct wrapped
{
    Iter iter;
    constexpr std::strong_ordering operator<=>(const wrapped& rhs) const
noexcept;
};

bool check(wrapped<basic_const_iterator<int*>> x) {
    return x < x;
}


gcc rejects this with constraint recursion. clang and MSVC accept. Oddly, if
you make _M_current public, gcc trunk accepts (even though no code even
references this) while gcc 13.2 still rejects. On compiler explorer:
https://godbolt.org/z/zfvxdGneK

Trunk's rejection message is also incomplete:

<source>: In substitution of 'template<class _It2>  requires 
__not_a_const_iterator<_It2> bool operator<(const _It2&, const
basic_const_iterator<int*>&) requires  my_totally_ordered_with<_It, _It2> [with
_It2 = int*]':
<source>:3:89:   required by substitution of 'template<class _It2>  requires 
__not_a_const_iterator<_It2> bool operator<(const _It2&, const
basic_const_iterator<int*>&) requires  my_totally_ordered_with<_It, _It2> [with
_It2 = int*]'
    3 | template <class T, class U> concept my_partially_ordered_with =
requires (T t, U u) { t < u; };
      |                                                                        
              ~~^~~
<source>:43:16:   required from here
   43 |     return x < x;
      |                ^

It tells you _It2=int*, but not what _It is. 

In this case, we're comparing two objects of type
wrapped<basic_const_iterator<int*>> with <, so we should have two candidates:

1. wrapped's <=>, which has no constraints
2. basic_const_iterator<int*>'s friend operator<, which we start instantiating
with _It=int* and _It2=wrapped<basic_const_iterator<int*>>. 

(2) requires checking if _It and _It2 can be ordered, which I think recursively
instantiates itself.

Assuming that's correct (hopefully?), I think CWG 2369 should actually cause
this to be accepted - since wrapped<basic_const_iterator<int*>> is not
convertible to basic_const_iterator<int*>, that should cause (2) to be rejected
before we even consider constraints since it's not viable. 

I have no idea what the public-ness of the member changes.

Reply via email to