On Mon, 27 Oct 2025 at 22:46 -0400, Patrick Palka wrote:
From: Jonathan Wakely <[email protected]>
Tested on x86_64-pc-linux-gnu. Patch originally written by Jonathan and
completed by me (mainly testsuite adjustments).
Thanks for taking on the task of figuring out what needing changing in
the tests. IIRC I made the changes to stringbuf and when tests failed
in unexpected ways I got confused and gave up.
I don't understand the stringbuf API enough to determine what the
correct value of egptr() - eback() (i.e. the get area size) should be in
the 26250.cc tests aftering calling overflow(). It's currently zero
after this patch, which I'm pretty sure is wrong?
[stringbuf.virtuals]/8 says: If ios_base::in is set in mode, the
function alters the read end pointer egptr() to point just past the new
write position.
In light of that I guess egptr() - eback() should still be 1 after this
patch then? That'd require making stringbuf::overflow() more
consistently set egptr() I believe.
As written, I think the "alters the read end pointer egptr()" wording
only applies in the case where overflow needs to make a new write
position available, i.e. needs to reallocate.
When we've been initialized with the SSO buffer the initial capacity
means that the single call to overflow in 26250.cc doesn't need to
reallocate. So I think it's correct that after your change, the size
of the get area isn't change by the overflow('x') call. Because it
doesn't reallocate.
But then what about for:
buf.sputn("01234567890abcdef", 16); // Exceed SSO buffer
buf.overflow('x');
VERIFY( buf.egptr() - buf.eback() == ? );
I would think the get area size should be 17? But other implementations
including us before this patch say 16, so I'm pretty confused.
Again, I think this depends on whether overflow('x') reallocates. If
the capacity after sputn means a write position is available, then my
reading of the spec says we don't change egptr().
But that doesn't really make sense to me. Surely the point of moving
the egptr is that a character has been written to the put area and
that character should now be available for reading, so the get area
should expand to include the just-written character. But that should
happen any time a character is written to the put area, not only when
reallocation was needed?
I'll do some experimenting and refresh my understanding of this
wording and what we do. But I think your patch is fine for trunk now,
with some comment changes requested below confusedconstructible_from
-- >8 --
LWG 2995 allows us to use the SSO std::string's short string buffer as
the initial put area for a stringbuf. This avoids a call to overflow for
the first char written to an ostringstream, because the put area
consists of 15 chars immediately after construction.
PR libstdc++/80676
libstdc++-v3/ChangeLog:
* include/std/sstream (basic_stringbuf::basic_stringbuf())
[_GLIBCXX_USE_CXX11_ABI]: Call _M_stringbuf_init to use SSO buffer.
(basic_stringbuf::basic_stringbuf(openmode))
[_GLIBCXX_USE_CXX11_ABI]: Likewise.
* testsuite/27_io/basic_stringbuf/cons/80676.cc: New.
* testsuite/27_io/basic_stringbuf/cons/char/1.cc: Adjust call to
check_pointers.
* testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc: Likewise.
* testsuite/27_io/basic_stringbuf/overflow/char/26250.cc
[_GLIBCXX_USE_CXX11_ABI]: Adjust get area size assert.
* testsuite/27_io/basic_stringbuf/overflow/wchar_t/26250.cc
[_GLIBCXX_USE_CXX11_ABI]: Likewise.
* testsuite/27_io/basic_stringbuf/sputc/char/9404-1.cc
[_GLIBCXX_USE_CXX11_ABI]: Don't expected overflow() to be
called.
* testsuite/27_io/basic_stringbuf/sputc/wchar_t/9404-1.cc
[_GLIBCXX_USE_CXX11_ABI]: Likewise.
* testsuite/27_io/basic_stringbuf/sputn/char/9404-2.cc
[_GLIBCXX_USE_CXX11_ABI]: Likewise.
* testsuite/util/testsuite_io.h (constraint_buf::check_pointers):
Add defaulted is_stringbuf bool parameter. Expect non-null
instead of null pointers when DR 2995 applies.
Co-authored-by: Patrick Palka <[email protected]>
---
libstdc++-v3/include/std/sstream | 12 +++++++--
.../27_io/basic_stringbuf/cons/80676.cc | 26 +++++++++++++++++++
.../27_io/basic_stringbuf/cons/char/1.cc | 2 +-
.../27_io/basic_stringbuf/cons/wchar_t/1.cc | 2 +-
.../basic_stringbuf/overflow/char/26250.cc | 4 +++
.../basic_stringbuf/overflow/wchar_t/26250.cc | 4 +++
.../basic_stringbuf/sputc/char/9404-1.cc | 4 +++
.../basic_stringbuf/sputc/wchar_t/9404-1.cc | 4 +++
.../basic_stringbuf/sputn/char/9404-2.cc | 4 +++
libstdc++-v3/testsuite/util/testsuite_io.h | 10 ++++++-
10 files changed, 67 insertions(+), 5 deletions(-)
create mode 100644 libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/80676.cc
diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream
index b1b41260ce3d..10fbaf00c084 100644
--- a/libstdc++-v3/include/std/sstream
+++ b/libstdc++-v3/include/std/sstream
@@ -128,7 +128,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
*/
basic_stringbuf()
: __streambuf_type(), _M_mode(ios_base::in | ios_base::out), _M_string()
- { }
+ {
+#if _GLIBCXX_USE_CXX11_ABI
+ _M_stringbuf_init(_M_mode);
+#endif
+ }
/**
* @brief Starts with an empty string buffer.
@@ -140,7 +144,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
explicit
basic_stringbuf(ios_base::openmode __mode)
: __streambuf_type(), _M_mode(__mode), _M_string()
- { }
+ {
+#if _GLIBCXX_USE_CXX11_ABI
+ _M_stringbuf_init(__mode);
+#endif
+ }
/**
* @brief Starts with an existing string buffer.
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/80676.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/80676.cc
new file mode 100644
index 000000000000..efdac03c366d
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/80676.cc
@@ -0,0 +1,26 @@
+// { dg-do run { target c++11 } }
+// { dg-require-effective-target cxx11_abi }
+
+// PR libstdc++/80676
+// [DR 2995] basic_stringbuf does not use initial capacity of SSO string
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+int counter;
+
+struct SB : std::stringbuf
+{
+ int_type overflow(int_type c) override
+ {
+ ++counter;
+ return std::stringbuf::overflow(c);
+ }
+};
+
+int main()
+{
+ SB sb;
+ sb.sputc('a');
+ VERIFY( counter == 0 );
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
index 78aca7730136..43f8bd1c933d 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/1.cc
@@ -27,7 +27,7 @@
void test01()
{
__gnu_test::constraint_stringbuf sbuf;
- VERIFY( sbuf.check_pointers() );
+ VERIFY( sbuf.check_pointers(/*is_stringbuf=*/true) );
}
void test02()
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
index 5d40df9fe395..aa833026aff4 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/1.cc
@@ -27,7 +27,7 @@
void test01()
{
__gnu_test::constraint_wstringbuf sbuf;
- VERIFY( sbuf.check_pointers() );
+ VERIFY( sbuf.check_pointers(/*is_stringbuf=*/true) );
}
void test02()
diff --git
a/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/char/26250.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/char/26250.cc
index 3cb3adea70b7..1b80857d2520 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/char/26250.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/char/26250.cc
@@ -45,7 +45,11 @@ void test01()
VERIFY( write_positions > 1 );
// 27.7.1.3, p8:
Could you please update these comments (and the ones on line 18) to
make it clear what "27.7.1.x" refers to, so:
// C++03 27.7.1.3 [lib.stringbuf.virtuals] p8:
and on line 18 fix the section number so it's not completely wrong!
// C++11 27.8.2.4 [stringbuf.virtuals] Overridden virtual functions
Or harmonize them to both refer to C++11, just as long as we don't
have naked subclause numbers which are no use without saying which
standard they come from.
+#if _GLIBCXX_USE_CXX11_ABI
+ VERIFY( buf.egptr() - buf.eback() == 0 );
+#else
VERIFY( buf.egptr() - buf.eback() == 1 );
+#endif
}
int main()
diff --git
a/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/wchar_t/26250.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/wchar_t/26250.cc
index be0bc14e5055..75de48bc84f1 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/wchar_t/26250.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/overflow/wchar_t/26250.cc
@@ -45,7 +45,11 @@ void test01()
VERIFY( write_positions > 1 );
// 27.7.1.3, p8:
+#if _GLIBCXX_USE_CXX11_ABI
+ VERIFY( buf.egptr() - buf.eback() == 0 );
+#else
VERIFY( buf.egptr() - buf.eback() == 1 );
+#endif
}
int main()
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/char/9404-1.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/char/9404-1.cc
index d5481eacfb3b..05d3bee8d499 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/char/9404-1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/char/9404-1.cc
@@ -53,7 +53,11 @@ void test04()
Derived_stringbuf dsbuf_01;
over_called = false;
dsbuf_01.sputc('i');
+#if _GLIBCXX_USE_CXX11_ABI
+ VERIFY( !over_called ); // DR 2995 lets us strengthen this
+#else
VERIFY( over_called );
+#endif
over_expected = dsbuf_01.pub_epptr() == dsbuf_01.pub_pptr();
over_called = false;
dsbuf_01.sputc('v');
diff --git
a/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/wchar_t/9404-1.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/wchar_t/9404-1.cc
index 48bcefc312e9..18f86d8f87e8 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/wchar_t/9404-1.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputc/wchar_t/9404-1.cc
@@ -53,7 +53,11 @@ void test04()
Derived_stringbuf dsbuf_01;
over_called = false;
dsbuf_01.sputc(L'i');
+#if _GLIBCXX_USE_CXX11_ABI
+ VERIFY( !over_called ); // DR 2995 lets us strengthen this
+#else
VERIFY( over_called );
+#endif
over_expected = dsbuf_01.pub_epptr() == dsbuf_01.pub_pptr();
over_called = false;
dsbuf_01.sputc(L'v');
diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputn/char/9404-2.cc
b/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputn/char/9404-2.cc
index bd0aa86e03cf..a7b06f825d6a 100644
--- a/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputn/char/9404-2.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/sputn/char/9404-2.cc
@@ -54,7 +54,11 @@ void test04()
Derived_stringbuf dsbuf_02;
over_called = false;
dsbuf_02.sputn("sonne's", 7);
+#if _GLIBCXX_USE_CXX11_ABI
+ VERIFY( !over_called ); // DR 2995 lets us strengthen this
+#else
VERIFY( over_called );
+#endif
over_expected = dsbuf_02.pub_epptr() == dsbuf_02.pub_pptr();
over_called = false;
dsbuf_02.sputn(" peak", 5);
diff --git a/libstdc++-v3/testsuite/util/testsuite_io.h
b/libstdc++-v3/testsuite/util/testsuite_io.h
index f4dca68117d6..797d11c905b8 100644
--- a/libstdc++-v3/testsuite/util/testsuite_io.h
+++ b/libstdc++-v3/testsuite/util/testsuite_io.h
@@ -68,7 +68,7 @@ namespace __gnu_test
}
bool
- check_pointers()
+ check_pointers(bool is_stringbuf = false)
{
bool one = this->eback() == 0;
bool two = this->gptr() == 0;
@@ -77,6 +77,14 @@ namespace __gnu_test
bool four = this->pbase() == 0;
bool five = this->pptr() == 0;
bool six = this->epptr() == 0;
+#if _GLIBCXX_USE_CXX11_ABI
+ if (is_stringbuf)
+ // DR 2995 made it implementation-defined whether the sequence
pointers
+ // (eback(), gptr(), egptr(), pbase(), pptr(), epptr()) are
initialized
+ // to null pointers. We initialize them to the underlying string's
+ // SSO buffer.
+ return !one && !two && !three && !four && !five && !six;
+#endif
return one && two && three && four && five && six;
}
};
--
2.51.1.549.g4e98b730f1