From 87841658d4fa5174d1797ee0abc73b3b3f11cad4 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 21 Apr 2020 23:43:27 +0100 Subject: [PATCH] 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. --- libstdc++-v3/ChangeLog | 4 + libstdc++-v3/include/bits/stl_iterator.h | 7 +- .../24_iterators/normal_iterator/cmp_c++20.cc | 95 +++++++++++++++++++ 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 libstdc++-v3/testsuite/24_iterators/normal_iterator/cmp_c++20.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 4e0a02fef2d..55df9a31244 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,9 @@ 2020-04-21 Jonathan Wakely + * 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. + * doc/Makefile.am (xml_sources_manual): Add missing XML files. * doc/Makefile.in: Regenerate. * doc/xml/manual/status_cxx1998.xml: Refer to "this section" instead 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 - 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 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include +#include +#include + +void +test01() +{ + using V = std::vector; + static_assert( std::totally_ordered ); + static_assert( std::three_way_comparable ); + using C = std::compare_three_way_result_t; + static_assert( std::same_as ); + + static_assert( std::random_access_iterator ); + static_assert( std::random_access_iterator ); +} + +// User-defined pointer type that supports operator< but not operator<=> +template +struct Pointer : __gnu_test::PointerBase, T> +{ + using __gnu_test::PointerBase, 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 +template +struct Alloc +{ + typedef T value_type; + typedef Pointer pointer; + + Alloc() = default; + template + Alloc(const Alloc&) { } + + pointer allocate(std::size_t n) + { return pointer(std::allocator().allocate(n)); } + + void deallocate(pointer p, std::size_t n) + { std::allocator().deallocate(p.operator->(), n); } +}; + +void +test02() +{ + using V = std::vector>; + static_assert( std::totally_ordered ); + static_assert( std::three_way_comparable ); + using C = std::compare_three_way_result_t; + static_assert( std::same_as ); + + static_assert( std::random_access_iterator ); + static_assert( std::random_access_iterator ); +} + +void +test03() +{ + struct P : Pointer { + bool operator<(const P&) const = delete; + }; + + struct C { + using pointer = P; + }; + + using I = __gnu_cxx::__normal_iterator; + static_assert( ! std::totally_ordered ); + static_assert( ! std::three_way_comparable ); +} -- 2.30.2