On Fri, Mar 7, 2025 at 5:08 PM Jonathan Wakely <jwak...@redhat.com> wrote:

>
>
> On Fri, 7 Mar 2025 at 15:59, Patrick Palka <ppa...@redhat.com> wrote:
>
>> On Fri, 7 Mar 2025, Jonathan Wakely wrote:
>>
>> >
>> >
>> > On Fri, 7 Mar 2025 at 15:05, Tomasz Kamiński wrote:
>> >       Add missing move_constructible && regular_invocable constrains on
>> functor type,
>> >       for invocations of `views::zip_transform` without range arguments.
>> >
>> >       libstdc++-v3/ChangeLog:
>> >
>> >               * include/std/ranges (_ZipTransform::operator()):
>> >               Add `move_constructible` and `regular_invocable`
>> constraints
>> >               * testsuite/std/ranges/zip_transform/1.cc: New tests
>> >       ---
>> >        Tested on x86_64-linux. OK for trunk?
>> >
>> >
>> > I think the server hook will reject the commit message. Does `git
>> gcc-verify` say it's OK?
>> >
>> > The summary line has [PR111138] but that PR number is not repeated in
>> the ChangeLog part.
>> >
>> > There should be "<TAB>PR libstdc++/111138" either before the
>> "libstdc++-v3/ChangeLog:" line, or before the "* include/std/ranges" line.
>> >
>> > This policy is documented at
>> > https://gcc.gnu.org/contribute.html#patches
>> >
>> > "If your patch relates a bug in the compiler for which there is an
>> existing PR number the bug number should be stated. Use the short-form
>> variant [PRnnnnn] without the Bugzilla component identifier and with
>> > no space between 'PR' and the number. The body of the commit message
>> should still contain the full form (PR <component>/nnnnn) within the body
>> of the commit message so that Bugzilla will correctly notice the
>> > commit."
>> >
>> >
>> >        libstdc++-v3/include/std/ranges                |  3 ++-
>> >        .../testsuite/std/ranges/zip_transform/1.cc    | 18
>> ++++++++++++++++++
>> >        2 files changed, 20 insertions(+), 1 deletion(-)
>> >
>> >       diff --git a/libstdc++-v3/include/std/ranges
>> b/libstdc++-v3/include/std/ranges
>> >       index e21f5284b46..33e9926b89f 100644
>> >       --- a/libstdc++-v3/include/std/ranges
>> >       +++ b/libstdc++-v3/include/std/ranges
>> >       @@ -5332,7 +5332,8 @@ namespace views::__adaptor
>> >            struct _ZipTransform
>> >            {
>> >              template<typename _Fp, typename... _Ts>
>> >       -       requires (sizeof...(_Ts) == 0) ||
>> __detail::__can_zip_transform_view<_Fp, _Ts...>
>> >       +       requires (sizeof...(_Ts) == 0) &&
>> move_constructible<decay_t<_Fp>> && regular_invocable<decay_t<_Fp>&>
>> >
>> >
>> > I would prefer parentheses here so I don't have to think about the
>> precedence.
>> >
>> > I think it's ((sizeof...(T) == 0) && move_cons && invoc) ||
>> can_zip_xform, right?
>>
>> Pedantically I guess it should be
>>
>>   requires (sizeof...(T) == 0 && move_cons && invoc)
>>     || (sizeof...(T) != 0 && can_zip_xform)
>>
>> We don't want/need to check can_zip_xform if we have no range arguments
>> and an unsuitable functor type.
>>
>
> Ah yes.
>
> can_zip_xform will check sizeof...(T) > 0 because that's in the
> transform_view constraints, but only after checking move_constructible<F>.
>
We are also required to check  `move_constructible<F>` (that I am adding as
part of the patch) for empty range pack case, so this is not observable.
But, I have added (sizeof...(Ts) != 0) check as I have two overloads, and
this will short-circuit.

>
>
>
>>
>> >
>> > Is this still missing a check that decay_t<invoke_result_t<FD&>> is an
>> object type?
>> >
>> > Maybe we want to create a helper concept which checks decay_t<_Fp>,
>> e.g. add this to __detail:
>> >
>> > template<typename _Fd>
>> >   concept __can_xform_empty // TODO: better name?
>> >     = move_constructible<_Fd> && regular_invocable<_Fd&>
>> >       && is_object_v<decay_t<invoke_result_t<_Fd&>>>;
>> >
>> > And then constrain _ZipTransform::operator() with:
>> >
>> >       template<typename _Fp, typename... _Ts>
>> > requires (sizeof...(_Ts) == 0 &&
>> __detail::__can_xform_empty<decay_t<_Fp>>)
>> >  || __detail::__can_zip_transform_view<_Fp, _Ts...>
>> >
>> > Or ... and maybe this is heresy ... we could overload operator()
>> >
>> > template<typename _Fp, typename... _Ts>
>> > requires __detail::__can_zip_transform_view<_Fp, _Ts...>
>> > constexpr auto
>> > operator() [[nodiscard]] (_Fp&& __f, _Ts&&... __ts) const
>> > {
>> >   return zip_transform_view(std::forward<_Fp>(__f),
>> std::forward<_Ts>(__ts)...);
>> > }
>> >
>> > template<typename _Fp>
>> > requires __detail::__can_xform_empty<decay_t<_Fp>>
>> > constexpr auto
>> > operator() [[nodiscard]] (_Fp&& __f) const
>> > {
>> >   return views::empty<decay_t<invoke_result_t<decay_t<_Fp>&>>>;
>> > }
>> >
>> > It's simpler for me to understand this way, but we would have to pay
>> the cost of overload resolution. So probably not a good idea.
>> >
>> >
>> >
>> >       +         || __detail::__can_zip_transform_view<_Fp, _Ts...>
>> >               constexpr auto
>> >               operator() [[nodiscard]] (_Fp&& __f, _Ts&&... __ts) const
>> >               {
>> >       diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>> b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>> >       index 20abdcba0f8..67839261cc7 100644
>> >       --- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>> >       +++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>> >       @@ -9,6 +9,20 @@
>> >        namespace ranges = std::ranges;
>> >        namespace views = std::views;
>> >
>> >       +template<typename T>
>> >       +concept can_zip_transform = requires (T t) {
>> >       +  views::zip_transform(std::forward<T>(t));
>> >       +};
>> >       +
>> >       +static_assert(!can_zip_transform<int>);
>> >       +
>> >       +struct NonMovable {
>> >       +  NonMovable(NonMovable&&) = delete;
>> >       +};
>> >       +
>> >       +static_assert(!can_zip_transform<NonMovable>);
>> >       +static_assert(!can_zip_transform<NonMovable&>);
>> >       +
>> >        constexpr bool
>> >        test01()
>> >        {
>> >       @@ -46,6 +60,10 @@ test01()
>> >          VERIFY( ranges::size(z3) == 3 );
>> >          VERIFY( ranges::equal(z3, (int[]){3, 6, 9}) );
>> >
>> >       +  auto z4 = views::zip_transform([] () { return 1; });
>> >       +  VERIFY( ranges::size(z4) == 0 );
>> >       +  static_assert(
>> std::same_as<ranges::range_value_t<decltype(z4)>, int> );
>> >       +
>> >          return true;
>> >        }
>> >
>> >       --
>> >       2.48.1
>> >
>> >
>> >
>
>

Reply via email to