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.
This commit is contained in:
Jonathan Wakely 2021-06-25 18:31:23 +01:00
parent 7ab7fa1b51
commit 9b6c65c754
9 changed files with 372 additions and 98 deletions

View 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;
}
// 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;
}
@ -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;
}

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

View File

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

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

View File

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

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

View File

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

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

View File

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