]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Fix exception handling in std::ostream seek functions
authorJonathan Wakely <jwakely@redhat.com>
Fri, 25 Jun 2021 17:31:23 +0000 (18:31 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Fri, 25 Jun 2021 17:47:23 +0000 (18:47 +0100)
N3168 added the requirement that the [ostream.seeks] functions create a
sentry object. Nothing in the requirements of those functions says
anything about catching exceptions and setting badbit.

As well as not catching exceptions, this change results in another
observable behaviour change. Previously seeking on a stream with eofbit
set would work (as long as badbit and failbit weren't set). The
construction of a sentry causes failbit to be set when eofbit is set,
which causes the seek to fail. It is necessary to clear the eofbit
before seeking now.

libstdc++-v3/ChangeLog:

* include/bits/ostream.tcc (sentry): Only set failbit if badbit
is set, not if eofbit is set.
(tellp, seekp, seekp): Create sentry object. Do not set badbit
on exceptions.
* testsuite/27_io/basic_ostream/seekp/char/exceptions_badbit_throw.cc:
Adjust expected behaviour.
* testsuite/27_io/basic_ostream/seekp/wchar_t/exceptions_badbit_throw.cc:
Likewise.
* testsuite/27_io/basic_ostream/tellp/char/exceptions_badbit_throw.cc:
Likewise.
* testsuite/27_io/basic_ostream/tellp/wchar_t/exceptions_badbit_throw.cc:
Likewise.
* testsuite/27_io/basic_ostream/seekp/char/n3168.cc: New test.
* testsuite/27_io/basic_ostream/seekp/wchar_t/n3168.cc: New test.
* testsuite/27_io/basic_ostream/tellp/char/n3168.cc: New test.
* testsuite/27_io/basic_ostream/tellp/wchar_t/n3168.cc: New test.

libstdc++-v3/include/bits/ostream.tcc
libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/exceptions_badbit_throw.cc
libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/n3168.cc [new file with mode: 0644]
libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/exceptions_badbit_throw.cc
libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/n3168.cc [new file with mode: 0644]
libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/exceptions_badbit_throw.cc
libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/n3168.cc [new file with mode: 0644]
libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/exceptions_badbit_throw.cc
libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/n3168.cc [new file with mode: 0644]

index 76ca28561c18b010fe88b0c3da10fc4457f4118d..20585f447ac27d42e140b101564b0a797059ed0d 100644 (file)
@@ -53,7 +53,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       if (__os.good())
        _M_ok = true;
-      else
+      else if (__os.bad())
        __os.setstate(ios_base::failbit);
     }
 
@@ -236,19 +236,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     basic_ostream<_CharT, _Traits>::
     tellp()
     {
+      sentry __cerb(*this);
       pos_type __ret = pos_type(-1);
-      __try
-       {
-         if (!this->fail())
-           __ret = this->rdbuf()->pubseekoff(0, ios_base::cur, ios_base::out);
-       }
-      __catch(__cxxabiv1::__forced_unwind&)
-       {
-         this->_M_setstate(ios_base::badbit);          
-         __throw_exception_again;
-       }
-      __catch(...)
-       { this->_M_setstate(ios_base::badbit); }
+      if (!this->fail())
+       __ret = this->rdbuf()->pubseekoff(0, ios_base::cur, ios_base::out);
       return __ret;
     }
 
@@ -257,30 +248,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     basic_ostream<_CharT, _Traits>::
     seekp(pos_type __pos)
     {
-      ios_base::iostate __err = ios_base::goodbit;
-      __try
+      sentry __cerb(*this);
+      if (!this->fail())
        {
-         if (!this->fail())
-           {
-             // _GLIBCXX_RESOLVE_LIB_DEFECTS
-             // 136.  seekp, seekg setting wrong streams?
-             const pos_type __p = this->rdbuf()->pubseekpos(__pos,
-                                                            ios_base::out);
+         // _GLIBCXX_RESOLVE_LIB_DEFECTS
+         // 136.  seekp, seekg setting wrong streams?
+         const pos_type __p = this->rdbuf()->pubseekpos(__pos, ios_base::out);
 
-             // 129. Need error indication from seekp() and seekg()
-             if (__p == pos_type(off_type(-1)))
-               __err |= ios_base::failbit;
-           }
-       }
-      __catch(__cxxabiv1::__forced_unwind&)
-       {
-         this->_M_setstate(ios_base::badbit);          
-         __throw_exception_again;
+         // 129. Need error indication from seekp() and seekg()
+         if (__p == pos_type(off_type(-1)))
+           this->setstate(ios_base::failbit);
        }
-      __catch(...)
-       { this->_M_setstate(ios_base::badbit); }
-      if (__err)
-       this->setstate(__err);
       return *this;
     }
 
@@ -289,30 +267,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     basic_ostream<_CharT, _Traits>::
     seekp(off_type __off, ios_base::seekdir __dir)
     {
-      ios_base::iostate __err = ios_base::goodbit;
-      __try
+      sentry __cerb(*this);
+      if (!this->fail())
        {
-         if (!this->fail())
-           {
-             // _GLIBCXX_RESOLVE_LIB_DEFECTS
-             // 136.  seekp, seekg setting wrong streams?
-             const pos_type __p = this->rdbuf()->pubseekoff(__off, __dir,
-                                                            ios_base::out);
+         // _GLIBCXX_RESOLVE_LIB_DEFECTS
+         // 136.  seekp, seekg setting wrong streams?
+         const pos_type __p = this->rdbuf()->pubseekoff(__off, __dir,
+                                                        ios_base::out);
 
-             // 129. Need error indication from seekp() and seekg()
-             if (__p == pos_type(off_type(-1)))
-               __err |= ios_base::failbit;
-           }
+         // 129. Need error indication from seekp() and seekg()
+         if (__p == pos_type(off_type(-1)))
+           this->setstate(ios_base::failbit);
        }
-      __catch(__cxxabiv1::__forced_unwind&)
-       {
-         this->_M_setstate(ios_base::badbit);          
-         __throw_exception_again;
-       }
-      __catch(...)
-       { this->_M_setstate(ios_base::badbit); }
-      if (__err)
-       this->setstate(__err);
       return *this;
     }
 
index a5a95fd872da64152fa8e295528a7c597311feab..bc59578bfa10b594d4cad317e1423ecf5502cb9a 100644 (file)
@@ -28,7 +28,6 @@ void test01()
 
   __gnu_test::fail_streambuf bib;
   ostream stream(&bib);
-  stream.exceptions(ios_base::badbit);
 
   ostream::pos_type pos = ostream::pos_type();
 
@@ -37,14 +36,11 @@ void test01()
       stream.seekp(pos);
       VERIFY( false );
     }
