gcc/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
Patrick Palka 0f4a2fb44d libstdc++: Refine range adaptors' "simple extra args" mechanism [PR100940]
The _S_has_simple_extra_args mechanism is used to simplify forwarding
of range adaptor's extra arguments when perfect forwarding call wrapper
semantics isn't required for correctness, on a per-adaptor basis.
Both views::take and views::drop are flagged as such, but it turns out
perfect forwarding semantics are needed for these adaptors in some
contrived cases, e.g. when their extra argument is a move-only class
that's implicitly convertible to an integral type.

To fix this, we could just clear the flag for views::take/drop as with
views::split, but that'd come at the cost of acceptable diagnostics
for ill-formed uses of these adaptors (see PR100577).

This patch instead allows adaptors to parameterize their
_S_has_simple_extra_args flag according the types of the captured extra
arguments, so that we could conditionally disable perfect forwarding
semantics only when the types of the extra arguments permit it.  We
then use this finer-grained mechanism to safely disable perfect
forwarding semantics for views::take/drop when the extra argument is
integer-like, rather than incorrectly always disabling it.  Similarly,
for views::split, rather than always enabling perfect forwarding
semantics we now safely disable it when the extra argument is a scalar
or a view, and recover good diagnostics for these common cases.

	PR libstdc++/100940

libstdc++-v3/ChangeLog:

	* include/std/ranges (__adaptor::_RangeAdaptor): Document the
	template form of _S_has_simple_extra_args.
	(__adaptor::__adaptor_has_simple_extra_args): Add _Args template
	parameter pack.  Try to treat _S_has_simple_extra_args as a
	variable template parameterized by _Args.
	(__adaptor::_Partial): Pass _Arg/_Args to the constraint
	__adaptor_has_simple_extra_args.
	(views::_Take::_S_has_simple_extra_args): Templatize according
	to the type of the extra argument.
	(views::_Drop::_S_has_simple_extra_args): Likewise.
	(views::_Split::_S_has_simple_extra_args): Define.
	* testsuite/std/ranges/adaptors/100577.cc (test01, test02):
	Adjust after changes to _S_has_simple_extra_args mechanism.
	(test03): Define.
2021-06-17 09:46:04 -04:00

116 lines
4.9 KiB
C++

// 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
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
// PR libstdc++/100577
#include <ranges>
namespace ranges = std::ranges;
namespace views = std::ranges::views;
void
test01()
{
// Verify adaptors are deemed to have simple extra arguments when appropriate.
using views::__adaptor::__adaptor_has_simple_extra_args;
using std::identity;
static_assert(__adaptor_has_simple_extra_args<decltype(views::transform), identity>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::filter), identity>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::drop), int>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>);
static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>);
// Verify all adaptor closures except for views::split(pattern) have a simple
// operator().
using views::__adaptor::__closure_has_simple_call_op;
__closure_has_simple_call_op auto a00 = views::all;
__closure_has_simple_call_op auto a01 = views::transform(std::identity{});
__closure_has_simple_call_op auto a02 = views::filter(std::identity{});
__closure_has_simple_call_op auto a03 = views::drop(42);
__closure_has_simple_call_op auto a04 = views::take(42);
__closure_has_simple_call_op auto a05 = views::take_while(std::identity{});
__closure_has_simple_call_op auto a06 = views::drop_while(std::identity{});
__closure_has_simple_call_op auto a07 = views::join;
__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) | a11;
// 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<decltype(a12)>);
static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
}
void
test02()
{
// Range adaptor closures with a simple operator() aren't implemented using a
// fallback deleted overload, so when a call is ill-formed overload resolution
// fails.
extern int x[10];
struct { } badarg;
views::transform(badarg)(x); // { dg-error "no match" }
views::filter(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_while(badarg) | views::all)(x); // { dg-error "no match" }
(views::drop_while(badarg) | views::all)(x); // { dg-error "no match" }
// In practice, range adaptor closures with non-simple operator() are
// implemented using a fallback deleted overload, so when a call is
// ill-formed overload resolution succeeds but selects the deleted overload
// (but only when the closure is invoked as an rvalue).
views::split(badarg)(x); // { dg-error "deleted function" }
(views::split(badarg) | views::all)(x); // { dg-error "deleted function" }
auto a0 = views::split(badarg);
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" }