diff mbox series

[committed] libstdc++: Implement LWG 1203 for rvalue iostreams

Message ID YIwKqS1groHvMeKS@redhat.com
State New
Headers show
Series [committed] libstdc++: Implement LWG 1203 for rvalue iostreams | expand

Commit Message

Jonathan Wakely April 30, 2021, 1:48 p.m. UTC
This implements the resolution of LWG 1203 so that the constraints for
rvalue stream insertion/extraction are simpler, and the return type is
the original rvalue stream type not its base class.

Signed-off-by: Jonathan Wakely <jwakely@redhat.com>

libstdc++-v3/ChangeLog:

	* include/std/istream (operator>>(Istream&&, x&)): Simplify, as
	per LWG 1203.
	* include/std/ostream (operator<<(Ostream&&, const x&)):
	Likewise.
	* testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc:
	Adjust dg-error pattern.
	* testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc:
	Likewise.
	* testsuite/27_io/basic_istream/extractors_other/char/4.cc: Define
	is_extractable trait to replace std::__is_extractable. Make it
	work with rvalue streams as well as lvalues, to replace f() and
	g() helper functions.
	* testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc:
	Likewise.
	* testsuite/27_io/basic_ostream/inserters_other/char/6.cc:
	Define is_insertable trait to replace std::__is_insertable. Make
	it work with rvalue streams as well as lvalues, to replace f()
	and g() helper functions.
	* testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc:
	Likewise.
	* testsuite/27_io/filesystem/path/io/dr2989.cc: Prune additional
	errors from new constraints.
	* testsuite/27_io/rvalue_streams-2.cc: Remove PR 80675 checks,
	which are no longer expected to compile.
	* testsuite/27_io/rvalue_streams.cc: Adjust existing test.
	Verify LWG 1203 changes.

Tested powerpc64le-linux. Committed to trunk.
commit aa475c4ac80733f85ba47b109fc1900f05e810e2
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Apr 30 14:07:28 2021

    libstdc++: Implement LWG 1203 for rvalue iostreams
    
    This implements the resolution of LWG 1203 so that the constraints for
    rvalue stream insertion/extraction are simpler, and the return type is
    the original rvalue stream type not its base class.
    
    Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
    
    libstdc++-v3/ChangeLog:
    
            * include/std/istream (operator>>(Istream&&, x&)): Simplify, as
            per LWG 1203.
            * include/std/ostream (operator<<(Ostream&&, const x&)):
            Likewise.
            * testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc:
            Adjust dg-error pattern.
            * testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc:
            Likewise.
            * testsuite/27_io/basic_istream/extractors_other/char/4.cc: Define
            is_extractable trait to replace std::__is_extractable. Make it
            work with rvalue streams as well as lvalues, to replace f() and
            g() helper functions.
            * testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc:
            Likewise.
            * testsuite/27_io/basic_ostream/inserters_other/char/6.cc:
            Define is_insertable trait to replace std::__is_insertable. Make
            it work with rvalue streams as well as lvalues, to replace f()
            and g() helper functions.
            * testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc:
            Likewise.
            * testsuite/27_io/filesystem/path/io/dr2989.cc: Prune additional
            errors from new constraints.
            * testsuite/27_io/rvalue_streams-2.cc: Remove PR 80675 checks,
            which are no longer expected to compile.
            * testsuite/27_io/rvalue_streams.cc: Adjust existing test.
            Verify LWG 1203 changes.

Comments

