public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed] libstdc++: Implement LWG 1203 for rvalue iostreams
@ 2021-04-30 13:48 Jonathan Wakely
  2021-05-06 15:55 ` Stephan Bergmann
  0 siblings, 1 reply; 5+ messages in thread
From: Jonathan Wakely @ 2021-04-30 13:48 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1534 bytes --]

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.


[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 33028 bytes --]

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.

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();
 }

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed] libstdc++: Implement LWG 1203 for rvalue iostreams
  2021-04-30 13:48 [committed] libstdc++: Implement LWG 1203 for rvalue iostreams Jonathan Wakely
@ 2021-05-06 15:55 ` Stephan Bergmann
  2021-05-06 17:09   ` Jonathan Wakely
  0 siblings, 1 reply; 5+ messages in thread
From: Stephan Bergmann @ 2021-05-06 15:55 UTC (permalink / raw)
  To: libstdc++; +Cc: Jonathan Wakely

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.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed] libstdc++: Implement LWG 1203 for rvalue iostreams
  2021-05-06 15:55 ` Stephan Bergmann
@ 2021-05-06 17:09   ` Jonathan Wakely
  2021-05-06 17:28     ` Jonathan Wakely
  0 siblings, 1 reply; 5+ messages in thread
From: Jonathan Wakely @ 2021-05-06 17:09 UTC (permalink / raw)
  To: Stephan Bergmann; +Cc: libstdc++

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.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed] libstdc++: Implement LWG 1203 for rvalue iostreams
  2021-05-06 17:09   ` Jonathan Wakely
@ 2021-05-06 17:28     ` Jonathan Wakely
  2021-05-07 22:49       ` Jonathan Wakely
  0 siblings, 1 reply; 5+ messages in thread
From: Jonathan Wakely @ 2021-05-06 17:28 UTC (permalink / raw)
  To: Stephan Bergmann; +Cc: libstdc++

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&&;


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed] libstdc++: Implement LWG 1203 for rvalue iostreams
  2021-05-06 17:28     ` Jonathan Wakely
@ 2021-05-07 22:49       ` Jonathan Wakely
  0 siblings, 0 replies; 5+ messages in thread
From: Jonathan Wakely @ 2021-05-07 22:49 UTC (permalink / raw)
  To: Stephan Bergmann; +Cc: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 6036 bytes --]

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.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 8957 bytes --]

commit a87ceadf17b4a899f3e74e2da8b6b209461d2742
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu May 6 19:14:42 2021

    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.

diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream
index ea34cce6298..5ad60dbd709 100644
--- a/libstdc++-v3/include/std/istream
+++ b/libstdc++-v3/include/std/istream
@@ -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
diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index fdd2a87665c..981697324c9 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -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
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 9f1e293474f..94e41a17e82 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
@@ -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>
 
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 fc7f5505bf4..b182be7ee3d 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
@@ -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>
 
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 4801afcba6c..3a748cd630b 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
@@ -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>
 
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/99692.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/99692.cc
new file mode 100644
index 00000000000..e41399a1bf1
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_other/char/99692.cc
@@ -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;
+}
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 3efeb804b00..8fc0694dddc 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
@@ -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>
 
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 609f1c32a0e..c5cda776477 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/path/io/dr2989.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/path/io/dr2989.cc
@@ -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" }

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-05-07 22:49 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-30 13:48 [committed] libstdc++: Implement LWG 1203 for rvalue iostreams Jonathan Wakely
2021-05-06 15:55 ` Stephan Bergmann
2021-05-06 17:09   ` Jonathan Wakely
2021-05-06 17:28     ` Jonathan Wakely
2021-05-07 22:49       ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).