-  catch (const __gnu_test::positioning_error&) 
+  catch (const __gnu_test::positioning_error&)
     {
-      // stream should set badbit and rethrow facet_error.
-      VERIFY( stream.bad() );
-      VERIFY( (stream.rdstate() & ios_base::failbit) == 0 );
-      VERIFY( !stream.eof() );
+      VERIFY( stream.good() );
     }
-  catch (...) 
+  catch (...)
     {
       VERIFY( false );
     }
@@ -56,7 +52,6 @@ void test02()
 
   __gnu_test::fail_streambuf bib;
   ostream stream(&bib);
-  stream.exceptions(ios_base::badbit);
 
   ostream::off_type off(5);
 
@@ -65,14 +60,11 @@ void test02()
       stream.seekp(off, ios_base::cur);
       VERIFY( false );
     }
-  catch (const __gnu_test::positioning_error&) 
+  catch (const __gnu_test::positioning_error&)
     {
-      // stream should set badbit and rethrow facet_error.
-      VERIFY( stream.bad() );
-      VERIFY( (stream.rdstate() & ios_base::failbit) == 0 );
-      VERIFY( !stream.eof() );
+      VERIFY( stream.good() );
     }
-  catch (...) 
+  catch (...)
     {
       VERIFY( false );
     }
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/n3168.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/n3168.cc
new file mode 100644 (file)
index 0000000..12da0b1
--- /dev/null
@@ -0,0 +1,103 @@
+#include <ostream>
+#include <testsuite_hooks.h>
+#include <testsuite_io.h>
+
+// C++11 27.7.3.5 basic_ostream seek members [ostream.seeks]
+
+// Verify [ostream.seeks] functions use a sentry, as per N3168.
+
+void
+test01()
+{
+  // Check that the sentry sets failbit when seeking on a bad stream.
+  // The standard doesn't guarantee this, but it is true for libstdc++.
+
+  std::ostream os(0);
+  VERIFY( os.rdstate() == std::ios_base::badbit );
+
+  std::ostream::pos_type pos = std::ostream::pos_type();
+  os.seekp(pos);
+  VERIFY( os.rdstate() & std::ios_base::failbit );
+
+  os.clear();
+  std::ostream::off_type off(5);
+  os.seekp(off, std::ios_base::cur);
+  VERIFY( os.rdstate() & std::ios_base::failbit );
+
+  os.clear();
+  os.exceptions(std::ios_base::failbit);
+
+  try
+  {
+    os.clear();
+    os.seekp(pos);
+    VERIFY( false );
+  }
+  catch (const std::ios_base::failure&)
+  {
+    VERIFY( os.rdstate() & std::ios_base::failbit );
+  }
+  catch (...)
+  {
+    VERIFY( false );
+  }
+
+  try
+  {
+    os.clear();
+    os.seekp(off, std::ios_base::cur);
+    VERIFY( false );
+  }
+  catch (const std::ios_base::failure&)
+  {
+    VERIFY( os.rdstate() & std::ios_base::failbit );
+  }
+  catch (...)
+  {
+    VERIFY( false );
+  }
+}
+
+void
+test02()
+{
+  // Check that the sentry flushes a tied stream when seeking.
+
+  {
+    __gnu_test::sync_streambuf buf;
+    std::ostream os(&buf);
+
+    __gnu_test::sync_streambuf buf_tie;
+    std::ostream os_tie(&buf_tie);
+
+    os.tie(&os_tie);
+
+    std::ostream::pos_type pos = std::ostream::pos_type();
+    os.seekp(pos);
+
+    VERIFY( ! buf.sync_called() );
+    VERIFY( buf_tie.sync_called() );
+  }
+
+  {
+    __gnu_test::sync_streambuf buf;
+    std::ostream os(&buf);
+
+    __gnu_test::sync_streambuf buf_tie;
+    std::ostream os_tie(&buf_tie);
+
+    os.tie(&os_tie);
+
+    std::ostream::off_type off(0);
+    os.seekp(off, std::ios_base::cur);
+
+    VERIFY( ! buf.sync_called() );
+    VERIFY( buf_tie.sync_called() );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+}
index 499389b332bf759b2c9d3c277aa914bf0f3df983..2daa959532d9c6c8bb51b7bd004a4e662331ff74 100644 (file)
@@ -28,7 +28,6 @@ void test01()
 
   __gnu_test::fail_wstreambuf bib;
   wostream stream(&bib);
-  stream.exceptions(ios_base::badbit);
 
   wostream::pos_type pos = wostream::pos_type();
 
@@ -37,14 +36,11 @@ void test01()
       stream.seekp(pos);
       VERIFY( false );
     }