Jonathan Wakely May 7, 2021, 10:49 p.m. UTC | #1
On 06/05/21 18:28 +0100, Jonathan Wakely wrote:
>On 06/05/21 18:09 +0100, Jonathan Wakely wrote:
>>On 06/05/21 17:55 +0200, Stephan Bergmann wrote:
>>>On 30/04/2021 15:48, Jonathan Wakely via Libstdc++ wrote:
>>>>This implements the resolution of LWG 1203 so that the constraints for
>>>>rvalue stream insertion/extraction are simpler, and the return type is
>>>>the original rvalue stream type not its base class.
>>>>
>>>>Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
>>>>
>>>>libstdc++-v3/ChangeLog:
>>>>
>>>>	* include/std/istream (operator>>(Istream&&, x&)): Simplify, as
>>>>	per LWG 1203.
>>>>	* include/std/ostream (operator<<(Ostream&&, const x&)):
>>>>	Likewise.
>>>>	* testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc:
>>>>	Adjust dg-error pattern.
>>>>	* testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc:
>>>>	Likewise.
>>>>	* testsuite/27_io/basic_istream/extractors_other/char/4.cc: Define
>>>>	is_extractable trait to replace std::__is_extractable. Make it
>>>>	work with rvalue streams as well as lvalues, to replace f() and
>>>>	g() helper functions.
>>>>	* testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc:
>>>>	Likewise.
>>>>	* testsuite/27_io/basic_ostream/inserters_other/char/6.cc:
>>>>	Define is_insertable trait to replace std::__is_insertable. Make
>>>>	it work with rvalue streams as well as lvalues, to replace f()
>>>>	and g() helper functions.
>>>>	* testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc:
>>>>	Likewise.
>>>>	* testsuite/27_io/filesystem/path/io/dr2989.cc: Prune additional
>>>>	errors from new constraints.
>>>>	* testsuite/27_io/rvalue_streams-2.cc: Remove PR 80675 checks,
>>>>	which are no longer expected to compile.
>>>>	* testsuite/27_io/rvalue_streams.cc: Adjust existing test.
>>>>	Verify LWG 1203 changes.
>>>>
>>>>Tested powerpc64le-linux. Committed to trunk.
>>>
>>>FWIW, it looks like this is causing issues for Clang (at least 
>>>Clang 11 and recent Clang 13 trunk):
>>>
>>>>$ cat test.cc
>>>>#include <ostream>
>>>>int i = 1 << std::ios::erase_event;
>>>
>>>(i.e., using and enum in namespace std),
>>>
>>>>$ clang++ --gcc-toolchain=~/gcc/trunk/inst -fsyntax-only test.cc
>>>>In file included from test.cc:1:
>>>>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/12.0.0/../../../../include/c++/12.0.0/ostream:727:33: error: cannot initialize a parameter of type 'std::ios_base *' with an rvalue of type 'int *'
>>>>  __rval_streamable(ios_base* = (_Tp*)nullptr);
>>>>                              ^ ~~~~~~~~~~~~~
>>>>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/12.0.0/../../../../include/c++/12.0.0/ostream:733:25: note: in instantiation of default function argument expression for '__rval_streamable<int, void>' required here
>>>>         typename = decltype(std::__rval_streamable<_Os>()
>>>>                             ^
>>>>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/12.0.0/../../../../include/c++/12.0.0/ostream:748:12: note: in instantiation of default argument for '__rvalue_stream_insertion_t<int, std::ios_base::event>' required here
>>>>  inline __rvalue_stream_insertion_t<_Ostream, _Tp>
>>>>         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>>test.cc:2:11: note: while substituting deduced template arguments into function template 'operator<<' [with _Ostream = int, _Tp = std::ios_base::event]
>>>>int i = 1 << std::ios::erase_event;
>>>>        ^
>>>>~/gcc/trunk/inst/lib/gcc/x86_64-pc-linux-gnu/12.0.0/../../../../include/c++/12.0.0/ostream:727:33: note: passing argument to parameter here
>>>>  __rval_streamable(ios_base* = (_Tp*)nullptr);
>>>>                              ^
>>>>1 error generated.
>>
>>It looks like the failed conversion with the default argument is not
>>in the immediate context, so is an error not a substitution failure.
>>Clang is probably right, so I'll change it.
>>
>>The reason I did it that way was to save instantiating
>>std::is_convertible but also because it seemed like an easy way to
>>avoid confusing diagnostics that say:
>>
>>error: forming pointer to reference type 'std::basic_ostream<char>&'
>>
>>for overload resolution failures for operator<< (because those
>>diagnostics are already hundreds of lines long and confusing enough
>>already).
>>
>>This seems to work:
>>
>>--- a/libstdc++-v3/include/std/ostream
>>+++ b/libstdc++-v3/include/std/ostream
>>@@ -722,9 +722,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>    __rval_streamable();
>>#else
>>  template<typename _Tp,
>>-          typename = _Require<__not_<__is_one_of<_Tp, _Tp&, ios_base>>>>
>>+          typename = _Require<__not_<is_same<_Tp, ios_base>>,
>>+                              is_convertible<_Tp*, ios_base*>>>
>>    _Tp&
>>-    __rval_streamable(ios_base* = (_Tp*)nullptr);
>>+    __rval_streamable();
>>#endif
>>
>>  // SFINAE helper to check constraints for operator<<(Ostream&&, const T&).
>>
>>
>>I'll finish testing that.
>
>Actually, if the function parameter can't be used for the convertible
>check then there's no benefit to that function at all. It's simpler to
>just put all the constraints directly on the alias template that also
>checks the operator<< expression:
>
>  // SFINAE helper to check constraints for operator<<(Ostream&&, const T&).
>  // If the constraints are satisfied, it is an alias for Ostream&&.
>#if __cpp_lib_concepts
>  // Use concepts if possible because they're cheaper to evaluate.
>  template<typename _Os, typename _Tp>
>    requires (!is_same_v<_Os, ios_base>)
>      && (!is_lvalue_reference_v<_Os>)
>      && requires (_Os* __os, ios_base* __b, const _Tp& __t) {
>	  __b = __os;
>	  *__os << __t;
>      }
>#else
>  template<typename _Os, typename _Tp,
>	   typename = _Require<__not_<is_same<_Os, ios_base>>,
>			       is_convertible<_Os*, ios_base*>>,
>	   typename
>	     = decltype(std::declval<_Os&>() << std::declval<const _Tp&>())>
>#endif
>    using __rvalue_stream_insertion_t = _Os&&;

Here's what I've pushed to trunk, and will push to gcc-11 after
testing it on the branch.

Tested x86_64-linux and powerpc64le-linux.
diff mbox series

Patch

diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream
index 1e5ebe4e88c..ea34cce6298 100644
--- a/libstdc++-v3/include/std/istream
+++ b/libstdc++-v3/include/std/istream
@@ -953,80 +953,34 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     ws(basic_istream<_CharT, _Traits>& __is);
 
 #if __cplusplus >= 201103L
-  template<typename _Ch, typename _Up>
-    basic_istream<_Ch, _Up>&
-    __is_convertible_to_basic_istream_test(basic_istream<_Ch, _Up>*);
-
-  template<typename _Tp, typename = void>
-    struct __is_convertible_to_basic_istream_impl
-    {
-      using __istream_type = void;
-    };
-
-  template<typename _Tp>
-    using __do_is_convertible_to_basic_istream_impl =
-    decltype(__is_convertible_to_basic_istream_test
-	     (declval<typename remove_reference<_Tp>::type*>()));
-
-  template<typename _Tp>
-    struct __is_convertible_to_basic_istream_impl
-    <_Tp,
-     __void_t<__do_is_convertible_to_basic_istream_impl<_Tp>>>
-    {
-      using __istream_type =
-	__do_is_convertible_to_basic_istream_impl<_Tp>;
-    };
-
-  template<typename _Tp>
-    struct __is_convertible_to_basic_istream
-    : __is_convertible_to_basic_istream_impl<_Tp>
-    {
-    public:
-      using type = __not_<is_void<
-        typename __is_convertible_to_basic_istream_impl<_Tp>::__istream_type>>;
-      constexpr static bool value = type::value;
-    };
-
-  template<typename _Istream, typename _Tp, typename = void>
-    struct __is_extractable : false_type {};
-
-  template<typename _Istream, typename _Tp>
-    struct __is_extractable<_Istream, _Tp,
-			    __void_t<decltype(declval<_Istream&>()
-					      >> declval<_Tp>())>>
-    : true_type {};
-
-  template<typename _Istream>
-    using __rvalue_istream_type =
-      typename __is_convertible_to_basic_istream<
-	_Istream>::__istream_type;
-
-  // [27.7.1.6] Rvalue stream extraction
+  // C++11 27.7.2.6 Rvalue stream extraction [istream.rvalue]
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
   // 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>())>
+    using __rvalue_stream_extraction_t = _Is&&;
+
   /**
    *  @brief  Generic extractor for rvalue stream
    *  @param  __is  An input stream.
    *  @param  __x  A reference to the extraction target.
-   *  @return  is
+   *  @return  __is
    *
    *  This is just a forwarding function to allow extraction from
    *  rvalue streams since they won't bind to the extractor functions
    *  that take an lvalue reference.
   */
   template<typename _Istream, typename _Tp>
-    inline
-    typename enable_if<__and_<__not_<is_lvalue_reference<_Istream>>,
-			      __is_convertible_to_basic_istream<_Istream>,
-			      __is_extractable<
-				__rvalue_istream_type<_Istream>,
-				_Tp&&>>::value,
-		       __rvalue_istream_type<_Istream>>::type
+    inline __rvalue_stream_extraction_t<_Istream, _Tp>
     operator>>(_Istream&& __is, _Tp&& __x)
     {
-      __rvalue_istream_type<_Istream> __ret_is = __is;
-      __ret_is >> std::forward<_Tp>(__x);
-      return __ret_is;
+      __is >> std::forward<_Tp>(__x);
+      return std::move(__is);
     }
 #endif // C++11
 
diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index a70221f2513..fdd2a87665c 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -704,77 +704,52 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __os.flush(); }
 
 #if __cplusplus >= 201103L
