This fixes a regression introduced when I replaced __normal_iterator's relational operators with operator<=>. If the wrapped iterator type doesn't define operator<=> then __normal_iterator doesdn't either, which breaks any use of fancy pointers that don't define <=>. The regression was found when trying to build cmcstl2.
The solution is to use synth-three-way to define __normal_iterator's spaceship operator, so that it is still defined even if the wrapped type only supports operator<. * include/bits/stl_iterator.h (__normal_iterator): Use synth-three-way to define operator<=>. * testsuite/24_iterators/normal_iterator/cmp_c++20.cc: New test. Tested powerpc64le-linux, committed to master.
commit 87841658d4fa5174d1797ee0abc73b3b3f11cad4 Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Apr 21 23:43:27 2020 +0100 libstdc++: Fix __normal_iterator comparisons for C++20 This fixes a regression introduced when I replaced __normal_iterator's relational operators with operator<=>. If the wrapped iterator type doesn't define operator<=> then __normal_iterator doesdn't either, which breaks any use of fancy pointers that don't define <=>. The regression was found when trying to build cmcstl2. The solution is to use synth-three-way to define __normal_iterator's spaceship operator, so that it is still defined even if the wrapped type only supports operator<. * include/bits/stl_iterator.h (__normal_iterator): Use synth-three-way to define operator<=>. * testsuite/24_iterators/normal_iterator/cmp_c++20.cc: New test. diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index 5bfdce6af2d..652f51c6e7f 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -1048,12 +1048,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __lhs.base() == __rhs.base(); } template<typename _IteratorL, typename _IteratorR, typename _Container> - constexpr auto + constexpr std::__detail::__synth3way_t<_IteratorR, _IteratorL> operator<=>(const __normal_iterator<_IteratorL, _Container>& __lhs, const __normal_iterator<_IteratorR, _Container>& __rhs) - noexcept(noexcept(__lhs.base() <=> __rhs.base())) - -> decltype(__lhs.base() <=> __rhs.base()) - { return __lhs.base() <=> __rhs.base(); } + noexcept(noexcept(std::__detail::__synth3way(__lhs.base(), __rhs.base()))) + { return std::__detail::__synth3way(__lhs.base(), __rhs.base()); } #else // Forward iterator requirements template<typename _IteratorL, typename _IteratorR, typename _Container> diff --git a/libstdc++-v3/testsuite/24_iterators/normal_iterator/cmp_c++20.cc b/libstdc++-v3/testsuite/24_iterators/normal_iterator/cmp_c++20.cc new file mode 100644 index 00000000000..a5014e8ae99 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/normal_iterator/cmp_c++20.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <iterator> +#include <vector> +#include <testsuite_allocator.h> + +void +test01() +{ + using V = std::vector<int>; + static_assert( std::totally_ordered<V::iterator> ); + static_assert( std::three_way_comparable<V::iterator> ); + using C = std::compare_three_way_result_t<V::iterator>; + static_assert( std::same_as<C, std::strong_ordering> ); + + static_assert( std::random_access_iterator<V::iterator> ); + static_assert( std::random_access_iterator<V::const_iterator> ); +} + +// User-defined pointer type that supports operator< but not operator<=> +template<typename T> +struct Pointer : __gnu_test::PointerBase<Pointer<T>, T> +{ + using __gnu_test::PointerBase<Pointer<T>, T>::PointerBase; + + friend bool operator<(const Pointer& lhs, const Pointer& rhs) noexcept + { return lhs.value < rhs.value; } + + std::partial_ordering operator<=>(const Pointer&) const = delete; +}; + +// Minimal allocator using Pointer<T> +template<typename T> +struct Alloc +{ + typedef T value_type; + typedef Pointer<T> pointer; + + Alloc() = default; + template<typename U> + Alloc(const Alloc<U>&) { } + + pointer allocate(std::size_t n) + { return pointer(std::allocator<T>().allocate(n)); } + + void deallocate(pointer p, std::size_t n) + { std::allocator<T>().deallocate(p.operator->(), n); } +}; + +void +test02() +{ + using V = std::vector<int, Alloc<int>>; + static_assert( std::totally_ordered<V::iterator> ); + static_assert( std::three_way_comparable<V::iterator> ); + using C = std::compare_three_way_result_t<V::iterator>; + static_assert( std::same_as<C, std::weak_ordering> ); + + static_assert( std::random_access_iterator<V::iterator> ); + static_assert( std::random_access_iterator<V::const_iterator> ); +} + +void +test03() +{ + struct P : Pointer<int> { + bool operator<(const P&) const = delete; + }; + + struct C { + using pointer = P; + }; + + using I = __gnu_cxx::__normal_iterator<P, C>; + static_assert( ! std::totally_ordered<I> ); + static_assert( ! std::three_way_comparable<I> ); +}