-  catch (const __gnu_test::positioning_error&) 
+  catch (const __gnu_test::positioning_error&)
     {
-      // stream should set badbit and rethrow facet_error.
-      VERIFY( stream.bad() );
-      VERIFY( (stream.rdstate() & ios_base::failbit) == 0 );
-      VERIFY( !stream.eof() );
+      VERIFY( stream.good() );
     }
-  catch (...) 
+  catch (...)
     {
       VERIFY( false );
     }
@@ -53,10 +49,9 @@ void test01()
 void test02()
 {
   using namespace std;
+
   __gnu_test::fail_wstreambuf bib;
   wostream stream(&bib);
-  stream.exceptions(ios_base::badbit);
 
   wostream::off_type off(5);
 
@@ -65,14 +60,11 @@ void test02()
       stream.seekp(off, ios_base::cur);
       VERIFY( false );
     }
-  catch (const __gnu_test::positioning_error&) 
+  catch (const __gnu_test::positioning_error&)
     {
-      // stream should set badbit and rethrow facet_error.
-      VERIFY( stream.bad() );
-      VERIFY( (stream.rdstate() & ios_base::failbit) == 0 );
-      VERIFY( !stream.eof() );
+      VERIFY( stream.good() );
     }
-  catch (...) 
+  catch (...)
     {
       VERIFY( false );
     }
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/n3168.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/n3168.cc
new file mode 100644 (file)
index 0000000..652f46f
--- /dev/null
@@ -0,0 +1,101 @@
+#include <ostream>
+#include <testsuite_hooks.h>
+#include <testsuite_io.h>
+
+// C++11 27.7.3.5 basic_ostream seek members [ostream.seeks]
+
+// Verify [ostream.seeks] functions use a sentry, as per N3168.
+
+void
+test01()
+{
+  // Check that the sentry sets failbit when seeking on a bad stream.
+  // The standard doesn't guarantee this, but it is true for libstdc++.
+
+  std::wostream os(0);
+  VERIFY( os.rdstate() == std::ios_base::badbit );
+
+  std::wostream::pos_type pos = std::wostream::pos_type();
+  os.seekp(pos);
+  VERIFY( os.rdstate() & std::ios_base::failbit );
+
+  os.clear();
+  std::wostream::off_type off(5);
+  os.seekp(off, std::ios_base::cur);
+  VERIFY( os.rdstate() & std::ios_base::failbit );
+
+  os.clear();
+  os.exceptions(std::ios_base::failbit);
+
+  try
+  {
+    os.clear();
+    os.seekp(pos);
+    VERIFY( false );
+  }
+  catch (const std::ios_base::failure&)
+  {
+    VERIFY( os.rdstate() & std::ios_base::failbit );
+  }
+  catch (...)
+  {
+    VERIFY( false );
+  }
+
+  try
+  {
+    os.clear();
+    os.seekp(off, std::ios_base::cur);
+    VERIFY( false );
+  }
+  catch (const std::ios_base::failure&)
+  {
+    VERIFY( os.rdstate() & std::ios_base::failbit );
+  }
+  catch (...)
+  {
+    VERIFY( false );
+  }
+}
+
+void
+test02()
+{
+  // Check that the sentry flushes a tied stream when seeking.
+
+  {
+    __gnu_test::sync_wstreambuf buf;
+    std::wostream os(&buf);
+
+    __gnu_test::sync_wstreambuf buf_tie;
+    std::wostream os_tie(&buf_tie);
+
+    os.tie(&os_tie);
+
+    std::wostream::pos_type pos = std::wostream::pos_type();
+    os.seekp(pos);
+
+    VERIFY( buf_tie.sync_called() );
+  }
+
+  {
+    __gnu_test::sync_wstreambuf buf;
+    std::wostream os(&buf);
+
+    __gnu_test::sync_wstreambuf buf_tie;
+    std::wostream os_tie(&buf_tie);
+
+    os.tie(&os_tie);
+
+    std::wostream::off_type off(0);
+    os.seekp(off, std::ios_base::cur);
+
+    VERIFY( buf_tie.sync_called() );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+}
index c6b7a8a58e0a6b2b691783430d1c45259f462f81..7ddddeccc65dd08380744292a1bd7d0568a74d1c 100644 (file)
@@ -28,21 +28,17 @@ void test01()
 {
   __gnu_test::fail_streambuf bib;
   ostream stream(&bib);
-  stream.exceptions(ios_base::badbit);
 
   try
     {
       stream.tellp();
       VERIFY( false );
     }
-  catch (const __gnu_test::positioning_error&) 
+  catch (const __gnu_test::positioning_error&)
     {
-      // stream should set badbit and rethrow facet_error.
-      VERIFY( stream.bad() );
-      VERIFY( (stream.rdstate() & ios_base::failbit) == 0 );
-      VERIFY( !stream.eof() );
+      VERIFY( stream.good() );
     }
-  catch (...) 
+  catch (...)
     {
       VERIFY(false);
     }
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/n3168.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/n3168.cc
new file mode 100644 (file)
index 0000000..8c2fe85
--- /dev/null
@@ -0,0 +1,64 @@
+#include <ostream>
+#include <testsuite_hooks.h>
+#include <testsuite_io.h>
+
+// C++11 27.7.3.5 basic_ostream seek members [ostream.seeks]
+
+// Verify [ostream.seeks] functions use a sentry, as per N3168.
+
+void
+test01()
+{
+  // Check that the sentry sets failbit when seeking on a bad stream.
+  // The standard doesn't guarantee this, but it is true for libstdc++.
+
+  std::ostream os(0);
+  VERIFY( os.rdstate() == std::ios_base::badbit );
+
+  os.tellp();
+  VERIFY( os.rdstate() & std::ios_base::failbit );
+
+  os.clear();
+
+  os.exceptions(std::ios_base::failbit);
+
+  try
+  {
+    os.clear();
+    os.tellp();
+    VERIFY( false );
+  }
+  catch (const std::ios_base::failure&)
+  {
+    VERIFY( os.rdstate() & std::ios_base::failbit );
+  }
+  catch (...)
+  {
+    VERIFY( false );
+  }
+}
+
+void
+test02()
+{
+  // Check that the sentry flushes a tied stream when seeking.
+
+  __gnu_test::sync_streambuf buf;
+  std::ostream os(&buf);
+
+  __gnu_test::sync_streambuf buf_tie;
+  std::ostream os_tie(&buf_tie);
+
+  os.tie(&os_tie);
+
+  os.tellp();
+
+  VERIFY( ! buf.sync_called() );
+  VERIFY( buf_tie.sync_called() );
+}
+
+int main()
+{
+  test01();
+  test02();
+}
index 6bdeeeb40284f81e76c418e238564c29f3182b5e..803c45abe1111cc506614ad5837794e18dc3b1db 100644 (file)
@@ -28,21 +28,17 @@ void test01()
 {
   __gnu_test::fail_wstreambuf bib;
   wostream stream(&bib);
-  stream.exceptions(ios_base::badbit);
 
   try
     {
       stream.tellp();
       VERIFY( false );
     }
-  catch (const __gnu_test::positioning_error&) 
+  catch (const __gnu_test::positioning_error&)
     {
-      // stream should set badbit and rethrow facet_error.
-      VERIFY( stream.bad() );
-      VERIFY( (stream.rdstate() & ios_base::failbit) == 0 );
-      VERIFY( !stream.eof() );
+      VERIFY( stream.good() );
     }
-  catch (...) 
+  catch (...)
     {
       VERIFY(false);
     }
diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/n3168.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/n3168.cc
new file mode 100644 (file)
index 0000000..887a9a7
--- /dev/null
@@ -0,0 +1,64 @@
+#include <ostream>
+#include <testsuite_hooks.h>
+#include <testsuite_io.h>
+
+// C++11 27.7.3.5 basic_ostream seek members [ostream.seeks]
+
+// Verify [ostream.seeks] functions use a sentry, as per N3168.
+
+void
+test01()
+{
+  // Check that the sentry sets failbit when seeking on a bad stream.
+  // The standard doesn't guarantee this, but it is true for libstdc++.
+
+  std::wostream os(0);
+  VERIFY( os.rdstate() == std::ios_base::badbit );
+
+  os.tellp();
+  VERIFY( os.rdstate() & std::ios_base::failbit );
+
+  os.clear();
+
+  os.exceptions(std::ios_base::failbit);
+
+  try
+  {
+    os.clear();
+    os.tellp();
+    VERIFY( false );
+  }
+  catch (const std::ios_base::failure&)
+  {
+    VERIFY( os.rdstate() & std::ios_base::failbit );
+  }
+  catch (...)
+  {
+    VERIFY( false );
+  }
+}
+
+void
+test02()
+{
+  // Check that the sentry flushes a tied stream when seeking.
+
+  __gnu_test::sync_wstreambuf buf;
+  std::wostream os(&buf);
+
+  __gnu_test::sync_wstreambuf buf_tie;
+  std::wostream os_tie(&buf_tie);
+
+  os.tie(&os_tie);
+
+  os.tellp();
+
+  VERIFY( ! buf.sync_called() );
+  VERIFY( buf_tie.sync_called() );
+}
+
+int main()
+{
+  test01();
+  test02();
+}