-  template<typename _Ch, typename _Up>
-    basic_ostream<_Ch, _Up>&
-    __is_convertible_to_basic_ostream_test(basic_ostream<_Ch, _Up>*);
-
-  template<typename _Tp, typename = void>
-    struct __is_convertible_to_basic_ostream_impl
-    {
-      using __ostream_type = void;
-    };
+  // C++11 27.7.3.9 Rvalue stream insertion [ostream.rvalue]
+  // _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
   template<typename _Tp>
-    using __do_is_convertible_to_basic_ostream_impl =
-    decltype(__is_convertible_to_basic_ostream_test
-	     (declval<typename remove_reference<_Tp>::type*>()));
+    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
 
-  template<typename _Tp>
-    struct __is_convertible_to_basic_ostream_impl
-    <_Tp,
-     __void_t<__do_is_convertible_to_basic_ostream_impl<_Tp>>>
-    {
-      using __ostream_type =
-	__do_is_convertible_to_basic_ostream_impl<_Tp>;
-    };
-
-  template<typename _Tp>
-    struct __is_convertible_to_basic_ostream
-    : __is_convertible_to_basic_ostream_impl<_Tp>
-    {
-    public:
-      using type = __not_<is_void<
-        typename __is_convertible_to_basic_ostream_impl<_Tp>::__ostream_type>>;
-      constexpr static bool value = type::value;
-    };
-
-  template<typename _Ostream, typename _Tp, typename = void>
-    struct __is_insertable : false_type {};
-
-  template<typename _Ostream, typename _Tp>
-    struct __is_insertable<_Ostream, _Tp,
-			   __void_t<decltype(declval<_Ostream&>()
-					     << declval<const _Tp&>())>>
-				    : true_type {};
-
-  template<typename _Ostream>
-    using __rvalue_ostream_type =
-      typename __is_convertible_to_basic_ostream<
-	_Ostream>::__ostream_type;
+  // 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&>())>
+    using __rvalue_stream_insertion_t = _Os&&;
 
   /**
    *  @brief  Generic inserter for rvalue stream
    *  @param  __os  An input stream.
    *  @param  __x  A reference to the object being inserted.
-   *  @return  os
+   *  @return  __os
    *
    *  This is just a forwarding function to allow insertion to
    *  rvalue streams since they won't bind to the inserter functions
    *  that take an lvalue reference.
   */
   template<typename _Ostream, typename _Tp>
