From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id 0E35A3898507; Fri, 25 Jun 2021 19:51:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0E35A3898507 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r12-1816] libstdc++: Fix exception handling in std::ostream seek functions X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: 7ab7fa1b51cb7227e051842bcb971b95c962327a X-Git-Newrev: 9b6c65c754f2b2a78f5353219ec62817064e0d24 Message-Id: <20210625195104.0E35A3898507@sourceware.org> Date: Fri, 25 Jun 2021 19:51:04 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 25 Jun 2021 19:51:04 -0000 https://gcc.gnu.org/g:9b6c65c754f2b2a78f5353219ec62817064e0d24 commit r12-1816-g9b6c65c754f2b2a78f5353219ec62817064e0d24 Author: Jonathan Wakely Date: Fri Jun 25 18:31:23 2021 +0100 libstdc++: Fix exception handling in std::ostream seek functions 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. Diff: --- libstdc++-v3/include/bits/ostream.tcc | 76 +++++---------- .../seekp/char/exceptions_badbit_throw.cc | 20 ++-- .../27_io/basic_ostream/seekp/char/n3168.cc | 103 +++++++++++++++++++++ .../seekp/wchar_t/exceptions_badbit_throw.cc | 22 ++--- .../27_io/basic_ostream/seekp/wchar_t/n3168.cc | 101 ++++++++++++++++++++ .../tellp/char/exceptions_badbit_throw.cc | 10 +- .../27_io/basic_ostream/tellp/char/n3168.cc | 64 +++++++++++++ .../tellp/wchar_t/exceptions_badbit_throw.cc | 10 +- .../27_io/basic_ostream/tellp/wchar_t/n3168.cc | 64 +++++++++++++ 9 files changed, 372 insertions(+), 98 deletions(-) diff --git a/libstdc++-v3/include/bits/ostream.tcc b/libstdc++-v3/include/bits/ostream.tcc index 76ca28561c1..20585f447ac 100644 --- a/libstdc++-v3/include/bits/ostream.tcc +++ b/libstdc++-v3/include/bits/ostream.tcc @@ -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; } diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/exceptions_badbit_throw.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/exceptions_badbit_throw.cc index a5a95fd872d..bc59578bfa1 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/exceptions_badbit_throw.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/exceptions_badbit_throw.cc @@ -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 index 00000000000..12da0b1f011 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/char/n3168.cc @@ -0,0 +1,103 @@ +#include +#include +#include + +// 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(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/exceptions_badbit_throw.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/exceptions_badbit_throw.cc index 499389b332b..2daa959532d 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/exceptions_badbit_throw.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/exceptions_badbit_throw.cc @@ -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 index 00000000000..652f46f615c --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/seekp/wchar_t/n3168.cc @@ -0,0 +1,101 @@ +#include +#include +#include + +// 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(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/exceptions_badbit_throw.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/exceptions_badbit_throw.cc index c6b7a8a58e0..7ddddeccc65 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/exceptions_badbit_throw.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/exceptions_badbit_throw.cc @@ -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 index 00000000000..8c2fe85db36 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/char/n3168.cc @@ -0,0 +1,64 @@ +#include +#include +#include + +// 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(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/exceptions_badbit_throw.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/exceptions_badbit_throw.cc index 6bdeeeb4028..803c45abe11 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/exceptions_badbit_throw.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/exceptions_badbit_throw.cc @@ -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 index 00000000000..887a9a773d1 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/tellp/wchar_t/n3168.cc @@ -0,0 +1,64 @@ +#include +#include +#include + +// 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(); +}