On Mon, 27 Jan 2025, Patrick Palka wrote:
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk/14?
>
> -- >8 --
>
> This case was incorrectly failing in C++23 mode even after P2492R2
> because the perfect forwarding simplification mechanism assumed bound
> arguments are copy-constructible which is no longer necessarily true
> after that paper. It'd be easy enough to fix the mechanism, but in
> C++23 mode the mechanism is no longer useful since we can just rely on
> deducing 'this' to implement perfect forwarding with a single overload
> (and done in r14-7150-gd2cb4693a0b383). So this patch just disables
> the mechanism in C++23 mode so that the generic implementation is always
> used.
I should add that this doesn't constitute an ABI change because the
disabled "simple" partial specializations of _Partial and _Pipe have the
same layout as the corresponding non-simple versions.
>
> PR libstdc++/118413
>
> libstdc++-v3/ChangeLog:
>
> * include/std/ranges
> (_GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING): New.
> (__closure_has_simple_call_op): Only define in C++20 mode.
> (__closure_has_simple_extra_args): Likewise.
> (_Partial, _Pipe): Likewise, for the "simple" partial
> specializations.
> (*::_S_has_simple_call_op): Likewise.
> (*::_S_has_simple_extra_args): Likewise.
> * testsuite/std/ranges/adaptors/100577.cc: Disable some
> implementation detail checks in C++23 mode.
> * testsuite/std/ranges/adaptors/transform.cc (test09): Also test
> partially applying the move-only function.
> ---
> libstdc++-v3/include/std/ranges | 53 +++++++++++++++++++
> .../testsuite/std/ranges/adaptors/100577.cc | 4 +-
> .../std/ranges/adaptors/transform.cc | 2 +
> 3 files changed, 58 insertions(+), 1 deletion(-)
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index ad69a94b21f..a2b8748bea6 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -1028,6 +1028,16 @@ namespace views::__adaptor
> }
> };
>
> +#if __cplusplus <= 202003L
> + // In C++20 mode we simplify perfect forwarding of a range adaptor
> closure's
> + // bound arguments when possible (according to their types), for sake of
> compile
> + // times and diagnostic quality. In C++23 mode we instead rely on
> deducing 'this'
> + // to idiomatically implement perfect forwarding. Note that this means the
> + // simplification logic doesn't consider C++23 <ranges> changes such as
> P2492R2.
> +# define _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING 1
> +#endif
> +
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> // True if the range adaptor closure _Adaptor has a simple operator(), i.e.
> // one that's not overloaded according to constness or value category of
> the
> // _Adaptor object.
> @@ -1039,6 +1049,7 @@ namespace views::__adaptor
> template<typename _Adaptor, typename... _Args>
> concept __adaptor_has_simple_extra_args =
> _Adaptor::_S_has_simple_extra_args
> || _Adaptor::template _S_has_simple_extra_args<_Args...>;
> +#endif
>
> // A range adaptor closure that represents partial application of
> // the range adaptor _Adaptor with arguments _Args.
> @@ -1139,6 +1150,7 @@ namespace views::__adaptor
> #endif
> };
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> // Partial specialization of the primary template for the case where the
> extra
> // arguments of the adaptor can always be safely and efficiently forwarded
> by
> // const reference. This lets us get away with a single operator()
> overload,
> @@ -1195,6 +1207,7 @@ namespace views::__adaptor
>
> static constexpr bool _S_has_simple_call_op = true;
> };
> +#endif // _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
>
> template<typename _Lhs, typename _Rhs, typename _Range>
> concept __pipe_invocable
> @@ -1245,6 +1258,7 @@ namespace views::__adaptor
> #endif
> };
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> // A partial specialization of the above primary template for the case
> where
> // both adaptor operands have a simple operator(). This in turn lets us
> // implement composition using a single simple operator(), which makes
> @@ -1271,6 +1285,7 @@ namespace views::__adaptor
>
> static constexpr bool _S_has_simple_call_op = true;
> };
> +#endif // _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> } // namespace views::__adaptor
>
> #if __cpp_lib_ranges >= 202202L
> @@ -1454,7 +1469,9 @@ namespace views::__adaptor
> return owning_view{std::forward<_Range>(__r)};
> }
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_call_op = true;
> +#endif
> };
>
> inline constexpr _All all;
> @@ -1872,7 +1889,9 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_Filter>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _Filter filter;
> @@ -2260,7 +2279,9 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_Transform>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _Transform transform;
> @@ -2494,12 +2515,14 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_Take>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> // The count argument of views::take is not always simple -- it can be
> // e.g. a move-only class that's implicitly convertible to the
> difference
> // type. But an integer-like count argument is surely simple.
> template<typename _Tp>
> static constexpr bool _S_has_simple_extra_args
> = ranges::__detail::__is_integer_like<_Tp>;
> +#endif
> };
>
> inline constexpr _Take take;
> @@ -2621,7 +2644,9 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_TakeWhile>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _TakeWhile take_while;
> @@ -2777,9 +2802,11 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_Drop>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> template<typename _Tp>
> static constexpr bool _S_has_simple_extra_args
> = _Take::_S_has_simple_extra_args<_Tp>;
> +#endif
> };
>
> inline constexpr _Drop drop;
> @@ -2866,7 +2893,9 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_DropWhile>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _DropWhile drop_while;
> @@ -3273,7 +3302,9 @@ namespace views::__adaptor
> return join_view<all_t<_Range>>{std::forward<_Range>(__r)};
> }
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_call_op = true;
> +#endif
> };
>
> inline constexpr _Join join;
> @@ -3730,6 +3761,7 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_LazySplit>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> // The pattern argument of views::lazy_split is not always simple --
> it can be
> // a non-view range, the value category of which affects whether the
> call
> // is well-formed. But a scalar or a view pattern argument is surely
> @@ -3738,6 +3770,7 @@ namespace views::__adaptor
> static constexpr bool _S_has_simple_extra_args
> = is_scalar_v<_Pattern> || (view<_Pattern>
> && copy_constructible<_Pattern>);
> +#endif
> };
>
> inline constexpr _LazySplit lazy_split;
> @@ -3937,9 +3970,11 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_Split>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> template<typename _Pattern>
> static constexpr bool _S_has_simple_extra_args
> = _LazySplit::_S_has_simple_extra_args<_Pattern>;
> +#endif
> };
>
> inline constexpr _Split split;
> @@ -4074,7 +4109,9 @@ namespace views::__adaptor
> return common_view{std::forward<_Range>(__r)};
> }
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_call_op = true;
> +#endif
> };
>
> inline constexpr _Common common;
> @@ -4207,7 +4244,9 @@ namespace views::__adaptor
> return reverse_view{std::forward<_Range>(__r)};
> }
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_call_op = true;
> +#endif
> };
>
> inline constexpr _Reverse reverse;
> @@ -4598,7 +4637,9 @@ namespace views::__adaptor
> return elements_view<all_t<_Range>, _Nm>{std::forward<_Range>(__r)};
> }
>
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_call_op = true;
> +#endif
> };
>
> template<size_t _Nm>
> @@ -6061,7 +6102,9 @@ namespace views::__adaptor
>
> using __adaptor::_RangeAdaptor<_AdjacentTransform>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> template<size_t _Nm>
> @@ -6617,7 +6660,9 @@ namespace views::__adaptor
>
> using __adaptor::_RangeAdaptor<_Chunk>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _Chunk chunk;
> @@ -6992,7 +7037,9 @@ namespace views::__adaptor
>
> using __adaptor::_RangeAdaptor<_Slide>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _Slide slide;
> @@ -7187,7 +7234,9 @@ namespace views::__adaptor
>
> using __adaptor::_RangeAdaptor<_ChunkBy>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _ChunkBy chunk_by;
> @@ -7715,9 +7764,11 @@ namespace views::__adaptor
>
> using _RangeAdaptor<_JoinWith>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> template<typename _Pattern>
> static constexpr bool _S_has_simple_extra_args
> = _LazySplit::_S_has_simple_extra_args<_Pattern>;
> +#endif
> };
>
> inline constexpr _JoinWith join_with;
> @@ -8333,7 +8384,9 @@ namespace views::__adaptor
>
> using __adaptor::_RangeAdaptor<_Stride>::operator();
> static constexpr int _S_arity = 2;
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> static constexpr bool _S_has_simple_extra_args = true;
> +#endif
> };
>
> inline constexpr _Stride stride;
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> index 53c81be7f02..eaa8454b318 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> @@ -31,6 +31,7 @@ namespace views = std::ranges::views;
> void
> test01()
> {
> +#ifdef _GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING
> // Verify adaptors are deemed to have simple extra arguments when
> appropriate.
> using views::__adaptor::__adaptor_has_simple_extra_args;
> using std::identity;
> @@ -78,6 +79,7 @@ test01()
> static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>);
> static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>);
> #endif
> +#endif
> }
>
> void
> @@ -135,7 +137,7 @@ test03()
> void
> test04()
> {
> -#if __STDC_HOSTED__
> +#if defined(_GLIBCXX_RANGES_SIMPLIFY_ADAPTOR_FORWARDING) && __STDC_HOSTED__
> // Non-trivially-copyable extra arguments make a closure not simple.
> using F = std::function<bool(bool)>;
> static_assert(!std::is_trivially_copyable_v<F>);
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> index dfc91fb5e1f..934d2f65dcf 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> @@ -191,8 +191,10 @@ test09()
> #if __cpp_lib_ranges >= 202207L
> // P2494R2 Relaxing range adaptors to allow for move only types
> static_assert( requires { transform(x, move_only{}); } );
> + static_assert( requires { x | transform(move_only{}); } ); // PR
> libstdc++/118413
> #else
> static_assert( ! requires { transform(x, move_only{}); } );
> + static_assert( ! requires { x | transform(move_only{}); } );
> #endif
> }
>
> --
> 2.48.1.91.g5f8f7081f7
>
>