-    inline
-    typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>,
-			      __is_convertible_to_basic_ostream<_Ostream>,
-			      __is_insertable<
-				__rvalue_ostream_type<_Ostream>,
-				const _Tp&>>::value,
-		       __rvalue_ostream_type<_Ostream>>::type
+    inline __rvalue_stream_insertion_t<_Ostream, _Tp>
     operator<<(_Ostream&& __os, const _Tp& __x)
     {
-      __rvalue_ostream_type<_Ostream> __ret_os = __os;
-      __ret_os << __x;
-      return __ret_os;
+      __os << __x;
+      return std::move(__os);
     }
 
 #if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc
index 7a39e7c1a3c..abd68af8a9a 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc
@@ -26,9 +26,9 @@ 
 void
 test01(std::istream& in, char* pc, signed char* sc, unsigned char* uc)
 {
-  in >> pc; // { dg-error "here" }
-  in >> sc; // { dg-error "here" }
-  in >> uc; // { dg-error "here" }
+  in >> pc; // { dg-error "no match" }
+  in >> sc; // { dg-error "no match" }
+  in >> uc; // { dg-error "no match" }
 }
 
 struct CT : std::char_traits<char> { };
@@ -37,9 +37,9 @@  void
 test02(std::basic_istream<char, CT>& in, char* pc, signed char* sc,
        unsigned char* uc)
 {
-  in >> pc; // { dg-error "here" }
-  in >> sc; // { dg-error "here" }
-  in >> uc; // { dg-error "here" }
+  in >> pc; // { dg-error "no match" }
+  in >> sc; // { dg-error "no match" }
+  in >> uc; // { dg-error "no match" }
 }
 
 // { dg-excess-errors "" }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc
