diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index b2943490e31..a4289009bc4 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -796,7 +796,9 @@ namespace views::__adaptor // // The optional static data member _Derived::_S_has_simple_extra_args should // be defined to true if the behavior of this adaptor is independent of the - // constness/value category of the extra arguments. + // constness/value category of the extra arguments. This data member could + // also be defined as a variable template parameterized by the types of the + // extra arguments. template struct _RangeAdaptor { @@ -818,9 +820,10 @@ namespace views::__adaptor concept __closure_has_simple_call_op = _Adaptor::_S_has_simple_call_op; // True if the behavior of the range adaptor non-closure _Adaptor is - // independent of the value category of its extra arguments. - template - concept __adaptor_has_simple_extra_args = _Adaptor::_S_has_simple_extra_args; + // independent of the value category of its extra arguments _Args. + template + concept __adaptor_has_simple_extra_args = _Adaptor::_S_has_simple_extra_args + || _Adaptor::template _S_has_simple_extra_args<_Args...>; // A range adaptor closure that represents partial application of // the range adaptor _Adaptor with arguments _Args. @@ -897,7 +900,7 @@ namespace views::__adaptor // This lets us get away with a single operator() overload, which makes // overload resolution failure diagnostics more concise. template - requires __adaptor_has_simple_extra_args<_Adaptor> + requires __adaptor_has_simple_extra_args<_Adaptor, _Args...> struct _Partial<_Adaptor, _Args...> : _RangeAdaptorClosure { tuple<_Args...> _M_args; @@ -926,7 +929,7 @@ namespace views::__adaptor // A lightweight specialization of the above template for the common case // where _Adaptor accepts a single extra argument. template - requires __adaptor_has_simple_extra_args<_Adaptor> + requires __adaptor_has_simple_extra_args<_Adaptor, _Arg> struct _Partial<_Adaptor, _Arg> : _RangeAdaptorClosure { _Arg _M_arg; @@ -2112,7 +2115,12 @@ namespace views::__adaptor using _RangeAdaptor<_Take>::operator(); static constexpr int _S_arity = 2; - static constexpr bool _S_has_simple_extra_args = true; + // 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 + static constexpr bool _S_has_simple_extra_args + = ranges::__detail::__is_integer_like<_Tp>; }; inline constexpr _Take take; @@ -2352,7 +2360,9 @@ namespace views::__adaptor using _RangeAdaptor<_Drop>::operator(); static constexpr int _S_arity = 2; - static constexpr bool _S_has_simple_extra_args = true; + template + static constexpr bool _S_has_simple_extra_args + = _Take::_S_has_simple_extra_args<_Tp>; }; inline constexpr _Drop drop; @@ -3230,9 +3240,14 @@ namespace views::__adaptor using _RangeAdaptor<_Split>::operator(); static constexpr int _S_arity = 2; - // The second argument of views::split is _not_ simple -- it can be a - // non-view range, the value category of which affects whether the call is - // well-formed. So we must not define _S_has_simple_extra_args to true. + // The pattern argument of views::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 + // simple. + template + static constexpr bool _S_has_simple_extra_args + = is_scalar_v<_Pattern> || (view<_Pattern> + && copy_constructible<_Pattern>); }; inline constexpr _Split split; diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc index 29176c8b7b0..8ef084621f9 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc @@ -28,16 +28,18 @@ namespace views = std::ranges::views; void test01() { - // Verify all multi-argument adaptors except for views::split are denoted - // to have simple extra arguments. + // Verify adaptors are deemed to have simple extra arguments when appropriate. using views::__adaptor::__adaptor_has_simple_extra_args; - static_assert(__adaptor_has_simple_extra_args); - static_assert(__adaptor_has_simple_extra_args); - static_assert(__adaptor_has_simple_extra_args); - static_assert(__adaptor_has_simple_extra_args); - static_assert(__adaptor_has_simple_extra_args); - static_assert(__adaptor_has_simple_extra_args); - static_assert(!__adaptor_has_simple_extra_args); + using std::identity; + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(__adaptor_has_simple_extra_args); + static_assert(!__adaptor_has_simple_extra_args); // Verify all adaptor closures except for views::split(pattern) have a simple // operator(). @@ -53,15 +55,17 @@ test01() __closure_has_simple_call_op auto a08 = views::common; __closure_has_simple_call_op auto a09 = views::reverse; __closure_has_simple_call_op auto a10 = views::keys; + __closure_has_simple_call_op auto a11 = views::split(' '); // Verify composition of simple closures is simple. __closure_has_simple_call_op auto b - = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10); + = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11; - // Verify views::split is the exception. - auto a11 = views::split(' '); - static_assert(!__closure_has_simple_call_op); - static_assert(!__closure_has_simple_call_op); - static_assert(!__closure_has_simple_call_op); + // Verify views::split(non_view_range) is an exception. + extern std::string s; + auto a12 = views::split(s); + static_assert(!__closure_has_simple_call_op); + static_assert(!__closure_has_simple_call_op); + static_assert(!__closure_has_simple_call_op); } void @@ -71,18 +75,14 @@ test02() // fallback deleted overload, so when a call is ill-formed overload resolution // fails. extern int x[10]; - auto badarg = nullptr; + struct { } badarg; views::transform(badarg)(x); // { dg-error "no match" } views::filter(badarg)(x); // { dg-error "no match" } - views::take(badarg)(x); // { dg-error "no match" } - views::drop(badarg)(x); // { dg-error "no match" } views::take_while(badarg)(x); // { dg-error "no match" } views::drop_while(badarg)(x); // { dg-error "no match" } (views::transform(badarg) | views::all)(x); // { dg-error "no match" } (views::filter(badarg) | views::all)(x); // { dg-error "no match" } - (views::take(badarg) | views::all)(x); // { dg-error "no match" } - (views::drop(badarg) | views::all)(x); // { dg-error "no match" } (views::take_while(badarg) | views::all)(x); // { dg-error "no match" } (views::drop_while(badarg) | views::all)(x); // { dg-error "no match" } @@ -96,6 +96,21 @@ test02() a0(x); // { dg-error "no match" }; auto a1 = a0 | views::all; a1(x); // { dg-error "no match" } + + views::take(badarg)(x); // { dg-error "deleted" } + views::drop(badarg)(x); // { dg-error "deleted" } + (views::take(badarg) | views::all)(x); // { dg-error "deleted" } + (views::drop(badarg) | views::all)(x); // { dg-error "deleted" } +} + +void +test03() +{ + // PR libstdc++/100940 + extern int x[10]; + struct S { operator int() && { return 5; }; }; + x | std::views::take(S{}); + x | std::views::drop(S{}); } // { dg-prune-output "in requirements" }