From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 49394384783B for ; Wed, 26 May 2021 19:36:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 49394384783B Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-33-9Q2zLJTYOdioXG0CK72vvw-1; Wed, 26 May 2021 15:36:22 -0400 X-MC-Unique: 9Q2zLJTYOdioXG0CK72vvw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 5FADB8015F8; Wed, 26 May 2021 19:36:21 +0000 (UTC) Received: from localhost (unknown [10.33.36.24]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9E7FEE2C3; Wed, 26 May 2021 19:36:20 +0000 (UTC) Date: Wed, 26 May 2021 20:36:19 +0100 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Change [range.iter.op] functions to function objects [PR 100768] Message-ID: MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: multipart/mixed; boundary="tOkXZJAlsRMXVH1R" Content-Disposition: inline X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_SBL, URIBL_SBL_A, URI_HEX autolearn=unavailable autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 26 May 2021 19:36:30 -0000 --tOkXZJAlsRMXVH1R Content-Type: text/plain; charset=us-ascii Content-Disposition: inline The standard specifies std::ranges::distance etc as function templates, but it also requires them to not be found by ADL, and to suppress ADL when normal unqualified lookup does find them. That means they need to be function objects. libstdc++-v3/ChangeLog: PR libstdc++/100768 * include/bits/ranges_base.h (advance, distance, next, prev): Replace function templates with function objects. * testsuite/24_iterators/headers/iterator/synopsis_c++20.cc: Adjust for changes to function objects. * testsuite/std/ranges/adaptors/elements.cc: Add using declarations for names from namespace ranges. * testsuite/std/ranges/adaptors/transform.cc: Likewise. * testsuite/24_iterators/range_operations/100768.cc: New test. Tested powerpc64le-linux. Committed to trunk. --tOkXZJAlsRMXVH1R Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="patch.txt" commit a49a045b92f982f5617c3bbde97a33157237e25b Author: Jonathan Wakely Date: Wed May 26 17:32:53 2021 libstdc++: Change [range.iter.op] functions to function objects [PR 100768] The standard specifies std::ranges::distance etc as function templates, but it also requires them to not be found by ADL, and to suppress ADL when normal unqualified lookup does find them. That means they need to be function objects. libstdc++-v3/ChangeLog: PR libstdc++/100768 * include/bits/ranges_base.h (advance, distance, next, prev): Replace function templates with function objects. * testsuite/24_iterators/headers/iterator/synopsis_c++20.cc: Adjust for changes to function objects. * testsuite/std/ranges/adaptors/elements.cc: Add using declarations for names from namespace ranges. * testsuite/std/ranges/adaptors/transform.cc: Likewise. * testsuite/24_iterators/range_operations/100768.cc: New test. diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index 9e5461f7548..11f05fa4e2d 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -674,197 +674,213 @@ namespace ranges // [range.iter.ops] range iterator operations - template - constexpr void - advance(_It& __it, iter_difference_t<_It> __n) - { - if constexpr (random_access_iterator<_It>) - __it += __n; - else if constexpr (bidirectional_iterator<_It>) - { - if (__n > 0) - { - do - { - ++__it; - } - while (--__n); - } - else if (__n < 0) - { - do - { - --__it; - } - while (++__n); - } - } - else - { - // cannot decrement a non-bidirectional iterator - __glibcxx_assert(__n >= 0); - while (__n-- > 0) - ++__it; - } - } - - template _Sent> - constexpr void - advance(_It& __it, _Sent __bound) - { - if constexpr (assignable_from<_It&, _Sent>) - __it = std::move(__bound); - else if constexpr (sized_sentinel_for<_Sent, _It>) - ranges::advance(__it, __bound - __it); - else - { - while (__it != __bound) - ++__it; - } - } - - template _Sent> - constexpr iter_difference_t<_It> - advance(_It& __it, iter_difference_t<_It> __n, _Sent __bound) - { - if constexpr (sized_sentinel_for<_Sent, _It>) - { - const auto __diff = __bound - __it; -#ifdef __cpp_lib_is_constant_evaluated - if (std::is_constant_evaluated() - && !(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0))) - throw "inconsistent directions for distance and bound"; -#endif - // n and bound must not lead in opposite directions: - __glibcxx_assert(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0)); - const auto __absdiff = __diff < 0 ? -__diff : __diff; - const auto __absn = __n < 0 ? -__n : __n;; - if (__absn >= __absdiff) - { - ranges::advance(__it, __bound); - return __n - __diff; - } - else - { - ranges::advance(__it, __n); - return 0; - } - } - else if (__it == __bound || __n == 0) - return iter_difference_t<_It>(0); - else if (__n > 0) - { - iter_difference_t<_It> __m = 0; - do - { + struct __advance_fn + { + template + constexpr void + operator()(_It& __it, iter_difference_t<_It> __n) const + { + if constexpr (random_access_iterator<_It>) + __it += __n; + else if constexpr (bidirectional_iterator<_It>) + { + if (__n > 0) + { + do + { + ++__it; + } + while (--__n); + } + else if (__n < 0) + { + do + { + --__it; + } + while (++__n); + } + } + else + { + // cannot decrement a non-bidirectional iterator + __glibcxx_assert(__n >= 0); + while (__n-- > 0) ++__it; - ++__m; - } - while (__m != __n && __it != __bound); - return __n - __m; - } - else if constexpr (bidirectional_iterator<_It> && same_as<_It, _Sent>) - { - iter_difference_t<_It> __m = 0; - do - { - --__it; - --__m; - } - while (__m != __n && __it != __bound); - return __n - __m; - } - else - { - // cannot decrement a non-bidirectional iterator - __glibcxx_assert(__n >= 0); - return __n; - } - } + } + } - template _Sent> - constexpr iter_difference_t<_It> - distance(_It __first, _Sent __last) - { - if constexpr (sized_sentinel_for<_Sent, _It>) - return __last - __first; - else - { - iter_difference_t<_It> __n = 0; - while (__first != __last) - { - ++__first; - ++__n; - } - return __n; - } - } + template _Sent> + constexpr void + operator()(_It& __it, _Sent __bound) const + { + if constexpr (assignable_from<_It&, _Sent>) + __it = std::move(__bound); + else if constexpr (sized_sentinel_for<_Sent, _It>) + (*this)(__it, __bound - __it); + else + { + while (__it != __bound) + ++__it; + } + } - template - constexpr range_difference_t<_Range> - distance(_Range&& __r) - { - if constexpr (sized_range<_Range>) - return static_cast>(ranges::size(__r)); - else - return ranges::distance(ranges::begin(__r), ranges::end(__r)); - } + template _Sent> + constexpr iter_difference_t<_It> + operator()(_It& __it, iter_difference_t<_It> __n, _Sent __bound) const + { + if constexpr (sized_sentinel_for<_Sent, _It>) + { + const auto __diff = __bound - __it; - template - constexpr _It - next(_It __x) - { - ++__x; - return __x; - } + // n and bound must not lead in opposite directions: + __glibcxx_assert(__n == 0 || __diff == 0 || (__n < 0 == __diff < 0)); + const auto __absdiff = __diff < 0 ? -__diff : __diff; + const auto __absn = __n < 0 ? -__n : __n;; + if (__absn >= __absdiff) + { + (*this)(__it, __bound); + return __n - __diff; + } + else + { + (*this)(__it, __n); + return 0; + } + } + else if (__it == __bound || __n == 0) + return iter_difference_t<_It>(0); + else if (__n > 0) + { + iter_difference_t<_It> __m = 0; + do + { + ++__it; + ++__m; + } + while (__m != __n && __it != __bound); + return __n - __m; + } + else if constexpr (bidirectional_iterator<_It> && same_as<_It, _Sent>) + { + iter_difference_t<_It> __m = 0; + do + { + --__it; + --__m; + } + while (__m != __n && __it != __bound); + return __n - __m; + } + else + { + // cannot decrement a non-bidirectional iterator + __glibcxx_assert(__n >= 0); + return __n; + } + } + }; - template - constexpr _It - next(_It __x, iter_difference_t<_It> __n) - { - ranges::advance(__x, __n); - return __x; - } + inline constexpr __advance_fn advance{}; - template _Sent> - constexpr _It - next(_It __x, _Sent __bound) - { - ranges::advance(__x, __bound); - return __x; - } + struct __distance_fn + { + template _Sent> + constexpr iter_difference_t<_It> + operator()(_It __first, _Sent __last) const + { + if constexpr (sized_sentinel_for<_Sent, _It>) + return __last - __first; + else + { + iter_difference_t<_It> __n = 0; + while (__first != __last) + { + ++__first; + ++__n; + } + return __n; + } + } - template _Sent> - constexpr _It - next(_It __x, iter_difference_t<_It> __n, _Sent __bound) - { - ranges::advance(__x, __n, __bound); - return __x; - } + template + constexpr range_difference_t<_Range> + operator()(_Range&& __r) const + { + if constexpr (sized_range<_Range>) + return static_cast>(ranges::size(__r)); + else + return (*this)(ranges::begin(__r), ranges::end(__r)); + } + }; - template - constexpr _It - prev(_It __x) - { - --__x; - return __x; - } + inline constexpr __distance_fn distance{}; - template - constexpr _It - prev(_It __x, iter_difference_t<_It> __n) - { - ranges::advance(__x, -__n); - return __x; - } + struct __next_fn + { + template + constexpr _It + operator()(_It __x) const + { + ++__x; + return __x; + } - template - constexpr _It - prev(_It __x, iter_difference_t<_It> __n, _It __bound) - { - ranges::advance(__x, -__n, __bound); - return __x; - } + template + constexpr _It + operator()(_It __x, iter_difference_t<_It> __n) const + { + ranges::advance(__x, __n); + return __x; + } + + template _Sent> + constexpr _It + operator()(_It __x, _Sent __bound) const + { + ranges::advance(__x, __bound); + return __x; + } + + template _Sent> + constexpr _It + operator()(_It __x, iter_difference_t<_It> __n, _Sent __bound) const + { + ranges::advance(__x, __n, __bound); + return __x; + } + }; + + inline constexpr __next_fn next{}; + + struct __prev_fn + { + template + constexpr _It + operator()(_It __x) const + { + --__x; + return __x; + } + + template + constexpr _It + operator()(_It __x, iter_difference_t<_It> __n) const + { + ranges::advance(__x, -__n); + return __x; + } + + template + constexpr _It + operator()(_It __x, iter_difference_t<_It> __n, _It __bound) const + { + ranges::advance(__x, -__n, __bound); + return __x; + } + }; + + inline constexpr __prev_fn prev{}; /// Type returned by algorithms instead of a dangling iterator or subrange. struct dangling diff --git a/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc b/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc index 3103094b36c..112d654aefb 100644 --- a/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc +++ b/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc @@ -31,26 +31,11 @@ namespace std namespace ranges { - template S> - constexpr iter_difference_t distance(I first, S last); - template - constexpr range_difference_t distance(R&& r); - - template - constexpr I next(I x); - template - constexpr I next(I x, iter_difference_t n); - template S> - constexpr I next(I x, S bound); - template S> - constexpr I next(I x, iter_difference_t n, S bound); - - template - constexpr I prev(I x); - template - constexpr I prev(I x, iter_difference_t n); - template - constexpr I prev(I x, iter_difference_t n, I bound); + // These are function objects of unspecified type. + auto& _distance = distance; + auto& _advance = advance; + auto& _next = next; + auto& _prev = prev; } template class move_sentinel; diff --git a/libstdc++-v3/testsuite/24_iterators/range_operations/100768.cc b/libstdc++-v3/testsuite/24_iterators/range_operations/100768.cc new file mode 100644 index 00000000000..bbcfcece5ff --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/range_operations/100768.cc @@ -0,0 +1,128 @@ +// Copyright (C) 2021 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++20" } +// { dg-do compile { target c++20 } } + +// PR libstdc++/100768 - Range iterator operations should be function objects + +#include +#include + +namespace ns1 +{ + struct R { }; + void check_adl(R) { } +} + +namespace ns2 +{ + using ns1::R; + + struct A { }; + + template + R advance(I, ...) { return R{}; } + + template + R distance(I, ...) { return R{}; } + + template + R next(I, ...) { return R{}; } + + template + R prev(I, ...) { return R{}; } +} + +template struct associated { }; + +void +test02() +{ + // This type has both ns2 and std::ranges as associated namespaces. + using X = associated; + + X range[1]; + X* iter = range; + X* const sentinel = iter + 1; + + // [range.iter.op.general] p2 says: "The function templates defined in + // [range.iter.ops] are not found by argument-dependent name lookup." + // + // If we do not meet that requirement then the following will find those + // function templates (because std::ranges is an associated namespace), + // and the calls to check_adl will be ill-formed. + check_adl( advance(iter, 1) ); + check_adl( advance(iter, 1, sentinel) ); + check_adl( distance(iter, sentinel) ); + check_adl( distance(range) ); + check_adl( next(iter) ); + check_adl( next(iter, 1) ); + check_adl( next(iter, sentinel) ); + check_adl( next(iter, 1, sentinel) ); + check_adl( prev(iter) ); + check_adl( prev(iter, 1) ); + check_adl( prev(iter, 1, sentinel) ); +} + +namespace ns3 +{ + struct A { }; + + void advance(A*, int) = delete; + void advance(A*, int, A*) = delete; + + void distance(A*, A*) = delete; + void distance(A(&)[1]) = delete; + + void next(A*) = delete; + void next(A*, int) = delete; + void next(A*, A*) = delete; + void next(A*, int, A*) = delete; + + void prev(A*) = delete; + void prev(A*, int) = delete; + void prev(A*, int, A*) = delete; +} + +void +test01() +{ + ns3::A range[1]; + ns3::A* iter = range; + ns3::A* const sentinel = iter + 1; + + // [range.iter.op.general] p2 also says: "When found by unqualified name + // lookup for the postfix-expression in a function call, they inhibit + // argument-dependent name lookup." + // + // If we do not meet that requirement then the following will find the + // deleted overloads in namespace ns3 (because it is an associated namespace + // and those functions are exact matches for the arguments). + using namespace std::ranges; + advance(iter, 1); + advance(iter, 3, sentinel); + distance(iter, sentinel); + distance(range); + next(iter); + next(iter, -1); + next(iter, sentinel); + next(iter, 5, sentinel); + prev(iter); + prev(iter, 0); + prev(iter, 0, sentinel); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc index 1322f44036e..633fb36725d 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc @@ -76,6 +76,9 @@ struct X void test03() { + using ranges::next; + using ranges::begin; + // LWG 3483 std::pair x[3]; __gnu_test::test_forward_range> r(x); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc index ab2f28a2111..c2e7ba0ba02 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -132,6 +132,9 @@ struct Y void test06() { + using ranges::next; + using ranges::begin; + // LWG 3483 Y y[3]; __gnu_test::test_forward_range r(y); --tOkXZJAlsRMXVH1R--