index bc8dfacf5f5..214f8d6a931 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc
@@ -26,7 +26,7 @@ 
 void
 test01(std::wistream& in, wchar_t* wc)
 {
-  in >> wc; // { dg-error "here" }
+  in >> wc; // { dg-error "no match" }
 }
 
 struct WT : std::char_traits<wchar_t> { };
@@ -34,7 +34,7 @@  struct WT : std::char_traits<wchar_t> { };
 void
 test02(std::basic_istream<wchar_t, WT>& in, wchar_t* wc)
 {
-  in >> wc; // { dg-error "here" }
+  in >> wc; // { dg-error "no match" }
 }
 
 // { dg-excess-errors "" }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/4.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/4.cc
index 5ad1c1b4e47..9f1e293474f 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/4.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/char/4.cc
@@ -21,6 +21,21 @@ 
 
 #include <sstream>
 
+template<typename Istream, typename T, typename = void>
+  struct is_extractable : std::false_type
+  { };
+
+template<typename> using void_t = void;
+
+template<typename Istream, typename T>
+  using extract_result
+    = decltype(std::declval<Istream>() >> std::declval<const T&>());
+
+template<typename Istream, typename T>
+  struct is_extractable<Istream, T, void_t<extract_result<Istream, T>>>
+  : std::true_type
+  { };
+
 struct X {};
 std::istream& operator>>(std::istream&, X&) = delete;
 
@@ -30,20 +45,6 @@  std::istream& operator>>(std::istream& is, Y&&) {return is;}
 
 struct Z{};
 
-template <class T>
-auto f(T&&) -> decltype(void(std::declval<std::istream&>()
-			     >> std::declval<T&&>()),
-			std::true_type());
-
-std::false_type f(...);
-
-template <class T>
-auto g(T&&) -> decltype(void(std::declval<std::istream&&>()
-			     >> std::declval<T&&>()),
-			std::true_type());
-
-std::false_type g(...);
-
 void test01()
 {
   Y y;
@@ -52,42 +53,18 @@  void test01()
   is >> Y();
   std::istringstream() >> y;
   std::istringstream() >> Y();
-  static_assert(!std::__is_extractable<std::istream&, X&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&&, X&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&, X&&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&&, X&&>::value, "");
-  static_assert(std::__is_extractable<std::istream&, Y&>::value, "");
-  static_assert(std::__is_extractable<std::istream&&, Y&>::value, "");
-  static_assert(std::__is_extractable<std::istream&, Y&&>::value, "");
-  static_assert(std::__is_extractable<std::istream&&, Y&&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&, Z&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&&, Z&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&, Z&&>::value, "");
-  static_assert(!std::__is_extractable<std::istream&&, Z&&>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
-		std::false_type>::value, "");
+  static_assert(!is_extractable<std::istream&, X&>::value, "");
+  static_assert(!is_extractable<std::istream&&, X&>::value, "");
+  static_assert(!is_extractable<std::istream&, X&&>::value, "");
+  static_assert(!is_extractable<std::istream&&, X&&>::value, "");
+  static_assert(is_extractable<std::istream&, Y&>::value, "");
+  static_assert(is_extractable<std::istream&&, Y&>::value, "");
+  static_assert(is_extractable<std::istream&, Y&&>::value, "");
+  static_assert(is_extractable<std::istream&&, Y&&>::value, "");
+  static_assert(!is_extractable<std::istream&, Z&>::value, "");
+  static_assert(!is_extractable<std::istream&&, Z&>::value, "");
+  static_assert(!is_extractable<std::istream&, Z&&>::value, "");
+  static_assert(!is_extractable<std::istream&&, Z&&>::value, "");
 }
 
 int main()
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc
index 94f2d9da09d..fc7f5505bf4 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc
@@ -21,6 +21,21 @@ 
 
 #include <sstream>
 
+template<typename Istream, typename T, typename = void>
+  struct is_extractable : std::false_type
+  { };
+
+template<typename> using void_t = void;
+
+template<typename Istream, typename T>
+  using extract_result
+    = decltype(std::declval<Istream>() >> std::declval<const T&>());
+
+template<typename Istream, typename T>
+  struct is_extractable<Istream, T, void_t<extract_result<Istream, T>>>
+  : std::true_type
+  { };
+
 struct X {};
 std::wistream& operator>>(std::wistream&, X&) = delete;
 
@@ -30,20 +45,6 @@  std::wistream& operator>>(std::wistream& is, Y&&) {return is;}
 
 struct Z{};
 
