diff --git a/stl/inc/ostream b/stl/inc/ostream index df2dd3f6b4..970cd37b73 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -493,8 +493,10 @@ public: _TRY_BEGIN _Meta = _Traits::eq_int_type(_Traits::eof(), _Meta) ? _Strbuf->sgetc() : _Strbuf->snextc(); _CATCH_ALL - _Myios::setstate(ios_base::failbit); - _RERAISE; + // N4971 [ostream.inserters]/9: "If an exception was thrown + // while extracting a character, the function sets failbit in the error state, + // and if failbit is set in exceptions() the caught exception is rethrown." + _Myios::setstate(ios_base::failbit, _Myios::exceptions() == ios_base::failbit); _CATCH_END if (_Traits::eq_int_type(_Traits::eof(), _Meta)) { diff --git a/tests/std/tests/GH_001858_iostream_exception/test.cpp b/tests/std/tests/GH_001858_iostream_exception/test.cpp index 9b9bc47ef2..5991d5e131 100644 --- a/tests/std/tests/GH_001858_iostream_exception/test.cpp +++ b/tests/std/tests/GH_001858_iostream_exception/test.cpp @@ -36,6 +36,8 @@ struct test_exception {}; template class throwing_buffer : public basic_streambuf { public: + using typename basic_streambuf::int_type; + streampos seekoff(streamoff, ios_base::seekdir, ios_base::openmode = ios_base::in | ios_base::out) override { throw test_exception{}; } @@ -48,6 +50,10 @@ class throwing_buffer : public basic_streambuf { throw test_exception{}; } + int_type underflow() override { + throw test_exception{}; + } + basic_streambuf* to_buf() { return this; } @@ -229,6 +235,32 @@ void test_ostream_exceptions() { // Expected case } } + + { // operator<< (testing GH-4322 ": Exception from streambuf should be caught and not rethrown") + basic_ostream os(buffer.to_buf()); + + try { + os << &buffer; + } catch (const ios_base::failure&) { + assert(false); + } catch (const test_exception&) { + assert(false); + } + } + + { // operator<< rethrows the caught exception if failbit is set in exceptions() + basic_ostream os(buffer.to_buf()); + os.exceptions(ios_base::failbit); + + try { + os << &buffer; + assert(false); + } catch (const ios_base::failure&) { + assert(false); + } catch (const test_exception&) { + // Expected case + } + } } // Also test strengthened and mandatory exception specifications.