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 [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 010453857023 for ; Mon, 29 Mar 2021 01:59:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 010453857023 Received: from mail-qk1-f200.google.com (mail-qk1-f200.google.com [209.85.222.200]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-564-WeKpUXYLN6-PNn5G8essIQ-1; Sun, 28 Mar 2021 21:59:02 -0400 X-MC-Unique: WeKpUXYLN6-PNn5G8essIQ-1 Received: by mail-qk1-f200.google.com with SMTP id g18so11270309qki.15 for ; Sun, 28 Mar 2021 18:59:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=MgQVtazGZOwsTRBGiugy9d0Wwrh6kTjCXgRbceE8N3U=; b=paWOEjTwMYSuRPaAoj4sS5G1b2XpRQtzDr3dKocsZus7E+aLZMpB9PUZBpyatRfy6o RMa7vpmhL7hcmOYnXBka7fb7JnEbl8h7uDJIH+pMxCEu+g/2iZPJiDnWyM3szIqq/gFP aL0xSc0Ir7CHZd1txxbUQmZccaqIshLhsxUut6H0VgBrIXtwFAW1JoTf4meqqsDVafsB Q4rZEVF4YMg14UuBXD8Cxo2Nwkq2xfKqay2QyKO0cfGXdboItwOKqTBZKpCqAuu+nGCf vY1dM2fb8wOUiC6718quLKBtTmj3UChnwt1z9Fw471DjmVfQBv3/R8l66CKR9PbcOCfK oGbA== X-Gm-Message-State: AOAM531aTRiYs63Un296DQr0uxY0A0KnIfrNfx2RWO7LRckyx3q/VeTi 9/hf73Jo8qpgw/S7wHxGLZVgWgVOzU1OqzB22DZosyfDnp1wI2fYtk9h6CCninLXtsB9pNJvhDm OZTnFasaUVic5t4s= X-Received: by 2002:ac8:6f2a:: with SMTP id i10mr21096791qtv.375.1616983139829; Sun, 28 Mar 2021 18:58:59 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxTASGT2CZkOGftcftxzDmUc+eYeOkKfGzdAQqMFA0IN3gG6VeSohicn2LTMVEAty44TH6C4g== X-Received: by 2002:ac8:6f2a:: with SMTP id i10mr21096755qtv.375.1616983138865; Sun, 28 Mar 2021 18:58:58 -0700 (PDT) Received: from localhost.localdomain (ool-457d493a.dyn.optonline.net. [69.125.73.58]) by smtp.gmail.com with ESMTPSA id z188sm12462217qkb.40.2021.03.28.18.58.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 28 Mar 2021 18:58:58 -0700 (PDT) From: Patrick Palka To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org, Patrick Palka Subject: [PATCH] libstdc++: Reimplement range adaptors [PR99433] Date: Sun, 28 Mar 2021 21:58:55 -0400 Message-Id: <20210329015855.3787379-1-ppalka@redhat.com> X-Mailer: git-send-email 2.31.1.133.g84d06cdc06 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-16.3 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 autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 29 Mar 2021 01:59:15 -0000 This rewrites our range adaptor implementation for more comprehensible error messages, improved SFINAE behavior and conformance to P2281. The diagnostic improvements mostly comes from using appropriately named functors instead of lambdas in the generic implementation of partial application and composition of range adaptors, and in the definition of each of the standard range adaptors. This makes their pretty printed types of much more comprehensible. The improved SFINAE behavior comes from constraining the range adaptors' member functions appropriately. This change fixes PR99433, and was also necessary in order to implement the wording changes of P2281. Finally, P2281 clarified that partial application and composition of range adaptors behaves like a perfect forwarding call wrapper. This patch implements this, except that we don't bother adding overloads for forwarding captured state entities as non-const lvalues, since it seems we only really need to handle the const lvalue and non-const rvalue cases. Such overloads can be easily added if they turn out to be needed. Tested on x86_64-pc-linux-gnu, does this look OK for trunk? libstdc++-v3/ChangeLog: PR libstdc++/99433 * include/std/ranges (views::__adaptor): Remove this namespace, moving everything within into the parent namespace. (__maybe_refwrap): Remove. (_detail::__adaptor_invocable): New concept. (_detail::__adaptor_partial_app_viable): New concept. (_RangeAdaptor): Rename template parameter _Callable to _Impl. Remove the now unnecessary deduction guide. (_RangeAdaptor::_M_callable): Remove data member. (_RangeAdaptor::_RangeAdaptor): Remove constructors. (_RangeAdaptor::operator()): Rewrite (_RangeAdaptor::_S_arity): Define. (_RangeAdaptor::_Partial): New class template. (__detail::__pipe_invocable): New concept. (_Pipe): New class template. (_RangeAdaptorClosure): Rename template parameter _Callable to _Callable to _Impl. Don't derive from _RangeAdaptor. New data member _M_impl. Add a defaulted default constructor. Add an explicit constructor taking an _Impl object. Remove the now unnecessary deduction guide. (_RangeAdaptorClosure::operator()): Reimplement. (_RangeAdaptorClosure::operator|): Reimplement. (__detail::__can_ref_view): New concept. (__detail::__can_subrange): New concept. (all): Replace the lambda here with ... (_All): ... this functor. Add appropriate constraints. Remove redundant viewable_range constraint. (__detail::__can_filter_view): New concept. (filter, _Filter): Like with all/_All. (__detail::__can_transform): New concept. (transform, _Transform): Like with all/_All. (__detail::__can_take_view): New concept. (take, _Take): Like with all/_All. (__detail::__can_take_while_view): New concept. (take_while, _TakeWhile): Like with all/_All. (__detail::__can_drop_view): New concept. (drop, _Drop): Like with all/_All. (__detail::__can_drop_while_view): New concept. (drop_while, _DropWhile): Like with all/_All. (__detail::__can_join_view): New concept. (join, _Join): Like with all/_All. (__detail::__can_split_view): New concept. (split, _Split): Like with all/_All. Rename template parameter _Fp to _Pattern). (__detail::__already_common): New concept. (__detail::__can_common_view): New concept. (common, _Common): Like with all/_All. (__detail::__can_reverse_view): New concept. (reverse, _Reverse): Like with all/_All. (__detail::__can_elements_view): New concept. (elements, _Elements): Like with all/_All. (keys, values): Adjust. * testsuite/std/ranges/adaptors/99433.cc: New test. * testsuite/std/ranges/adaptors/all.cc (test05): New test. * testsuite/std/ranges/adaptors/common.cc (test03): New test. * testsuite/std/ranges/adaptors/drop.cc (test09): New test. * testsuite/std/ranges/adaptors/drop_while.cc (test04): New test. * testsuite/std/ranges/adaptors/elements.cc (test04): New test. * testsuite/std/ranges/adaptors/filter.cc (test06): New test. * testsuite/std/ranges/adaptors/join.cc (test09): New test. * testsuite/std/ranges/adaptors/p2281.cc: New test. * testsuite/std/ranges/adaptors/reverse.cc (test07): New test. * testsuite/std/ranges/adaptors/split.cc (test01, test04): Adjust. (test09): New test. * testsuite/std/ranges/adaptors/split_neg.cc (test01): Adjust expected error message. (test02): Likewise. Extend test. * testsuite/std/ranges/adaptors/take.cc (test06): New test. * testsuite/std/ranges/adaptors/take_while.cc (test05): New test. * testsuite/std/ranges/adaptors/transform.cc (test07, test08): New test. --- libstdc++-v3/include/std/ranges | 713 ++++++++++++------ .../testsuite/std/ranges/adaptors/99433.cc | 41 + .../testsuite/std/ranges/adaptors/all.cc | 12 + .../testsuite/std/ranges/adaptors/common.cc | 12 + .../testsuite/std/ranges/adaptors/drop.cc | 18 + .../std/ranges/adaptors/drop_while.cc | 18 + .../testsuite/std/ranges/adaptors/elements.cc | 12 + .../testsuite/std/ranges/adaptors/filter.cc | 18 + .../testsuite/std/ranges/adaptors/join.cc | 12 + .../testsuite/std/ranges/adaptors/p2281.cc | 83 ++ .../testsuite/std/ranges/adaptors/reverse.cc | 15 +- .../testsuite/std/ranges/adaptors/split.cc | 35 +- .../std/ranges/adaptors/split_neg.cc | 10 +- .../testsuite/std/ranges/adaptors/take.cc | 18 + .../std/ranges/adaptors/take_while.cc | 18 + .../std/ranges/adaptors/transform.cc | 37 + 16 files changed, 825 insertions(+), 247 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index adbc6d7b274..a65c2a891a5 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -726,165 +726,208 @@ namespace __detail namespace views { - namespace __adaptor + namespace __detail { - template - inline constexpr auto - __maybe_refwrap(_Tp& __arg) - { return reference_wrapper<_Tp>{__arg}; } + template + concept __adaptor_invocable + = requires { std::declval<_Impl>()(declval<_Args>()...); }; + + template + concept __adaptor_partial_app_viable = (_Impl::_S_arity > 1) + && (sizeof...(_Args) == _Impl::_S_arity - 1) + && (constructible_from, _Args> && ...); + } // namespace __detail - template - inline constexpr auto - __maybe_refwrap(const _Tp& __arg) - { return reference_wrapper{__arg}; } + template + struct _RangeAdaptorClosure; - template - inline constexpr decltype(auto) - __maybe_refwrap(_Tp&& __arg) - { return std::forward<_Tp>(__arg); } + template + struct _RangeAdaptor + { + template + requires __detail::__adaptor_invocable<_Impl, _Range, _Args...> + constexpr auto + operator()(_Range&& __r, _Args&&... __args) const + { + return _Impl{}(std::forward<_Range>(__r), std::forward<_Args>(__args)...); + } - template - struct _RangeAdaptorClosure; + template + struct _Partial; - template - struct _RangeAdaptor - { - protected: - [[no_unique_address]] - __detail::__maybe_present_t, - _Callable> _M_callable; + template + requires __detail::__adaptor_partial_app_viable<_Impl, _Args...> + constexpr auto + operator()(_Args&&... __args) const + { + return _RangeAdaptorClosure{_Partial{std::forward<_Args>(__args)...}}; + } - public: - constexpr - _RangeAdaptor(const _Callable& = {}) - requires is_default_constructible_v<_Callable> - { } + static_assert(_Impl::_S_arity > 1, + "a range adaptor that accepts only one argument " + "must be defined as a _RangeAdaptorClosure"); + }; - constexpr - _RangeAdaptor(_Callable __callable) - requires (!is_default_constructible_v<_Callable>) - : _M_callable(std::move(__callable)) - { } + // Represents partial application of _Impl with arguments _Args. + template + template + struct _RangeAdaptor<_Impl>::_Partial + { + tuple<_Args...> _M_args; - template - requires (sizeof...(_Args) >= 1) - constexpr auto - operator()(_Args&&... __args) const - { - // [range.adaptor.object]: If a range adaptor object accepts more - // than one argument, then the following expressions are equivalent: - // - // (1) adaptor(range, args...) - // (2) adaptor(args...)(range) - // (3) range | adaptor(args...) - // - // In this case, adaptor(args...) is a range adaptor closure object. - // - // We handle (1) and (2) here, and (3) is just a special case of a - // more general case already handled by _RangeAdaptorClosure. - if constexpr (is_invocable_v<_Callable, _Args...>) - { - static_assert(sizeof...(_Args) != 1, - "a _RangeAdaptor that accepts only one argument " - "should be defined as a _RangeAdaptorClosure"); - // Here we handle adaptor(range, args...) -- just forward all - // arguments to the underlying adaptor routine. - return _Callable{}(std::forward<_Args>(__args)...); - } - else - { - // Here we handle adaptor(args...)(range). - // Given args..., we return a _RangeAdaptorClosure that takes a - // range argument, such that (2) is equivalent to (1). - // - // We need to be careful about how we capture args... in this - // closure. By using __maybe_refwrap, we capture lvalue - // references by reference (through a reference_wrapper) and - // otherwise capture by value. - auto __closure - = [...__args(__maybe_refwrap(std::forward<_Args>(__args)))] - (_Range&& __r) { - // This static_cast has two purposes: it forwards a - // reference_wrapper capture as a T&, and otherwise - // forwards the captured argument as an rvalue. - return _Callable{}(std::forward<_Range>(__r), - (static_cast>> - (__args))...); - }; - using _ClosureType = decltype(__closure); - return _RangeAdaptorClosure<_ClosureType>(std::move(__closure)); - } - } - }; + constexpr + _Partial(_Args... __args) + : _M_args(std::move(__args)...) + { } - template - _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>; + template + requires __detail::__adaptor_invocable<_Impl, _Range, const _Args&...> + constexpr auto + operator()(_Range&& __r) const & + { + auto __forwarder = [&__r] (const auto&... __args) { + return _Impl{}(std::forward<_Range>(__r), __args...); + }; + return std::apply(__forwarder, _M_args); + } - template - struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable> - { - using _RangeAdaptor<_Callable>::_RangeAdaptor; + template + requires __detail::__adaptor_invocable<_Impl, _Range, _Args...> + constexpr auto + operator()(_Range&& __r) && + { + auto __forwarder = [&__r] (auto&... __args) { + return _Impl{}(std::forward<_Range>(__r), std::move(__args)...); + }; + return std::apply(__forwarder, _M_args); + } - template - requires requires { declval<_Callable>()(declval<_Range>()); } - constexpr auto - operator()(_Range&& __r) const - { - if constexpr (is_default_constructible_v<_Callable>) - return _Callable{}(std::forward<_Range>(__r)); - else - return this->_M_callable(std::forward<_Range>(__r)); - } + template + constexpr auto + operator()(_Range&& __r) const && = delete; - template - requires requires { declval<_Callable>()(declval<_Range>()); } - friend constexpr auto - operator|(_Range&& __r, const _RangeAdaptorClosure& __o) - { return __o(std::forward<_Range>(__r)); } + static constexpr int _S_arity = 1; + }; - template - friend constexpr auto - operator|(const _RangeAdaptorClosure<_Tp>& __x, - const _RangeAdaptorClosure& __y) - { - if constexpr (is_default_constructible_v<_Tp> - && is_default_constructible_v<_Callable>) - { - auto __closure = [] (_Up&& __e) { - return std::forward<_Up>(__e) | decltype(__x){} | decltype(__y){}; - }; - return _RangeAdaptorClosure(__closure); - } - else if constexpr (is_default_constructible_v<_Tp> - && !is_default_constructible_v<_Callable>) - { - auto __closure = [__y] (_Up&& __e) { - return std::forward<_Up>(__e) | decltype(__x){} | __y; - }; - return _RangeAdaptorClosure(__closure); - } - else if constexpr (!is_default_constructible_v<_Tp> - && is_default_constructible_v<_Callable>) - { - auto __closure = [__x] (_Up&& __e) { - return std::forward<_Up>(__e) | __x | decltype(__y){}; - }; - return _RangeAdaptorClosure(__closure); - } - else - { - auto __closure = [__x, __y] (_Up&& __e) { - return std::forward<_Up>(__e) | __x | __y; - }; - return _RangeAdaptorClosure(__closure); - } - } - }; + // A lightweight partial specialization of the above primary template for + // the common case where _Impl accepts a single extra argument. + template + template + struct _RangeAdaptor<_Impl>::_Partial<_Arg> + { + _Arg _M_arg; - template - _RangeAdaptorClosure(_Callable) -> _RangeAdaptorClosure<_Callable>; - } // namespace __adaptor + constexpr + _Partial(_Arg __arg) + : _M_arg(std::move(__arg)) + { } + + template + requires __detail::__adaptor_invocable<_Impl, _Range, const _Arg&> + constexpr auto + operator()(_Range&& __r) const & + { return _Impl{}(std::forward<_Range>(__r), _M_arg); } + + template + requires __detail::__adaptor_invocable<_Impl, _Range, _Arg> + constexpr auto + operator()(_Range&& __r) && + { return _Impl{}(std::forward<_Range>(__r), std::move(_M_arg)); } + + template + constexpr auto + operator()(_Range&& __r) const && = delete; + + static constexpr int _S_arity = 1; + }; + + namespace __detail + { + template + concept __pipe_invocable + = requires { std::declval<_Rhs>()(std::declval<_Lhs>()(std::declval<_Range>())); }; + } // namespace __detail + + // Represents composition of the range adaptors _Lhs and _Rhs. + template + struct _Pipe + { + [[no_unique_address]] _RangeAdaptorClosure<_Lhs> _M_lhs; + [[no_unique_address]] _RangeAdaptorClosure<_Rhs> _M_rhs; + + constexpr + _Pipe(_RangeAdaptorClosure<_Lhs> __lhs, + _RangeAdaptorClosure<_Rhs> __rhs) + : _M_lhs(std::move(__lhs)), _M_rhs(std::move(__rhs)) + { } + + template + requires __detail::__pipe_invocable + constexpr auto + operator()(_Range&& __r) const & + { return _M_rhs(_M_lhs(std::forward<_Range>(__r))); } + + template + requires __detail::__pipe_invocable<_Lhs, _Rhs, _Range> + constexpr auto + operator()(_Range&& __r) && + { return std::move(_M_rhs)(std::move(_M_lhs)(std::forward<_Range>(__r))); } + + template + constexpr auto + operator()(_Range&& __r) const && = delete; + + static constexpr int _S_arity = 1; + }; + + template + struct _RangeAdaptorClosure + { + [[no_unique_address]] _Impl _M_impl; + + _RangeAdaptorClosure() = default; + + constexpr explicit + _RangeAdaptorClosure(_Impl __impl) + : _M_impl(std::move(__impl)) + { } + + template + requires __detail::__adaptor_invocable + constexpr auto + operator()(_Range&& __r) const & + { return _M_impl(std::forward<_Range>(__r)); } + + template + requires __detail::__adaptor_invocable<_Impl, _Range> + constexpr auto + operator()(_Range&& __r) && + { return std::move(_M_impl)(std::forward<_Range>(__r)); } + + template + constexpr auto + operator()(_Range&& __r) const && = delete; + + template + requires same_as, _RangeAdaptorClosure> + && __detail::__adaptor_invocable<_Self, _Range> + friend constexpr auto + operator|(_Range&& __r, _Self&& __self) + { return std::forward<_Self>(__self)._M_impl(std::forward<_Range>(__r)); } + + template + friend constexpr auto + operator|(_RangeAdaptorClosure __lhs, _RangeAdaptorClosure<_Rhs> __rhs) + { + using _Lhs = _Impl; + return _RangeAdaptorClosure<_Pipe<_Lhs, _Rhs>>{{std::move(__lhs), + std::move(__rhs)}}; + } + + static_assert(_Impl::_S_arity == 1, + "a range adaptor that takes extra arguments " + "must be defined as a _RangeAdaptor"); + }; } // namespace views template requires is_object_v<_Range> @@ -941,20 +984,39 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptorClosure all - = [] (_Range&& __r) - { - if constexpr (view>) - return std::forward<_Range>(__r); - else if constexpr (requires { ref_view{std::forward<_Range>(__r)}; }) - return ref_view{std::forward<_Range>(__r)}; - else - return subrange{std::forward<_Range>(__r)}; - }; + namespace __detail + { + template + concept __can_ref_view = requires { ref_view{std::declval<_Range>()}; }; + + template + concept __can_subrange = requires { subrange{std::declval<_Range>()}; }; + } // namespace __detail + + struct _All + { + template + requires view> + || __detail::__can_ref_view<_Range> + || __detail::__can_subrange<_Range> + constexpr auto + operator()(_Range&& __r) const + { + if constexpr (view>) + return std::forward<_Range>(__r); + else if constexpr (__detail::__can_ref_view<_Range>) + return ref_view{std::forward<_Range>(__r)}; + else + return subrange{std::forward<_Range>(__r)}; + } + + static constexpr int _S_arity = 1; + }; + + inline constexpr _RangeAdaptorClosure<_All> all; template using all_t = decltype(all(std::declval<_Range>())); - } // namespace views // XXX: the following algos are copied from ranges_algo.h to avoid a circular @@ -1305,11 +1367,27 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor filter - = [] (_Range&& __r, _Pred&& __p) - { - return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)}; - }; + namespace __detail + { + template + concept __can_filter_view + = requires { filter_view{std::declval<_Range>(), std::declval<_Pred>()}; }; + } // namespace __detail + + struct _Filter + { + template + requires __detail::__can_filter_view<_Range, _Pred> + constexpr auto + operator()(_Range&& __r, _Pred&& __p) const + { + return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_Filter> filter; } // namespace views template @@ -1653,11 +1731,27 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor transform - = [] (_Range&& __r, _Fp&& __f) - { - return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)}; - }; + namespace __detail + { + template + concept __can_transform_view + = requires { transform_view{std::declval<_Range>(), std::declval<_Fp>()}; }; + } // namespace __detail + + struct _Transform + { + template + requires __detail::__can_transform_view<_Range, _Fp> + constexpr auto + operator()(_Range&& __r, _Fp&& __f) const + { + return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_Transform> transform; } // namespace views template @@ -1816,11 +1910,27 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor take - = [] (_Range&& __r, _Tp&& __n) - { - return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)}; - }; + namespace __detail + { + template + concept __can_take_view + = requires { take_view{std::declval<_Range>(), std::declval<_Tp>()}; }; + } // namespace __detail + + struct _Take + { + template + requires __detail::__can_take_view<_Range, _Tp> + constexpr auto + operator()(_Range&& __r, _Tp&& __n) const + { + return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_Take> take; } // namespace views template @@ -1918,11 +2028,27 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor take_while - = [] (_Range&& __r, _Pred&& __p) - { - return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)}; - }; + namespace __detail + { + template + concept __can_take_while_view + = requires { take_while_view{std::declval<_Range>(), std::declval<_Pred>()}; }; + } // namespace __detail + + struct _TakeWhile + { + template + requires __detail::__can_take_while_view<_Range, _Pred> + constexpr auto + operator()(_Range&& __r, _Pred&& __p) const + { + return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_TakeWhile> take_while; } // namespace views template @@ -2020,11 +2146,27 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor drop - = [] (_Range&& __r, _Tp&& __n) - { - return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)}; - }; + namespace __detail + { + template + concept __can_drop_view + = requires { drop_view{std::declval<_Range>(), std::declval<_Tp>()}; }; + } // namespace __detail + + struct _Drop + { + template + requires __detail::__can_drop_view<_Range, _Tp> + constexpr auto + operator()(_Range&& __r, _Tp&& __n) const + { + return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_Drop> drop; } // namespace views template @@ -2085,12 +2227,28 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor drop_while - = [] (_Range&& __r, _Pred&& __p) - { - return drop_while_view{std::forward<_Range>(__r), - std::forward<_Pred>(__p)}; - }; + namespace __detail + { + template + concept __can_drop_while_view + = requires { drop_while_view{std::declval<_Range>(), std::declval<_Pred>()}; }; + } // namespace __detail + + struct _DropWhile + { + template + requires __detail::__can_drop_while_view<_Range, _Pred> + constexpr auto + operator()(_Range&& __r, _Pred&& __p) const + { + return drop_while_view{std::forward<_Range>(__r), + std::forward<_Pred>(__p)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_DropWhile> drop_while; } // namespace views template @@ -2409,13 +2567,29 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptorClosure join - = [] (_Range&& __r) - { - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 3474. Nesting join_views is broken because of CTAD - return join_view>{std::forward<_Range>(__r)}; - }; + namespace __detail + { + template + concept __can_join_view + = requires { join_view>{std::declval<_Range>()}; }; + } // namespace __detail + + struct _Join + { + template + requires __detail::__can_join_view<_Range> + constexpr auto + operator()(_Range&& __r) const + { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3474. Nesting join_views is broken because of CTAD + return join_view>{std::forward<_Range>(__r)}; + } + + static constexpr int _S_arity = 1; + }; + + inline constexpr _RangeAdaptorClosure<_Join> join; } // namespace views namespace __detail @@ -2784,9 +2958,9 @@ namespace views } }; - template - split_view(_Range&&, _Pred&&) - -> split_view, views::all_t<_Pred>>; + template + split_view(_Range&&, _Pattern&&) + -> split_view, views::all_t<_Pattern>>; template split_view(_Range&&, range_value_t<_Range>) @@ -2794,11 +2968,27 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptor split - = [] (_Range&& __r, _Fp&& __f) - { - return split_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)}; - }; + namespace __detail + { + template + concept __can_split_view + = requires { split_view{std::declval<_Range>(), std::declval<_Pattern>()}; }; + } // namespace __detail + + struct _Split + { + template + requires __detail::__can_split_view<_Range, _Pattern> + constexpr auto + operator()(_Range&& __r, _Pattern&& __f) const + { + return split_view{std::forward<_Range>(__r), std::forward<_Pattern>(__f)}; + } + + static constexpr int _S_arity = 2; + }; + + inline constexpr _RangeAdaptor<_Split> split; } // namespace views namespace views @@ -2911,16 +3101,35 @@ namespace views namespace views { - inline constexpr __adaptor::_RangeAdaptorClosure common - = [] (_Range&& __r) - { - if constexpr (common_range<_Range> - && requires { views::all(std::forward<_Range>(__r)); }) - return views::all(std::forward<_Range>(__r)); - else - return common_view{std::forward<_Range>(__r)}; - }; + namespace __detail + { + template + concept __already_common = common_range<_Range> + && requires { views::all(std::declval<_Range>()); }; + + template + concept __can_common_view + = requires { common_view{std::declval<_Range>()}; }; + } // namespace __detail + struct _Common + { + template + requires __detail::__already_common<_Range> + || __detail::__can_common_view<_Range> + constexpr auto + operator()(_Range&& __r) const + { + if constexpr (__detail::__already_common<_Range>) + return views::all(std::forward<_Range>(__r)); + else + return common_view{std::forward<_Range>(__r)}; + } + + static constexpr int _S_arity = 1; + }; + + inline constexpr _RangeAdaptorClosure<_Common> common; } // namespace views template @@ -3016,27 +3225,42 @@ namespace views template inline constexpr bool __is_reverse_view> = true; - } - inline constexpr __adaptor::_RangeAdaptorClosure reverse - = [] (_Range&& __r) - { - using _Tp = remove_cvref_t<_Range>; - if constexpr (__detail::__is_reverse_view<_Tp>) - return std::forward<_Range>(__r).base(); - else if constexpr (__detail::__is_reversible_subrange<_Tp>) - { - using _Iter = decltype(ranges::begin(__r).base()); - if constexpr (sized_range<_Tp>) - return subrange<_Iter, _Iter, subrange_kind::sized> - (__r.end().base(), __r.begin().base(), __r.size()); - else - return subrange<_Iter, _Iter, subrange_kind::unsized> - (__r.end().base(), __r.begin().base()); - } - else - return reverse_view{std::forward<_Range>(__r)}; - }; + template + concept __can_reverse_view + = requires { reverse_view{std::declval<_Range>()}; }; + } // namespace __detail + + struct _Reverse + { + template + requires __detail::__is_reverse_view> + || __detail::__is_reversible_subrange> + || __detail::__can_reverse_view<_Range> + constexpr auto + operator()(_Range&& __r) const + { + using _Tp = remove_cvref_t<_Range>; + if constexpr (__detail::__is_reverse_view<_Tp>) + return std::forward<_Range>(__r).base(); + else if constexpr (__detail::__is_reversible_subrange<_Tp>) + { + using _Iter = decltype(ranges::begin(__r).base()); + if constexpr (sized_range<_Tp>) + return subrange<_Iter, _Iter, subrange_kind::sized> + {__r.end().base(), __r.begin().base(), __r.size()}; + else + return subrange<_Iter, _Iter, subrange_kind::unsized> + {__r.end().base(), __r.begin().base()}; + } + else + return reverse_view{std::forward<_Range>(__r)}; + } + + static constexpr int _S_arity = 1; + }; + + inline constexpr _RangeAdaptorClosure<_Reverse> reverse; } // namespace views namespace __detail @@ -3335,16 +3559,31 @@ namespace views namespace views { + namespace __detail + { + template + concept __can_elements_view + = requires { elements_view, _Nm>{std::declval<_Range>()}; }; + } // namespace __detail + template - inline constexpr __adaptor::_RangeAdaptorClosure elements - = [] (_Range&& __r) + struct _Elements { - using _El = elements_view, _Nm>; - return _El{std::forward<_Range>(__r)}; + template + requires __detail::__can_elements_view<_Nm, _Range> + constexpr auto + operator()(_Range&& __r) const + { + return elements_view, _Nm>{std::forward<_Range>(__r)}; + } + + static constexpr int _S_arity = 1; }; - inline constexpr __adaptor::_RangeAdaptorClosure keys = elements<0>; - inline constexpr __adaptor::_RangeAdaptorClosure values = elements<1>; + template + inline constexpr _RangeAdaptorClosure<_Elements<_Nm>> elements; + inline constexpr auto keys = elements<0>; + inline constexpr auto values = elements<1>; } // namespace views } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc new file mode 100644 index 00000000000..1fd22dfcdf2 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc @@ -0,0 +1,41 @@ +// 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++2a" } +// { dg-do compile { target c++2a } } + +// PR libstdc++/99433 + +#include +#include + +template +struct deep +{ + underlying_adaptor_t adaptor; + + template + friend auto operator|(range_t &range, deep const &me) + { + return me.adaptor(range[0]); + } +}; + +auto f = [] (auto nucl) -> decltype(nucl + ' ') { return nucl + ' '; }; +auto complement = deep{std::views::transform(f)}; +std::vector> foo{}; +auto v = foo | complement; diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc index 33e4b96686e..ee5670d5fd6 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc @@ -112,6 +112,17 @@ static_assert(sizeof(decltype(views::take(5) | views::drop(5))) | views::drop(5) | views::reverse))); +template +void +test05() +{ + // Verify SFINAE behavior. + static_assert(!requires { all(); }); + static_assert(!requires { all(0, 0); }); + static_assert(!requires { all(0); }); + static_assert(!requires { 0 | all; }); +} + int main() { @@ -119,4 +130,5 @@ main() test02(); static_assert(test03()); static_assert(test04()); + test05(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc index 719b25be006..085e8ff907d 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc @@ -60,9 +60,21 @@ test02() VERIFY( std::count(v2.begin(), v2.end(), 1) == 2); } +template +void +test03() +{ + // Verify SFINAE behavior. + static_assert(!requires { common(); }); + static_assert(!requires { common(0, 0); }); + static_assert(!requires { common(0); }); + static_assert(!requires { 0 | common; }); +} + int main() { test01(); test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc index 19afba8a9ae..c0525109bd1 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc @@ -258,6 +258,23 @@ test08() VERIFY( ra_test_wrapper::increment_count == 0 ); } +template +void +test09() +{ + // Verify SFINAE behavior. + extern int x[5]; + int* n = 0; + static_assert(!requires { drop(); }); + static_assert(!requires { drop(x, n, n); }); + static_assert(!requires { drop(x, n); }); + static_assert(!requires { drop(n)(x); }); + static_assert(!requires { x | (drop(n) | views::all); }); + static_assert(!requires { (drop(n) | views::all)(x); }); + static_assert(!requires { drop | views::all; }); + static_assert(!requires { views::all | drop; }); +} + int main() { @@ -269,4 +286,5 @@ main() test06(); test07(); test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc index e807388538b..58489d54f89 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc @@ -89,6 +89,23 @@ test03() VERIFY( ranges::equal(v, (int[]){3,4,5}) ); } +template +void +test04() +{ + // Verify SFINAE behavior. + extern int x[5]; + auto p = [] (int*) { return true; }; + static_assert(!requires { drop_while(); }); + static_assert(!requires { drop_while(x, p, p); }); + static_assert(!requires { drop_while(x, p); }); + static_assert(!requires { drop_while(p)(x); }); + static_assert(!requires { x | (drop_while(p) | views::all); }); + static_assert(!requires { (drop_while(p) | views::all)(x); }); + static_assert(!requires { drop_while | views::all; }); + static_assert(!requires { views::all | drop_while; }); +} + int main() { @@ -96,4 +113,5 @@ main() test02(); test03(); test03(); + test04(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc index 76807772006..b0d122f8db5 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc @@ -89,10 +89,22 @@ test03() VERIFY( (next(b_const, 2) - b_const) == 2 ); } +template> +void +test04() +{ + // Verify SFINAE behavior. + static_assert(!requires { elements(); }); + static_assert(!requires { elements(0, 0); }); + static_assert(!requires { elements(0); }); + static_assert(!requires { 0 | elements; }); +} + int main() { test01(); test02(); test03(); + test04(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc index c60eec51368..e0f6b8d4c4b 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc @@ -123,6 +123,23 @@ test05() VERIFY( ranges::equal(v, (int[]){2,4}) ); } +template +void +test06() +{ + // Verify SFINAE behavior. + extern int x[5]; + auto p = [] (int*) { return true; }; + static_assert(!requires { filter(); }); + static_assert(!requires { filter(x, p, p); }); + static_assert(!requires { filter(x, p); }); + static_assert(!requires { filter(p)(x); }); + static_assert(!requires { x | (filter(p) | views::all); }); + static_assert(!requires { (filter(p) | views::all)(x); }); + static_assert(!requires { filter | views::all; }); + static_assert(!requires { views::all | filter; }); +} + int main() { @@ -132,4 +149,5 @@ main() test04(); test05(); test05(); + test06(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc index 6d780f4887b..fb06a7698af 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc @@ -149,6 +149,17 @@ test08() VERIFY( i->a == 5 ); } +template +void +test09() +{ + // Verify SFINAE behavior. + static_assert(!requires { join(); }); + static_assert(!requires { join(0, 0); }); + static_assert(!requires { join(0); }); + static_assert(!requires { 0 | join; }); +} + int main() { @@ -160,4 +171,5 @@ main() test06(); test07(); test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc new file mode 100644 index 00000000000..c916a5ea8b7 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc @@ -0,0 +1,83 @@ +// 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++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +// Verify P2281 changes to the forwarding semantics of partial application +// and composition of range adaptor objects. + +void +test01() +{ + auto split_into_strings = [] (auto p) { + return views::split(p) | views::transform([](auto r){ + return std::string(r.begin(), ranges::next(r.begin(), r.end())); + }); + }; + constexpr std::string_view s = "hello world"; + constexpr std::string_view p = " "; + constexpr auto v1 = s | split_into_strings(p); + constexpr auto v2 = split_into_strings(p)(s); + VERIFY( ranges::equal(v1, (std::string_view[]){"hello", "world"}) ); + VERIFY( ranges::equal(v2, (std::string_view[]){"hello", "world"}) ); +} + +struct move_only_range +{ + move_only_range() { } + move_only_range(move_only_range&&); + move_only_range& operator=(move_only_range&&); + move_only_range(const move_only_range&) = delete; + move_only_range& operator=(const move_only_range&) = delete; + char* begin(); + char* end(); +}; + +template<> + inline constexpr bool std::ranges::enable_view = true; + +template +void +test02() +{ + std::string_view s; + move_only_range p; + static_assert(requires { s | split(std::move(p)); }); + static_assert(requires { split(std::move(p))(s); }); + static_assert(requires { split(std::move(p)) | views::all; }); + static_assert(requires { views::all | split(std::move(p)); }); + static_assert(!requires { split(p); }); + static_assert(!requires { split(p) | views::all; }); + static_assert(!requires { views::all | split(p); }); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc index 0d52498e207..47e34eb6581 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc @@ -138,7 +138,8 @@ namespace test_ns void make_reverse_iterator(T&&) {} } // namespace test_ns -void test06() +void +test06() { // Check that views::reverse works and does not use ADL which could lead // to accidentally finding test_ns::make_reverse_iterator(A*). @@ -149,6 +150,17 @@ void test06() static_assert( std::ranges::range ); } +template +void +test07() +{ + // Verify SFINAE behavior. + static_assert(!requires { reverse(); }); + static_assert(!requires { reverse(0, 0); }); + static_assert(!requires { reverse(0); }); + static_assert(!requires { 0 | reverse; }); +} + int main() { @@ -158,4 +170,5 @@ main() test04(); test05(); test06(); + test07(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc index dc7f55a2f92..b9fb3728708 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc @@ -39,7 +39,7 @@ test01() { auto x = "the quick brown fox"sv; auto p = std::string{" "}; - auto v = x | views::split(p); + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); @@ -83,7 +83,7 @@ test04() static_assert(!ranges::view); static_assert(std::same_as>); - auto v = x | views::split(p); + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); @@ -152,6 +152,36 @@ test08() VERIFY( i == v.end() ); } +template +void +test09() +{ + // Verify SFINAE behavior. + std::string s, p; + static_assert(!requires { split(); }); + static_assert(!requires { split(s, p, 0); }); + static_assert(!requires { split(p)(); }); + static_assert(!requires { s | split; }); + + static_assert(!requires { s | split(p); }); + static_assert(!requires { split(p)(s); }); + static_assert(!requires { s | (split(p) | views::all); }); + static_assert(!requires { (split(p) | views::all)(s); }); + + static_assert(requires { s | split(views::all(p)); }); + static_assert(requires { split(views::all(p))(s); }); + static_assert(requires { s | (split(views::all(p)) | views::all); }); + static_assert(requires { (split(views::all(p)) | views::all)(s); }); + + auto adapt = split(p); + static_assert(requires { s | adapt; }); + static_assert(requires { adapt(s); }); + + auto adapt2 = split(p) | views::all; + static_assert(requires { s | adapt2; }); + static_assert(requires { adapt2(s); }); +} + int main() { @@ -163,4 +193,5 @@ main() test06(); test07(); test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc index fb84049711f..4229314a9dc 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc @@ -30,8 +30,7 @@ test01() { using namespace std::literals; auto x = "the quick brown fox"sv; - auto v = views::split(x, std::initializer_list{' ', ' '}); - v.begin(); // { dg-error "" } + auto v = views::split(x, std::initializer_list{' ', ' '}); // { dg-error "no match" } } void @@ -39,11 +38,8 @@ test02() { using namespace std::literals; auto x = "the quick brown fox"sv; - auto v = x | views::split(std::initializer_list{' ', ' '}); // { dg-error "no match" } - v.begin(); + auto v1 = views::split(std::initializer_list{' ', ' '})(x); // { dg-error "deleted" } + auto v2 = x | views::split(std::initializer_list{' ', ' '}); // { dg-error "no match" } } // { dg-prune-output "in requirements" } -// { dg-error "deduction failed" "" { target *-*-* } 0 } -// { dg-error "no match" "" { target *-*-* } 0 } -// { dg-error "constraint failure" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc index 5947c7bafab..55f74824737 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc @@ -100,6 +100,23 @@ test05() b = ranges::end(v); } +template +void +test06() +{ + // Verify SFINAE behavior. + extern int x[5]; + int* n = 0; + static_assert(!requires { take(); }); + static_assert(!requires { take(x, n, n); }); + static_assert(!requires { take(x, n); }); + static_assert(!requires { take(n)(x); }); + static_assert(!requires { x | (take(n) | views::all); }); + static_assert(!requires { (take(n) | views::all)(x); }); + static_assert(!requires { take | views::all; }); + static_assert(!requires { views::all | take; }); +} + int main() { @@ -108,4 +125,5 @@ main() test03(); test04(); test05(); + test06(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc index ff377e578c4..1ab8df3bc62 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc @@ -79,6 +79,23 @@ test04() static_assert(!ranges::range); } +template +void +test05() +{ + // Verify SFINAE behavior. + extern int x[5]; + auto p = [] (int*) { return true; }; + static_assert(!requires { take_while(); }); + static_assert(!requires { take_while(x, p, p); }); + static_assert(!requires { take_while(x, p); }); + static_assert(!requires { take_while(p)(x); }); + static_assert(!requires { x | (take_while(p) | views::all); }); + static_assert(!requires { (take_while(p) | views::all)(x); }); + static_assert(!requires { take_while | views::all; }); + static_assert(!requires { views::all | take_while; }); +} + int main() { @@ -86,4 +103,5 @@ main() test02(); test03(); test04(); + test05(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc index 4e7e7ba0a67..aefe7d4d9a3 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -145,6 +145,41 @@ test06() VERIFY( (next(b_const, 2) - b_const) == 2 ); } +void +test07() +{ + int x[] = {1,2,3,4,5}; + auto v1 = views::transform([] (auto& x) { return &x; }); + auto v2 = views::transform([] (auto x) { return *x; }); + auto v = x | (v1 | v2); + // If ADL for the outermost operator| above found the hidden friend function + // decltype(v2)::operator|(_Range&&, const decltype(v2)&) then we'd first have + // to check this friend function's constraints (because of CWG2369) which + // means checking that the call v2(x) is well-formed, which would lead to a + // fatal error during return type deduction of the second generic lambda above + // (with x having type 'char'). So this test essentially verifies that our + // range adaptors are implemented such that ADL doesn't find this unviable + // hidden friend function in the first place. + VERIFY( ranges::equal(v, x) ); +} + +template +void +test08() +{ + // Verify SFINAE behavior. + extern int x[5]; + auto f = [] (int* e) { return e; }; + static_assert(!requires { transform(); }); + static_assert(!requires { transform(x, f, f); }); + static_assert(!requires { transform(x, f); }); + static_assert(!requires { transform(f)(x); }); + static_assert(!requires { x | (transform(f) | views::all); }); + static_assert(!requires { (transform(f) | views::all)(x); }); + static_assert(!requires { transform | views::all; }); + static_assert(!requires { views::all | transform; }); +} + int main() { @@ -154,4 +189,6 @@ main() test04(); test05(); test06(); + test07(); + test08(); } -- 2.31.1.133.g84d06cdc06