-template <class T>
-auto f(T&&) -> decltype(void(std::declval<std::wistream&>()
-			     >> std::declval<T&&>()),
-			std::true_type());
-
-std::false_type f(...);
-
-template <class T>
-auto g(T&&) -> decltype(void(std::declval<std::wistream&&>()
-			     >> std::declval<T&&>()),
-			std::true_type());
-
-std::false_type g(...);
-
 void test01()
 {
   Y y;
@@ -52,42 +53,18 @@  void test01()
   is >> Y();
   std::wistringstream() >> y;
   std::wistringstream() >> Y();
-  static_assert(!std::__is_extractable<std::wistream&, X&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&&, X&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&, X&&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&&, X&&>::value, "");
-  static_assert(std::__is_extractable<std::wistream&, Y&>::value, "");
-  static_assert(std::__is_extractable<std::wistream&&, Y&>::value, "");
-  static_assert(std::__is_extractable<std::wistream&, Y&&>::value, "");
-  static_assert(std::__is_extractable<std::wistream&&, Y&&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&, Z&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&&, Z&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&, Z&&>::value, "");
-  static_assert(!std::__is_extractable<std::wistream&&, Z&&>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
-		std::false_type>::value, "");
+  static_assert(!is_extractable<std::wistream&, X&>::value, "");
+  static_assert(!is_extractable<std::wistream&&, X&>::value, "");
+  static_assert(!is_extractable<std::wistream&, X&&>::value, "");
+  static_assert(!is_extractable<std::wistream&&, X&&>::value, "");
+  static_assert(is_extractable<std::wistream&, Y&>::value, "");
+  static_assert(is_extractable<std::wistream&&, Y&>::value, "");
+  static_assert(is_extractable<std::wistream&, Y&&>::value, "");
+  static_assert(is_extractable<std::wistream&&, Y&&>::value, "");
+  static_assert(!is_extractable<std::wistream&, Z&>::value, "");
+  static_assert(!is_extractable<std::wistream&&, Z&>::value, "");
+  static_assert(!is_extractable<std::wistream&, Z&&>::value, "");
+  static_assert(!is_extractable<std::wistream&&, Z&&>::value, "");
 }
 
 int main()
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/6.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/6.cc
index b86763c60fd..4801afcba6c 100644
--- a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/6.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/6.cc
@@ -21,6 +21,22 @@ 
 
 #include <sstream>
 
+template<typename Ostream, typename T, typename = void>
+  struct is_insertable
+  : std::false_type
+  { };
+
+template<typename> using void_t = void;
+
+template<typename Ostream, typename T>
+  using insert_result
+    = decltype(std::declval<Ostream>() << std::declval<const T&>());
+
+template<typename Ostream, typename T>
+  struct is_insertable<Ostream, T, void_t<insert_result<Ostream, T>>>
+  : std::true_type
+  { };
+
 struct X {};
 std::ostream& operator<<(std::ostream&, const X&) = delete;
 
@@ -30,20 +46,6 @@  std::ostream& operator<<(std::ostream&& os, const Y&) {return os;}
 
 struct Z{};
 
-template <class T>
-auto f(T&&) -> decltype(void(std::declval<std::ostream&>()
-			     << std::declval<T&&>()),
-			std::true_type());
-
-std::false_type f(...);
-
-template <class T>
-auto g(T&&) -> decltype(void(std::declval<std::ostream&&>()
-			     << std::declval<T&&>()),
-			std::true_type());
-
-std::false_type g(...);
-
 void test01()
 {
   Y y;
@@ -52,42 +54,18 @@  void test01()
   os << Y();
   std::ostringstream() << y;
   std::ostringstream() << Y();
-  static_assert(!std::__is_insertable<std::ostream&, X&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&&, X&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&, X&&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&&, X&&>::value, "");
-  static_assert(std::__is_insertable<std::ostream&, Y&>::value, "");
-  static_assert(std::__is_insertable<std::ostream&&, Y&&>::value, "");
-  static_assert(std::__is_insertable<std::ostream&, Y&>::value, "");
-  static_assert(std::__is_insertable<std::ostream&&, Y&&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&, Z&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&&, Z&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&, Z&&>::value, "");
-  static_assert(!std::__is_insertable<std::ostream&&, Z&&>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
-		std::false_type>::value, "");
+  static_assert(!is_insertable<std::ostream&, X&>::value, "");
+  static_assert(!is_insertable<std::ostream&&, X&>::value, "");
+  static_assert(!is_insertable<std::ostream&, X&&>::value, "");
+  static_assert(!is_insertable<std::ostream&&, X&&>::value, "");
+  static_assert(is_insertable<std::ostream&, Y&>::value, "");
+  static_assert(is_insertable<std::ostream&&, Y&&>::value, "");
+  static_assert(is_insertable<std::ostream&, Y&>::value, "");
+  static_assert(is_insertable<std::ostream&&, Y&&>::value, "");
+  static_assert(!is_insertable<std::ostream&, Z&>::value, "");
+  static_assert(!is_insertable<std::ostream&&, Z&>::value, "");
+  static_assert(!is_insertable<std::ostream&, Z&&>::value, "");
+  static_assert(!is_insertable<std::ostream&&, Z&&>::value, "");
 }
 
 int main()
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc
index 0994a949652..3efeb804b00 100644
--- a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc
@@ -21,6 +21,22 @@ 
 
 #include <sstream>
 
