libstdc++: Fix constraints for rvalue stream insertion/extraction

The __rval_streamable() function was an attempt to test for
convertibility cheaply and without confusing diagnostics. It doesn't
work with Clang though, and is probably ill-formed.

Replace that helper function with a check for derivation from ios_base,
and use that in the alias templates __rvalue_stream_insertion_t and
__rvalue_stream_extraction_t. Use concepts for the constraints when
available.

libstdc++-v3/ChangeLog:

	* include/std/istream (__rvalue_stream_extraction_t): Replace
	use of __rval_streamable.
	* include/std/ostream (__rvalue_stream_insertion_t): Likewise.
	(__rval_streamable): Remove.
	(_Require_derived_from_ios_base, __derived_from_ios_base): New
	helper for checking constraints.
	* testsuite/27_io/basic_istream/extractors_other/char/4.cc: Fix
	reference to the wrong subclause of the standard.
	* testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc:
	Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/char/6.cc:
	Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc:
	Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/char/99692.cc:
	New test.
	* testsuite/27_io/filesystem/path/io/dr2989.cc: Adjust pruned
	errors.
This commit is contained in:
Jonathan Wakely 2021-05-06 19:14:42 +01:00
parent 7c4c9fcc0d
commit a87ceadf17
8 changed files with 67 additions and 31 deletions

View file

@ -958,12 +958,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 2328. Rvalue stream extraction should use perfect forwarding
// 1203. More useful rvalue stream insertion
// SFINAE helper to check constraints for operator>>(Istream&&, T&&).
// If the constraints are satisfied, it is an alias for Istream&&.
template<typename _Is, typename _Tp,
typename = decltype(std::__rval_streamable<_Is>()
>> std::declval<_Tp>())>
#if __cpp_lib_concepts
template<typename _Is, typename _Tp>
requires __derived_from_ios_base<_Is>
&& requires (_Is& __is, _Tp&& __t) { __is >> std::forward<_Tp>(__t); }
using __rvalue_stream_extraction_t = _Is&&;
#else
template<typename _Is, typename _Tp,
typename = _Require_derived_from_ios_base<_Is>,
typename = decltype(std::declval<_Is&>() >> std::declval<_Tp>())>
using __rvalue_stream_extraction_t = _Is&&;
#endif
/**
* @brief Generic extractor for rvalue stream

View file

@ -708,31 +708,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 1203. More useful rvalue stream insertion
// SFINAE helper to check constraints for operator<<(Ostream&&, const T&).
// If Ostream is publicly and unambiguously derived from ios_base, then
// __rval_streamable<Ostream>() is equivalent to declval<Ostream&>().
// Otherwise, it results in a substitution failure. Specifically, it will
// fail if Ostream is an lvalue reference or the same type as ios_base.
// Use concepts if possible because they're cheaper to evaluate.
#if __cpp_lib_concepts
// Use concepts if possible because they're cheaper to evaluate.
template<typename _Tp>
requires (!is_same_v<_Tp, ios_base>)
&& requires (_Tp* __t, ios_base* __b) { __b = __t; }
_Tp&
__rval_streamable();
#else
template<typename _Tp,
typename = _Require<__not_<__is_one_of<_Tp, _Tp&, ios_base>>>>
_Tp&
__rval_streamable(ios_base* = (_Tp*)nullptr);
#endif
concept __derived_from_ios_base = is_class_v<_Tp>
&& (!is_same_v<_Tp, ios_base>)
&& requires (_Tp* __t, ios_base* __b) { __b = __t; };
// SFINAE helper to check constraints for operator<<(Ostream&&, const T&).
// If the constraints are satisfied, it is an alias for Ostream&&.
template<typename _Os, typename _Tp,
typename = decltype(std::__rval_streamable<_Os>()
<< std::declval<const _Tp&>())>
template<typename _Os, typename _Tp>
requires __derived_from_ios_base<_Os>
&& requires (_Os& __os, const _Tp& __t) { __os << __t; }
using __rvalue_stream_insertion_t = _Os&&;
#else
template<typename _Tp>
using _Require_derived_from_ios_base
= _Require<is_class<_Tp>, __not_<is_same<_Tp, ios_base>>,
is_convertible<typename add_pointer<_Tp>::type, ios_base*>>;
template<typename _Os, typename _Tp,
typename = _Require_derived_from_ios_base<_Os>,
typename
= decltype(std::declval<_Os&>() << std::declval<const _Tp&>())>
using __rvalue_stream_insertion_t = _Os&&;
#endif
/**
* @brief Generic inserter for rvalue stream

View file

@ -17,7 +17,7 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// 27.6.2.5.3 basic_ostream manipulator inserters
// C++11 27.7.2.6 Rvalue stream extraction [istream.rvalue]
#include <sstream>

View file

@ -17,7 +17,7 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// 27.6.2.5.3 basic_ostream manipulator inserters
// C++11 27.7.2.6 Rvalue stream extraction [istream.rvalue]
#include <sstream>

View file

@ -17,7 +17,7 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// 27.6.2.5.3 basic_ostream manipulator inserters
// C++11 27.7.3.9 Rvalue stream insertion [ostream.rvalue]
#include <sstream>

View file

@ -0,0 +1,34 @@
// { dg-do compile { target c++11 } }
#include <ostream>
struct CustomStream : std::ostream {};
namespace N {
class A{};
}
std::ostream& operator<<(std::ostream& s, const N::A&)
{
return s;
}
CustomStream&& operator<<(CustomStream&& s, const N::A& v)
{
static_cast<std::ostream&>(s) << v;
return std::move(s);
}
void test_pr99692()
{
// PR libstdc++/99692
CustomStream() << N::A{};
}
int test_shift_ios_enum()
{
// https://gcc.gnu.org/pipermail/libstdc++/2021-May/052507.html
int i = 1 << std::ios::erase_event;
return i;
}

View file

@ -17,7 +17,7 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// 27.6.2.5.3 basic_ostream manipulator inserters
// C++11 27.7.3.9 Rvalue stream insertion [ostream.rvalue]
#include <sstream>

View file

@ -33,4 +33,3 @@ void foo(std::iostream& s) {
s >> p; // { dg-error "no match" }
}
// { dg-prune-output "no type .*enable_if" }
// { dg-prune-output "no matching function for call to '__rval_streamable" }