From patchwork Mon Dec 9 17:35:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1206451 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-515540-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="UGYahQkn"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="HBfHOuEb"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47Wr2P078gz9sP3 for ; Tue, 10 Dec 2019 04:35:55 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type; q=dns; s= default; b=dpmDuADrv+Pr6czTB8WKVp5t9F/6qII0JYhvaykJd3UWDKXqoyw15 IbjtV6bFMJ1m7jS/kcymFCmq87otc07iECm7/q9TcVpgepDkz6DZOdIW9wSTRWhK FIZ3XinPmgOiwlf7p9O1q2st2e16iWxd6dVO/OnqR2FScx1h3HWBsI= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type; s= default; bh=TCsvb4EaGpvoB663yv+rR1n2pj0=; b=UGYahQknRaZVoyBQwuHP HVJIcHcENtaGCe9vhUtUfxfEi87rPCfzfU05hNAk0/bSq0x9EZBEN4lgIw+ZOY6F YgSlLp5XaxFb0oEDyW32NZqwhRnyypOdP3Gnkcwe2z4DFDEph4j+NoEIkOEIkC6K 3sW2amMSmT/DvznpxfPSoWU= Received: (qmail 76801 invoked by alias); 9 Dec 2019 17:35:31 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 76739 invoked by uid 89); 9 Dec 2019 17:35:30 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-17.4 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=unavailable version=3.3.1 spammy=lie, performing, ra X-HELO: us-smtp-delivery-1.mimecast.com Received: from us-smtp-2.mimecast.com (HELO us-smtp-delivery-1.mimecast.com) (205.139.110.61) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 09 Dec 2019 17:35:23 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1575912922; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=iUc8GynGYQYJm+Z61LW4VwZcTIEL7NWlQVXgk+ufrc4=; b=HBfHOuEbNDg2L5CoNJFLaGm48CzbWpfN4L1G6m17UDDaEBEbHDF0rt6QuzlwnIb+twW6fC HhuwJ8QzjutRMjM0krS6JpdSQZcht/h2o41+Y3LOIdbXxBzemlI3YJn03mXMLX9KkE1mGP YEzjntwRQ7I2qEq3sPErjJfm3tnMt4o= 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-404-FIF2XpOHNdCHzGpjhqSgQQ-1; Mon, 09 Dec 2019 12:35:18 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C27D18017DF; Mon, 9 Dec 2019 17:35:17 +0000 (UTC) Received: from localhost (unknown [10.33.36.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 203766BF9B; Mon, 9 Dec 2019 17:35:16 +0000 (UTC) Date: Mon, 9 Dec 2019 17:35:16 +0000 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH] libstdc++: Implement ranges::safe_range for C++20 (P1870R1) Message-ID: <20191209173516.GA1888520@redhat.com> MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett User-Agent: Mutt/1.12.1 (2019-06-15) X-Mimecast-Spam-Score: 0 Content-Disposition: inline This change replaces the __forwarding_range implementation detail with the ranges::safe_range concept and adds the ranges::enable_safe_range variable template for opt-in in to the concept. It also adjusts the begin/end/rbegin/rend customization point objects to match the new rules for accessing rvalue ranges only when safe to do so. * include/bits/range_access.h (ranges::enable_safe_range): Define. (ranges::begin, ranges::end, ranges::rbegin, ranges::rend): Constrain to only accept types satisfying safe_range and treat argument as an lvalue when calling a member of performing ADL. (ranges::__detail::__range_impl, ranges::__detail::__forwarding_range): Remove. (ranges::range): Adjust definition. (ranges::safe_range): Define. (ranges::iterator_t, ranges::range_difference_t): Reorder definitions to match the synopsis in the working draft. (ranges::disable_sized_range): Remove duplicate definition. * include/experimental/string_view (ranges::enable_safe_range): Add partial specialization for std::experimental::basic_string_view. * include/std/ranges (ranges::viewable_range, ranges::subrange) (ranges::empty_view, ranges::iota_view): Use safe_range. Specialize enable_safe_range. (ranges::safe_iterator_t, ranges::safe_subrange_t): Define. * include/std/span (ranges::enable_safe_range): Add partial specialization for std::span. * include/std/string_view (ranges::enable_safe_range): Likewise for std::basic_string_view. * testsuite/std/ranges/access/begin.cc: Adjust expected results. * testsuite/std/ranges/access/cbegin.cc: Likewise. * testsuite/std/ranges/access/cdata.cc: Likewise. * testsuite/std/ranges/access/cend.cc: Likewise. * testsuite/std/ranges/access/crbegin.cc: Likewise. * testsuite/std/ranges/access/crend.cc: Likewise. * testsuite/std/ranges/access/data.cc: Likewise. * testsuite/std/ranges/access/end.cc: Likewise. * testsuite/std/ranges/access/rbegin.cc: Likewise. * testsuite/std/ranges/access/rend.cc: Likewise. * testsuite/std/ranges/empty_view.cc: Test ranges::begin and ranges::end instead of unqualified calls to begin and end. * testsuite/std/ranges/safe_range.cc: New test. * testsuite/std/ranges/safe_range_types.cc: New test. * testsuite/util/testsuite_iterators.h: Add comment about safe_range. Tested powerpc64le-linux, committed to trunk. commit b081863b2bb9d2a0b5421556a3e3d7c638278145 Author: Jonathan Wakely Date: Mon Dec 9 08:50:48 2019 +0000 libstdc++: Implement ranges::safe_range for C++20 (P1870R1) This change replaces the __forwarding_range implementation detail with the ranges::safe_range concept and adds the ranges::enable_safe_range variable template for opt-in in to the concept. It also adjusts the begin/end/rbegin/rend customization point objects to match the new rules for accessing rvalue ranges only when safe to do so. * include/bits/range_access.h (ranges::enable_safe_range): Define. (ranges::begin, ranges::end, ranges::rbegin, ranges::rend): Constrain to only accept types satisfying safe_range and treat argument as an lvalue when calling a member of performing ADL. (ranges::__detail::__range_impl, ranges::__detail::__forwarding_range): Remove. (ranges::range): Adjust definition. (ranges::safe_range): Define. (ranges::iterator_t, ranges::range_difference_t): Reorder definitions to match the synopsis in the working draft. (ranges::disable_sized_range): Remove duplicate definition. * include/experimental/string_view (ranges::enable_safe_range): Add partial specialization for std::experimental::basic_string_view. * include/std/ranges (ranges::viewable_range, ranges::subrange) (ranges::empty_view, ranges::iota_view): Use safe_range. Specialize enable_safe_range. (ranges::safe_iterator_t, ranges::safe_subrange_t): Define. * include/std/span (ranges::enable_safe_range): Add partial specialization for std::span. * include/std/string_view (ranges::enable_safe_range): Likewise for std::basic_string_view. * testsuite/std/ranges/access/begin.cc: Adjust expected results. * testsuite/std/ranges/access/cbegin.cc: Likewise. * testsuite/std/ranges/access/cdata.cc: Likewise. * testsuite/std/ranges/access/cend.cc: Likewise. * testsuite/std/ranges/access/crbegin.cc: Likewise. * testsuite/std/ranges/access/crend.cc: Likewise. * testsuite/std/ranges/access/data.cc: Likewise. * testsuite/std/ranges/access/end.cc: Likewise. * testsuite/std/ranges/access/rbegin.cc: Likewise. * testsuite/std/ranges/access/rend.cc: Likewise. * testsuite/std/ranges/empty_view.cc: Test ranges::begin and ranges::end instead of unqualified calls to begin and end. * testsuite/std/ranges/safe_range.cc: New test. * testsuite/std/ranges/safe_range_types.cc: New test. * testsuite/util/testsuite_iterators.h: Add comment about safe_range. diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h index c94e965afb4..6ebd667f031 100644 --- a/libstdc++-v3/include/bits/range_access.h +++ b/libstdc++-v3/include/bits/range_access.h @@ -342,6 +342,9 @@ namespace ranges template inline constexpr bool disable_sized_range = false; + template + inline constexpr bool enable_safe_range = false; + namespace __detail { using __max_diff_type = long long; @@ -359,10 +362,19 @@ namespace ranges constexpr make_unsigned_t<_Tp> __to_unsigned_like(_Tp __t) noexcept { return __t; } + + // Part of the constraints of ranges::safe_range + template + concept __maybe_safe_range + = is_lvalue_reference_v<_Tp> || enable_safe_range>; + } // namespace __detail namespace __cust_access { + using std::ranges::__detail::__maybe_safe_range; + using std::__detail::__class_or_enum; + template constexpr decay_t<_Tp> __decay_copy(_Tp&& __t) @@ -370,20 +382,19 @@ namespace ranges { return std::forward<_Tp>(__t); } template - concept __member_begin = is_lvalue_reference_v<_Tp> - && requires(_Tp __t) - { { __decay_copy(__t.begin()) } -> input_or_output_iterator; }; + concept __member_begin = requires(_Tp& __t) + { + { __decay_copy(__t.begin()) } -> input_or_output_iterator; + }; template void begin(_Tp&&) = delete; template void begin(initializer_list<_Tp>&&) = delete; template - concept __adl_begin - = std::__detail::__class_or_enum> - && requires(_Tp&& __t) + concept __adl_begin = __class_or_enum> + && requires(_Tp& __t) { - { __decay_copy(begin(std::forward<_Tp>(__t))) } - -> input_or_output_iterator; + { __decay_copy(begin(__t)) } -> input_or_output_iterator; }; struct _Begin @@ -396,47 +407,45 @@ namespace ranges if constexpr (is_array_v>) return true; else if constexpr (__member_begin<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp>().begin())); + return noexcept(__decay_copy(std::declval<_Tp&>().begin())); else - return noexcept(__decay_copy(begin(std::declval<_Tp>()))); + return noexcept(__decay_copy(begin(std::declval<_Tp&>()))); } public: - template + template<__maybe_safe_range _Tp> requires is_array_v> || __member_begin<_Tp> || __adl_begin<_Tp> constexpr auto - operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) { if constexpr (is_array_v>) { static_assert(is_lvalue_reference_v<_Tp>); - return __e; + return __t; } else if constexpr (__member_begin<_Tp>) - return __e.begin(); + return __t.begin(); else - return begin(std::forward<_Tp>(__e)); + return begin(__t); } }; template - concept __member_end = is_lvalue_reference_v<_Tp> - && requires(_Tp __t) + concept __member_end = requires(_Tp& __t) { { __decay_copy(__t.end()) } - -> sentinel_for; + -> sentinel_for(__t)))>; }; template void end(_Tp&&) = delete; template void end(initializer_list<_Tp>&&) = delete; template - concept __adl_end - = std::__detail::__class_or_enum> - && requires(_Tp&& __t) + concept __adl_end = __class_or_enum> + && requires(_Tp& __t) { - { __decay_copy(end(std::forward<_Tp>(__t))) } + { __decay_copy(end(__t)) } -> sentinel_for(__t)))>; }; @@ -450,28 +459,28 @@ namespace ranges if constexpr (is_array_v>) return true; else if constexpr (__member_end<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp>().end())); + return noexcept(__decay_copy(std::declval<_Tp&>().end())); else - return noexcept(__decay_copy(end(std::declval<_Tp>()))); + return noexcept(__decay_copy(end(std::declval<_Tp&>()))); } public: - template + template<__maybe_safe_range _Tp> requires is_array_v> || __member_end<_Tp> || __adl_end<_Tp> constexpr auto - operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) { if constexpr (is_array_v>) { static_assert(is_lvalue_reference_v<_Tp>); static_assert(is_bounded_array_v>); - return __e + extent_v>; + return __t + extent_v>; } else if constexpr (__member_end<_Tp>) - return __e.end(); + return __t.end(); else - return end(std::forward<_Tp>(__e)); + return end(__t); } }; @@ -510,27 +519,25 @@ namespace ranges }; template - concept __member_rbegin = is_lvalue_reference_v<_Tp> - && requires(_Tp __t) - { { __decay_copy(__t.rbegin()) } -> input_or_output_iterator; }; + concept __member_rbegin = requires(_Tp& __t) + { + { __decay_copy(__t.rbegin()) } -> input_or_output_iterator; + }; template void rbegin(_Tp&&) = delete; template - concept __adl_rbegin - = std::__detail::__class_or_enum> - && requires(_Tp&& __t) + concept __adl_rbegin = __class_or_enum> + && requires(_Tp& __t) { - { __decay_copy(rbegin(std::forward<_Tp>(__t))) } - -> input_or_output_iterator; + { __decay_copy(rbegin(__t)) } -> input_or_output_iterator; }; template - concept __reversable = requires(_Tp&& __t) + concept __reversable = requires(_Tp& __t) { - { _Begin{}(std::forward<_Tp>(__t)) } -> bidirectional_iterator; - { _End{}(std::forward<_Tp>(__t)) } - -> same_as(__t)))>; + { _Begin{}(__t) } -> bidirectional_iterator; + { _End{}(__t) } -> same_as; }; struct _RBegin @@ -541,14 +548,14 @@ namespace ranges _S_noexcept() { if constexpr (__member_rbegin<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp>().rbegin())); + return noexcept(__decay_copy(std::declval<_Tp&>().rbegin())); else if constexpr (__adl_rbegin<_Tp>) - return noexcept(__decay_copy(rbegin(std::declval<_Tp>()))); + return noexcept(__decay_copy(rbegin(std::declval<_Tp&>()))); else { - if constexpr (noexcept(_End{}(std::declval<_Tp>()))) + if constexpr (noexcept(_End{}(std::declval<_Tp&>()))) { - using _It = decltype(_End{}(std::declval<_Tp>())); + using _It = decltype(_End{}(std::declval<_Tp&>())); // std::reverse_iterator copy-initializes its member. return is_nothrow_copy_constructible_v<_It>; } @@ -558,24 +565,23 @@ namespace ranges } public: - template + template<__maybe_safe_range _Tp> requires __member_rbegin<_Tp> || __adl_rbegin<_Tp> || __reversable<_Tp> constexpr auto - operator()(_Tp&& __e) const + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) { if constexpr (__member_rbegin<_Tp>) - return __e.rbegin(); + return __t.rbegin(); else if constexpr (__adl_rbegin<_Tp>) - return rbegin(std::forward<_Tp>(__e)); + return rbegin(__t); else - return std::make_reverse_iterator(_End{}(std::forward<_Tp>(__e))); + return std::make_reverse_iterator(_End{}(__t)); } }; template - concept __member_rend = is_lvalue_reference_v<_Tp> - && requires(_Tp __t) + concept __member_rend = requires(_Tp& __t) { { __decay_copy(__t.rend()) } -> sentinel_for; @@ -584,11 +590,10 @@ namespace ranges template void rend(_Tp&&) = delete; template - concept __adl_rend - = std::__detail::__class_or_enum> - && requires(_Tp&& __t) + concept __adl_rend = __class_or_enum> + && requires(_Tp& __t) { - { __decay_copy(rend(std::forward<_Tp>(__t))) } + { __decay_copy(rend(__t)) } -> sentinel_for(__t)))>; }; @@ -600,14 +605,14 @@ namespace ranges _S_noexcept() { if constexpr (__member_rend<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp>().rend())); + return noexcept(__decay_copy(std::declval<_Tp&>().rend())); else if constexpr (__adl_rend<_Tp>) - return noexcept(__decay_copy(rend(std::declval<_Tp>()))); + return noexcept(__decay_copy(rend(std::declval<_Tp&>()))); else { - if constexpr (noexcept(_Begin{}(std::declval<_Tp>()))) + if constexpr (noexcept(_Begin{}(std::declval<_Tp&>()))) { - using _It = decltype(_Begin{}(std::declval<_Tp>())); + using _It = decltype(_Begin{}(std::declval<_Tp&>())); // std::reverse_iterator copy-initializes its member. return is_nothrow_copy_constructible_v<_It>; } @@ -617,18 +622,18 @@ namespace ranges } public: - template + template<__maybe_safe_range _Tp> requires __member_rend<_Tp> || __adl_rend<_Tp> || __reversable<_Tp> constexpr auto - operator()(_Tp&& __e) const + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) { if constexpr (__member_rend<_Tp>) - return __e.rend(); + return __t.rend(); else if constexpr (__adl_rend<_Tp>) - return rend(std::forward<_Tp>(__e)); + return rend(__t); else - return std::make_reverse_iterator(_Begin{}(std::forward<_Tp>(__e))); + return std::make_reverse_iterator(_Begin{}(__t)); } }; @@ -667,8 +672,7 @@ namespace ranges template void size(_Tp&&) = delete; template - concept __adl_size - = std::__detail::__class_or_enum> + concept __adl_size = __class_or_enum> && !disable_sized_range> && requires(_Tp&& __t) { @@ -842,25 +846,26 @@ namespace ranges inline constexpr __cust_access::_CData cdata{}; } - namespace __detail - { - template - concept __range_impl = requires(_Tp&& __t) { - ranges::begin(std::forward<_Tp>(__t)); - ranges::end(std::forward<_Tp>(__t)); - }; - - } // namespace __detail - /// [range.range] The range concept. template - concept range = __detail::__range_impl<_Tp&>; + concept range = requires(_Tp& __t) + { + ranges::begin(__t); + ranges::end(__t); + }; + + /// [range.range] The safe_range concept. + template + concept safe_range = range<_Tp> && __detail::__maybe_safe_range<_Tp>; + + template + using iterator_t = decltype(ranges::begin(std::declval<_Range&>())); template using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); template - using iterator_t = decltype(ranges::begin(std::declval<_Range&>())); + using range_difference_t = iter_difference_t>; template using range_value_t = iter_value_t>; @@ -872,43 +877,38 @@ namespace ranges using range_rvalue_reference_t = iter_rvalue_reference_t>; - template - using range_difference_t = iter_difference_t>; - - namespace __detail - { - template - concept __forwarding_range = range<_Tp> && __range_impl<_Tp>; - } // namespace __detail - /// [range.sized] The sized_range concept. template concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); }; - template - inline constexpr bool disable_sized_range = false; - // [range.refinements] + + /// A range for which ranges::begin returns an output iterator. template concept output_range = range<_Range> && output_iterator, _Tp>; + /// A range for which ranges::begin returns an input iterator. template concept input_range = range<_Tp> && input_iterator>; + /// A range for which ranges::begin returns a forward iterator. template concept forward_range = input_range<_Tp> && forward_iterator>; + /// A range for which ranges::begin returns a bidirectional iterator. template concept bidirectional_range = forward_range<_Tp> && bidirectional_iterator>; + /// A range for which ranges::begin returns a random access iterator. template concept random_access_range = bidirectional_range<_Tp> && random_access_iterator>; + /// A range for which ranges::begin returns a contiguous iterator. template concept contiguous_range = random_access_range<_Tp> && contiguous_iterator> @@ -917,11 +917,12 @@ namespace ranges { ranges::data(__t) } -> same_as>>; }; + /// A range for which ranges::begin and ranges::end return the same type. template concept common_range = range<_Tp> && same_as, sentinel_t<_Tp>>; - // [range.iter.ops] range iterator operations + // [range.iter.ops] range iterator operations template constexpr void diff --git a/libstdc++-v3/include/experimental/string_view b/libstdc++-v3/include/experimental/string_view index 84b2a3eb402..cc9cb93ad5e 100644 --- a/libstdc++-v3/include/experimental/string_view +++ b/libstdc++-v3/include/experimental/string_view @@ -691,6 +691,18 @@ namespace experimental } // namespace literals } // namespace experimental +#if __cpp_lib_concepts + namespace ranges + { + template extern inline const bool enable_safe_range; + // Opt-in to safe_range concept + template + inline constexpr bool + enable_safe_range> + = true; + } +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index e1bf6eec5d0..c2567818cb5 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -89,9 +89,10 @@ namespace ranges = range<_Tp> && movable<_Tp> && default_initializable<_Tp> && enable_view<_Tp>; + /// A range which can be safely converted to a view. template concept viewable_range = range<_Tp> - && (__detail::__forwarding_range<_Tp> || view>); + && (safe_range<_Tp> || view>); namespace __detail { @@ -295,7 +296,7 @@ namespace ranges } template<__detail::__not_same_as _Rng> - requires __detail::__forwarding_range<_Rng> + requires safe_range<_Rng> && convertible_to, _It> && convertible_to, _Sent> constexpr @@ -306,7 +307,7 @@ namespace ranges _M_size._M_size = ranges::size(__r); } - template<__detail::__forwarding_range _Rng> + template requires convertible_to, _It> && convertible_to, _Sent> constexpr @@ -401,12 +402,6 @@ namespace ranges ranges::advance(_M_begin, __n, _M_end); return *this; } - - friend constexpr _It - begin(subrange&& __r) { return __r.begin(); } - - friend constexpr _Sent - end(subrange&& __r) { return __r.end(); } }; template _Sent> @@ -424,14 +419,14 @@ namespace ranges -> subrange, tuple_element_t<1, _Pr>, subrange_kind::sized>; - template<__detail::__forwarding_range _Rng> + template subrange(_Rng&&) -> subrange, sentinel_t<_Rng>, (sized_range<_Rng> || sized_sentinel_for, iterator_t<_Rng>>) ? subrange_kind::sized : subrange_kind::unsized>; - template<__detail::__forwarding_range _Rng> + template subrange(_Rng&&, __detail::__make_unsigned_like_t>) -> subrange, sentinel_t<_Rng>, subrange_kind::sized>; @@ -457,6 +452,12 @@ namespace ranges else return __r.end(); } + + template _Sent, + subrange_kind _Kind> + inline constexpr bool + enable_safe_range> = true; + } // namespace ranges using ranges::get; @@ -471,8 +472,19 @@ namespace ranges constexpr dangling(_Args&&...) noexcept { } }; + template + using safe_iterator_t = conditional_t, + iterator_t<_Range>, + dangling>; + + template + using safe_subrange_t = conditional_t, + subrange>, + dangling>; + template requires is_object_v<_Tp> - class empty_view : public view_interface> + class empty_view + : public view_interface> { public: static constexpr _Tp* begin() noexcept { return nullptr; } @@ -480,11 +492,11 @@ namespace ranges static constexpr _Tp* data() noexcept { return nullptr; } static constexpr size_t size() noexcept { return 0; } static constexpr bool empty() noexcept { return true; } - - friend constexpr _Tp* begin(empty_view) noexcept { return nullptr; } - friend constexpr _Tp* end(empty_view) noexcept { return nullptr; } }; + template + inline constexpr bool enable_safe_range> = true; + namespace __detail { template requires is_object_v<_Tp> @@ -899,6 +911,9 @@ namespace ranges == __detail::__is_signed_integer_like<_Bound>)) iota_view(_Winc, _Bound) -> iota_view<_Winc, _Bound>; + template + inline constexpr bool enable_safe_range> = true; + namespace views { template diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span index c71f8bc3f89..f215decb453 100644 --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -399,16 +399,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return {this->data() + __offset, __count}; } - // observers: range helpers - - friend constexpr iterator - begin(span __sp) noexcept - { return __sp.begin(); } - - friend constexpr iterator - end(span __sp) noexcept - { return __sp.end(); } - private: [[no_unique_address]] __detail::__extent_storage _M_extent; pointer _M_ptr; @@ -478,6 +468,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using type = _Type; }; + namespace ranges + { + template extern inline const bool enable_safe_range; + // Opt-in to safe_range concept + template + inline constexpr bool + enable_safe_range> = true; + } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // concepts diff --git a/libstdc++-v3/include/std/string_view b/libstdc++-v3/include/std/string_view index 9d2a8e8e0c2..add432bbb09 100644 --- a/libstdc++-v3/include/std/string_view +++ b/libstdc++-v3/include/std/string_view @@ -724,6 +724,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } // namespace string_literals } // namespace literals +#if __cpp_lib_concepts + namespace ranges + { + template extern inline const bool enable_safe_range; + // Opt-in to safe_range concept + template + inline constexpr bool + enable_safe_range> = true; + } +#endif _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/testsuite/std/ranges/access/begin.cc b/libstdc++-v3/testsuite/std/ranges/access/begin.cc index e4c245a76bb..8439a2175e7 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/begin.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/begin.cc @@ -71,11 +71,22 @@ struct R int a[4] = { 0, 1, 2, 3 }; friend int* begin(R& r) { return r.a + 0; } - friend int* begin(R&& r) { return r.a + 1; } + friend int* begin(R&& r); // this overload is not defined friend const int* begin(const R& r) noexcept { return r.a + 2; } - friend const int* begin(const R&& r) noexcept { return r.a + 3; } + friend const int* begin(const R&& r) noexcept; // not defined }; +struct RV // view on an R +{ + R& r; + + friend int* begin(RV& rv) { return begin(rv.r); } + friend const int* begin(const RV& rv) noexcept { return begin(rv.r); } +}; + +// Allow ranges::begin to work with RV&& +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { @@ -86,20 +97,23 @@ test03() static_assert(!noexcept(std::ranges::begin(r))); VERIFY( std::ranges::begin(r) == begin(r) ); - static_assert(same_as); - static_assert(!noexcept(std::ranges::begin(std::move(r)))); - VERIFY( std::ranges::begin(std::move(r)) == begin(std::move(r)) ); - - static_assert(same_as); static_assert(noexcept(std::ranges::begin(c))); VERIFY( std::ranges::begin(c) == begin(c) ); - static_assert(same_as); - static_assert(noexcept(std::ranges::begin(std::move(c)))); - VERIFY( std::ranges::begin(std::move(c)) == begin(std::move(c)) ); + RV v{r}; + // enable_safe_range allows ranges::begin to work for rvalues, + // but it will call v.begin() or begin(v) on an lvalue: + static_assert(same_as); + static_assert(!noexcept(std::ranges::begin(std::move(v)))); + VERIFY( std::ranges::begin(std::move(v)) == begin(v) ); + + const RV cv{r}; + static_assert(same_as); + static_assert(noexcept(std::ranges::begin(std::move(cv)))); + VERIFY( std::ranges::begin(std::move(cv)) == begin(cv) ); } struct RR @@ -111,12 +125,15 @@ struct RR short* begin() noexcept { return &s; } const long* begin() const { return &l; } - friend int* begin(RR& r) { return r.a + 0; } - friend int* begin(RR&& r) { return r.a + 1; } + friend int* begin(RR& r) noexcept { return r.a + 0; } + friend int* begin(RR&& r); // not defined friend const int* begin(const RR& r) { return r.a + 2; } - friend const int* begin(const RR&& r) noexcept { return r.a + 3; } + friend const int* begin(const RR&& r) noexcept; // not defined }; +// N.B. this is a lie, begin on an RR rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test04() { @@ -125,14 +142,16 @@ test04() VERIFY( std::ranges::begin(r) == &r.s ); static_assert(noexcept(std::ranges::begin(r))); - VERIFY( std::ranges::begin(std::move(r)) == r.a + 1 ); - static_assert(!noexcept(std::ranges::begin(std::move(r)))); + // calls r.begin() on an lvalue, not rvalue + VERIFY( std::ranges::begin(std::move(r)) == std::ranges::begin(r) ); + static_assert(noexcept(std::ranges::begin(std::move(r)))); VERIFY( std::ranges::begin(c) == &r.l ); static_assert(!noexcept(std::ranges::begin(c))); - VERIFY( std::ranges::begin(std::move(c)) == r.a + 3 ); - static_assert(noexcept(std::ranges::begin(std::move(c)))); + // calls r.begin() on a const lvalue, not rvalue + VERIFY( std::ranges::begin(std::move(c)) == std::ranges::begin(c) ); + static_assert(!noexcept(std::ranges::begin(std::move(c)))); } int diff --git a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc index 54db3658896..709df0d1588 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc @@ -40,20 +40,34 @@ struct R int a[4] = { 0, 1, 2, 3 }; friend int* begin(R& r) { return r.a + 0; } - friend int* begin(R&& r) { return r.a + 1; } + friend int* begin(R&&); // this function is not defined friend const int* begin(const R& r) noexcept { return r.a + 2; } - friend const int* begin(const R&& r) noexcept { return r.a + 3; } + friend const int* begin(const R&&); // this function is not defined }; +struct RV // view on an R +{ + R& r; + + friend int* begin(RV&); // this function is not defined + friend const int* begin(const RV& rv) noexcept { return begin(std::as_const(rv.r)); } +}; + +// Allow ranges::begin to work with RV&& +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { R r; const R& c = r; VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c)); - VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(std::move(c))); VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c)); - VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(std::move(c))); + + RV v{r}; + VERIFY(std::ranges::cbegin(std::move(v)) == std::ranges::begin(c)); + const RV cv{r}; + VERIFY(std::ranges::cbegin(std::move(cv)) == std::ranges::begin(c)); } struct RR @@ -71,15 +85,18 @@ struct RR friend const int* begin(const RR&& r) noexcept { return r.a + 3; } }; +// N.B. this is a lie, cbegin on an RR rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test04() { RR r; const RR& c = r; VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c)); - VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(std::move(c))); + VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(c)); VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c)); - VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(std::move(c))); + VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(c)); } int diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc index b16c99607a5..f928bb795c2 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc @@ -51,17 +51,22 @@ struct R long l = 0; int* data() const { return nullptr; } - friend long* begin(R&& r) { return &r.l; } - friend const long* begin(const R&& r) { return &r.l + 1; } + friend long* begin(R&& r); // this function is not defined + friend const long* begin(const R& r) { return &r.l; } + friend const short* begin(const R&&); // not defined }; +// This is a lie, ranges::begin(R&&) returns a dangling iterator. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { R r; const R& c = r; - VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::data(std::move(c)) ); - VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::data(std::move(c)) ); + VERIFY( std::ranges::cdata(r) == std::ranges::data(c) ); + VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::begin(c) ); + VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::begin(c) ); } int diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc b/libstdc++-v3/testsuite/std/ranges/access/cend.cc index 3b57b3dbcaf..59053f75d67 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc @@ -49,15 +49,31 @@ struct R friend const int* end(const R&& r) noexcept { return r.a + 3; } }; +struct RV // view on an R +{ + R& r; + + friend const int* begin(RV& rv) { return rv.r.begin(); } + friend int* end(RV& rv) { return end(rv.r); } + friend const int* begin(const RV& rv) noexcept { return rv.r.begin(); } + friend const int* end(const RV& rv) noexcept { return end(std::as_const(rv.r)); } +}; + +// Allow ranges::end to work with RV&& +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { R r; const R& c = r; VERIFY( std::ranges::cend(r) == std::ranges::end(c) ); - VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(std::move(c)) ); VERIFY( std::ranges::cend(c) == std::ranges::end(c) ); - VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(std::move(c)) ); + + RV v{r}; + const RV cv{r}; + VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) ); + VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) ); } struct RR @@ -81,15 +97,19 @@ struct RR friend const int* end(const RR&& r) noexcept { return r.a + 3; } }; +// N.B. this is a lie, begin/end on an RR rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test04() { RR r; const RR& c = r; VERIFY( std::ranges::cend(r) == std::ranges::end(c) ); - VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(std::move(c)) ); VERIFY( std::ranges::cend(c) == std::ranges::end(c) ); - VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(std::move(c)) ); + + VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(c) ); + VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(c) ); } int diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc index d9e5b0cbef7..17ea7983470 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc @@ -31,15 +31,29 @@ struct R1 friend const int* rbegin(const R1&& r) { return &r.j; } }; +struct R1V // view on an R1 +{ + R1& r; + + friend const long* rbegin(R1V&); // this is not defined + friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); } +}; + +// Allow ranges::end to work with R1V&& +template<> constexpr bool std::ranges::enable_safe_range = true; + void test01() { R1 r; const R1& c = r; VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) ); - VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(std::move(c)) ); VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) ); - VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(std::move(c)) ); + + R1V v{r}; + const R1V cv{r}; + VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) ); + VERIFY( std::ranges::crbegin(std::move(cv)) == std::ranges::rbegin(c) ); } struct R2 @@ -50,19 +64,23 @@ struct R2 const int* begin() const { return a; } const int* end() const { return a + 2; } - friend const long* begin(const R2&& r) { return r.l; } - friend const long* end(const R2&& r) { return r.l + 2; } + friend const long* begin(const R2&&); // not defined + friend const long* end(const R2&&); // not defined }; +// N.B. this is a lie, rbegin on an R2 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test02() { R2 r; const R2& c = r; VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) ); - VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(std::move(c)) ); VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) ); - VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(std::move(c)) ); + + VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(c) ); + VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(c) ); } int diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc b/libstdc++-v3/testsuite/std/ranges/access/crend.cc index e56491973b2..bc4d0d2949c 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc @@ -33,15 +33,18 @@ struct R1 friend constexpr const int* rend(const R1&& r) { return &r.j + 1; } }; +// N.B. this is a lie, rend on an R1 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test01() { R1 r; const R1& c = r; VERIFY( std::ranges::crend(r) == std::ranges::rend(c) ); - VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) ); VERIFY( std::ranges::crend(c) == std::ranges::rend(c) ); - VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) ); + VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(c) ); + VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(c) ); } struct R2 @@ -56,14 +59,17 @@ struct R2 friend const long* end(const R2&& r) { return r.l + 2; } }; +// N.B. this is a lie, rend on an R2 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test02() { R2 r; const R2& c = r; VERIFY( std::ranges::crend(r) == std::ranges::rend(c) ); - VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) ); VERIFY( std::ranges::crend(c) == std::ranges::rend(c) ); + VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) ); VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) ); } @@ -78,6 +84,9 @@ struct R3 friend const int* rend(const R3& r) { return &r.i; } }; +// N.B. this is a lie, rend on an R3 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { diff --git a/libstdc++-v3/testsuite/std/ranges/access/data.cc b/libstdc++-v3/testsuite/std/ranges/access/data.cc index 49321640182..1bd4e06dd0e 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/data.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/data.cc @@ -56,15 +56,20 @@ struct R3 long l = 0; int* data() const { return nullptr; } - friend long* begin(R3&& r) { return &r.l; } - friend const long* begin(const R3&& r) { return &r.l + 1; } + friend long* begin(R3& r) { return &r.l; } + friend const long* begin(const R3& r) { return &r.l + 1; } }; +// N.B. this is a lie, begin on an R3 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { R3 r; const R3& c = r; + // r.data() can only be used on an lvalue, but ranges::begin(R3&&) is OK + // because R3 satisfies ranges::safe_range. VERIFY( std::ranges::data(std::move(r)) == std::to_address(std::ranges::begin(std::move(r))) ); VERIFY( std::ranges::data(std::move(c)) == std::to_address(std::ranges::begin(std::move(c))) ); } diff --git a/libstdc++-v3/testsuite/std/ranges/access/end.cc b/libstdc++-v3/testsuite/std/ranges/access/end.cc index ed269c5433f..eaa13d031b3 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/end.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/end.cc @@ -74,6 +74,19 @@ struct R friend const int* end(const R&& r) noexcept { return r.a + 3; } }; +struct RV // view on an R +{ + R& r; + + const int* begin() const; + + friend int* end(RV& v) noexcept { return end(v.r); } + friend const int* end(const RV& v) { return end(std::as_const(v.r)); } +}; + +// Allow ranges::begin to work with RV&& +template<> constexpr bool std::ranges::enable_safe_range = true; + void test03() { @@ -84,20 +97,21 @@ test03() static_assert(!noexcept(std::ranges::end(r))); VERIFY( std::ranges::end(r) == end(r) ); - static_assert(same_as); - static_assert(!noexcept(std::ranges::end(std::move(r)))); - VERIFY( std::ranges::end(std::move(r)) == end(std::move(r)) ); - - static_assert(same_as); static_assert(noexcept(std::ranges::end(c))); VERIFY( std::ranges::end(c) == end(c) ); - static_assert(same_as); - static_assert(noexcept(std::ranges::end(std::move(c)))); - VERIFY( std::ranges::end(std::move(c)) == end(std::move(c)) ); + RV v{r}; + static_assert(same_as); + static_assert(noexcept(std::ranges::end(std::move(v)))); + VERIFY( std::ranges::end(std::move(v)) == end(r) ); + + const RV cv{r}; + static_assert(same_as); + static_assert(!noexcept(std::ranges::end(std::move(cv)))); + VERIFY( std::ranges::end(std::move(cv)) == end(c) ); } struct RR @@ -123,6 +137,9 @@ struct RR friend const int* end(const RR&& r) noexcept { return r.a + 3; } }; +// N.B. this is a lie, end on an RR rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test04() { @@ -131,14 +148,14 @@ test04() VERIFY( std::ranges::end(r) == &r.s ); static_assert(noexcept(std::ranges::end(r))); - VERIFY( std::ranges::end(std::move(r)) == r.a + 1 ); - static_assert(!noexcept(std::ranges::end(std::move(r)))); + VERIFY( std::ranges::end(std::move(r)) == &r.s ); + static_assert(noexcept(std::ranges::end(std::move(r)))); VERIFY( std::ranges::end(c) == &r.l ); static_assert(!noexcept(std::ranges::end(c))); - VERIFY( std::ranges::end(std::move(c)) == r.a + 3 ); - static_assert(noexcept(std::ranges::end(std::move(c)))); + VERIFY( std::ranges::end(std::move(c)) == &r.l ); + static_assert(!noexcept(std::ranges::end(std::move(c)))); } int diff --git a/libstdc++-v3/testsuite/std/ranges/access/rbegin.cc b/libstdc++-v3/testsuite/std/ranges/access/rbegin.cc index 067ddd7ced6..aecbc2bb8af 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/rbegin.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/rbegin.cc @@ -31,26 +31,31 @@ struct R1 friend constexpr const int* rbegin(const R1&& r) { return &r.j; } }; +// N.B. this is a lie, rbegin on an R1 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test01() { constexpr R1 r; static_assert( std::ranges::rbegin(r) == &r.i ); - static_assert( std::ranges::rbegin(std::move(r)) == &r.j ); + static_assert( std::ranges::rbegin(std::move(r)) == &r.i ); } struct R2 { int a[2] = { }; - long l[2] = { }; constexpr const int* begin() const { return a; } constexpr const int* end() const { return a + 2; } - friend constexpr const long* begin(const R2&& r) { return r.l; } - friend constexpr const long* end(const R2&& r) { return r.l + 2; } + friend constexpr const long* begin(const R2&&); // not defined + friend constexpr const long* end(const R2&&); // not defined }; +// N.B. this is a lie, begin/end on an R2 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test02() { diff --git a/libstdc++-v3/testsuite/std/ranges/access/rend.cc b/libstdc++-v3/testsuite/std/ranges/access/rend.cc index 17caa9fb31a..b447e97b27c 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/rend.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/rend.cc @@ -29,16 +29,19 @@ struct R1 constexpr const int* rbegin() const { return &i; } constexpr const int* rend() const { return &i + 1; } - friend constexpr const int* rbegin(const R1&& r) { return &r.j; } - friend constexpr const int* rend(const R1&& r) { return &r.j + 1; } + friend constexpr const int* rbegin(const R1&&); // not defined + friend constexpr const int* rend(const R1&&); // not defined }; +// N.B. this is a lie, rend on an R1 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test01() { constexpr R1 r; static_assert( std::ranges::rend(r) == &r.i + 1 ); - static_assert( std::ranges::rend(std::move(r)) == &r.j + 1 ); + static_assert( std::ranges::rend(std::move(r)) == &r.i + 1 ); } struct R2 @@ -53,6 +56,9 @@ struct R2 friend constexpr const long* end(const R2&& r) { return r.l + 2; } }; +// N.B. this is a lie, begin/end on an R2 rvalue will return a dangling pointer. +template<> constexpr bool std::ranges::enable_safe_range = true; + void test02() { diff --git a/libstdc++-v3/testsuite/std/ranges/empty_view.cc b/libstdc++-v3/testsuite/std/ranges/empty_view.cc index 6524b37d699..b23e51fde55 100644 --- a/libstdc++-v3/testsuite/std/ranges/empty_view.cc +++ b/libstdc++-v3/testsuite/std/ranges/empty_view.cc @@ -31,5 +31,5 @@ static_assert(e.end() == nullptr); static_assert(e.data() == nullptr); static_assert(e.empty()); -static_assert(begin(e) == nullptr); -static_assert(end(e) == nullptr); +static_assert(std::ranges::begin(e) == nullptr); +static_assert(std::ranges::end(e) == nullptr); diff --git a/libstdc++-v3/testsuite/std/ranges/safe_range.cc b/libstdc++-v3/testsuite/std/ranges/safe_range.cc new file mode 100644 index 00000000000..0cf30856d15 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/safe_range.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2019 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 + +static_assert( std::ranges::safe_range ); +static_assert( std::ranges::safe_range ); +static_assert( !std::ranges::safe_range ); +static_assert( !std::ranges::safe_range ); + +using __gnu_test::test_contiguous_range; + +static_assert( !std::ranges::safe_range> ); +static_assert( std::ranges::safe_range&> ); +static_assert( !std::ranges::safe_range&&> ); + +template<> +constexpr bool + std::ranges::enable_safe_range> = true; + +static_assert( std::ranges::safe_range> ); +static_assert( std::ranges::safe_range&> ); +static_assert( std::ranges::safe_range&&> ); diff --git a/libstdc++-v3/testsuite/std/ranges/safe_range_types.cc b/libstdc++-v3/testsuite/std/ranges/safe_range_types.cc new file mode 100644 index 00000000000..e730ec0a530 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/safe_range_types.cc @@ -0,0 +1,59 @@ +// Copyright (C) 2019 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 +#include + +template +constexpr bool +rvalue_is_safe_range() +{ + using std::ranges::safe_range; + + // An lvalue range always models safe_range + static_assert( safe_range ); + static_assert( safe_range ); + + // Result should not depend on addition of const or rvalue-reference. + static_assert( safe_range == safe_range ); + static_assert( safe_range == safe_range ); + static_assert( safe_range == safe_range ); + + return std::ranges::safe_range; +} + +static_assert( rvalue_is_safe_range>() ); +static_assert( rvalue_is_safe_range>() ); +static_assert( rvalue_is_safe_range>() ); +static_assert( rvalue_is_safe_range>() ); + +static_assert( rvalue_is_safe_range>() ); +static_assert( rvalue_is_safe_range>() ); + +static_assert( ! rvalue_is_safe_range() ); +static_assert( ! rvalue_is_safe_range() ); + +static_assert( rvalue_is_safe_range() ); +static_assert( rvalue_is_safe_range() ); + +static_assert( rvalue_is_safe_range() ); +static_assert( rvalue_is_safe_range() ); diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h index 13993a4209b..3dac911129a 100644 --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h @@ -758,6 +758,11 @@ namespace __gnu_test template using test_output_sized_range = test_sized_range; + +// test_container, test_range and test_sized_range do not own their elements, +// so they all model std::ranges::safe_range. This file does not define +// specializations of std::ranges::enable_safe_range, so that individual +// test can decide whether or not to do so. #endif // C++20 } // namespace __gnu_test #endif // _TESTSUITE_ITERATORS