+template<typename Ostream, typename T, typename = void>
+  struct is_insertable
+  : std::false_type
+  { };
+
+template<typename> using void_t = void;
+
+template<typename Ostream, typename T>
+  using insert_result
+    = decltype(std::declval<Ostream>() << std::declval<const T&>());
+
+template<typename Ostream, typename T>
+  struct is_insertable<Ostream, T, void_t<insert_result<Ostream, T>>>
+  : std::true_type
+  { };
+
 struct X {};
 std::wostream& operator<<(std::wostream&, const X&) = delete;
 
@@ -30,20 +46,6 @@  std::wostream& operator<<(std::wostream&& os, const Y&) {return os;}
 
 struct Z{};
 
-template <class T>
-auto f(T&&) -> decltype(void(std::declval<std::wostream&>()
-			     << std::declval<T&&>()),
-			std::true_type());
-
-std::false_type f(...);
-
-template <class T>
-auto g(T&&) -> decltype(void(std::declval<std::wostream&&>()
-			     << std::declval<T&&>()),
-			std::true_type());
-
-std::false_type g(...);
-
 void test01()
 {
   Y y;
@@ -52,42 +54,18 @@  void test01()
   os << Y();
   std::wostringstream() << y;
   std::wostringstream() << Y();
-  static_assert(!std::__is_insertable<std::wostream&, X&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&&, X&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&, X&&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&&, X&&>::value, "");
-  static_assert(std::__is_insertable<std::wostream&, Y&>::value, "");
-  static_assert(std::__is_insertable<std::wostream&&, Y&&>::value, "");
-  static_assert(std::__is_insertable<std::wostream&, Y&>::value, "");
-  static_assert(std::__is_insertable<std::wostream&&, Y&&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&, Z&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&&, Z&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&, Z&&>::value, "");
-  static_assert(!std::__is_insertable<std::wostream&&, Z&&>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<X&&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
-		std::true_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&>())),
-		std::false_type>::value, "");
-  static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
-		std::false_type>::value, "");
+  static_assert(!is_insertable<std::wostream&, X&>::value, "");
+  static_assert(!is_insertable<std::wostream&&, X&>::value, "");
+  static_assert(!is_insertable<std::wostream&, X&&>::value, "");
+  static_assert(!is_insertable<std::wostream&&, X&&>::value, "");
+  static_assert(is_insertable<std::wostream&, Y&>::value, "");
+  static_assert(is_insertable<std::wostream&&, Y&&>::value, "");
+  static_assert(is_insertable<std::wostream&, Y&>::value, "");
+  static_assert(is_insertable<std::wostream&&, Y&&>::value, "");
+  static_assert(!is_insertable<std::wostream&, Z&>::value, "");
+  static_assert(!is_insertable<std::wostream&&, Z&>::value, "");
+  static_assert(!is_insertable<std::wostream&, Z&&>::value, "");
+  static_assert(!is_insertable<std::wostream&&, Z&&>::value, "");
 }
 
 int main()
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/io/dr2989.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/io/dr2989.cc
index c5cda776477..609f1c32a0e 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/path/io/dr2989.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/path/io/dr2989.cc
@@ -33,3 +33,4 @@  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" }
diff --git a/libstdc++-v3/testsuite/27_io/rvalue_streams-2.cc b/libstdc++-v3/testsuite/27_io/rvalue_streams-2.cc
index 7aa09aef471..d2b6997e4b9 100644
--- a/libstdc++-v3/testsuite/27_io/rvalue_streams-2.cc
+++ b/libstdc++-v3/testsuite/27_io/rvalue_streams-2.cc
@@ -58,16 +58,7 @@  struct X { };
 std::ostream& operator<<(std::ostream& os, const X&) { return os; }
 std::istream& operator>>(std::istream& is, X&&) { return is; }
 
-struct O : std::ostream { };
-
-void operator<<(O&, X) = delete;
-
-struct I : std::istream { };
-
-void operator>>(I&, X) = delete;
-
 // PR libstdc++/65543
-// PR libstdc++/80675
 // PR libstdc++/80940
 int main()
 {
@@ -82,6 +73,4 @@  int main()
   MyStream2 stream2{};
   stream2 << "aaa";
   stream2 >> msi;
-  O{} << X{};
-  I{} >> X{};
 }
diff --git a/libstdc++-v3/testsuite/27_io/rvalue_streams.cc b/libstdc++-v3/testsuite/27_io/rvalue_streams.cc
index def706999a3..3d359091b76 100644
--- a/libstdc++-v3/testsuite/27_io/rvalue_streams.cc
+++ b/libstdc++-v3/testsuite/27_io/rvalue_streams.cc
@@ -25,8 +25,6 @@  void
 test01()
 {
   int i = 1742;
-  // This usage isn't supported by the current draft.
-  // std::string result = (std::ostringstream() << i).str();
   std::ostringstream() << i;
   std::string result ("1742");
   int i2;
@@ -45,10 +43,10 @@  test02()
 {
   X x;
   std::istringstream is;
-  auto& ref1 = (std::move(is) >> x);
+  auto&& ref1 = (std::move(is) >> x);
   VERIFY( &ref1 == &is );
   VERIFY( x.as_rvalue == false );
-  auto& ref2 = (std::move(is) >> std::move(x));
+  auto&& ref2 = (std::move(is) >> std::move(x));
   VERIFY( &ref2 == &is );
   VERIFY( x.as_rvalue == true );
 
@@ -57,6 +55,71 @@  test02()
   std::istringstream("x") >> &arr[0];
 #endif
   std::istringstream("x") >> arr;
+  VERIFY( std::string(arr) == "x" );
+}
+
+// LWG 1203 More useful rvalue stream insertion
+void
+test03()
+{
+  int i = 1203;
+  std::string result = (std::ostringstream() << "i = " << i).str();
+  VERIFY( result == "i = 1203" );
+
+  std::ostringstream os;
+  std::ostringstream&& ros = std::move(os) << result;
+  VERIFY( &ros == &os );
+  VERIFY( ros.str() == result );
+
+  std::stringstream ss;
+  std::stringstream&& rss = std::move(ss) << result;
+  VERIFY( &rss == &ss );
+  VERIFY( rss.str() == result );
+
+  std::istringstream is("first second third");
+  std::istringstream&& ris = std::move(is) >> result;
+  VERIFY( &ris == &is );
+  VERIFY( result == "first" );
+
+  std::stringstream ss2("fourth fifth sixth");
+  std::stringstream&& rss2 = std::move(ss2) >> result;
+  VERIFY( &rss2 == &ss2 );
+  VERIFY( result == "fourth" );
+}
+
+struct A { friend void operator<<(std::ios_base&, A) { } };
+
+struct O : private std::ios_base { friend void operator<<(O&, int) { } };
+
+template<typename Ostream, typename T, typename = void>
+  struct is_insertable
+  : std::false_type
+  { };
+
+template<typename> using void_t = void;
+
+template<typename Ostream, typename T>
+  using insert_result
+    = decltype(std::declval<Ostream>() << std::declval<const T&>());
+
+template<typename Ostream, typename T>
+  struct is_insertable<Ostream, T, void_t<insert_result<Ostream, T>>>
+  : std::true_type
+  { };
+
+// LWG 1203 negative tests
+void
+test04()
+{
+  static_assert( is_insertable<std::ios_base&, A>::value,
+      "valid using the friend operator<<" );
+  static_assert( !is_insertable<std::ios_base&&, A>::value,
+      "ill-formed because ios_base is not derived from ios_base" );
+
+  static_assert( is_insertable<O&, int>::value,
+      "valid using the friend operator<<" );
+  static_assert( !is_insertable<O&&, int>::value,
+      "ill-formed because O is not publicly derived from ios_base" );
 }
 
 int
@@ -64,5 +127,6 @@  main()
 {
   test01();
   test02();
-  return 0;
+  test03();
+  test04();
 }