Skip to content

[libc++] Fixes istream::sync. #76467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 18, 2024
Merged

[libc++] Fixes istream::sync. #76467

merged 3 commits into from
Feb 18, 2024

Conversation

mordante
Copy link
Member

This fixes two issues.

The return value

Based on the wording

[istream.unformatted]/37
Effects: Behaves as an unformatted input function (as described above),
except that it does not count the number of characters extracted and
does not affect the value returned by subsequent calls to gcount().
After constructing a sentry object, if rdbuf() is a null pointer,
returns -1.

[istream.unformatted]/1
... It then creates an object of class sentry with the default argument
noskipws (second) argument true. If the sentry object returns true, when
converted to a value of type bool, the function endeavors to obtain the
requested input. ...

It could be argued the current behaviour is correct, however constructing a istream rdbuf() == nullptr creates a sentry that returns false; its state is always bad in this case.

As mentioned in the bug report, after this change the 3 major implementations behave the same.

The setting of the state

When pubsync returned -1 it updated the local __state variable and returned. This early return caused the state up the istream not to be updated to the new state.

Fixes: #51497
Fixes: #51499

@mordante mordante requested a review from a team as a code owner December 27, 2023 19:37
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 27, 2023
@llvmbot
Copy link
Member

llvmbot commented Dec 27, 2023

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

This fixes two issues.

The return value

Based on the wording

[istream.unformatted]/37
Effects: Behaves as an unformatted input function (as described above),
except that it does not count the number of characters extracted and
does not affect the value returned by subsequent calls to gcount().
After constructing a sentry object, if rdbuf() is a null pointer,
returns -1.

[istream.unformatted]/1
... It then creates an object of class sentry with the default argument
noskipws (second) argument true. If the sentry object returns true, when
converted to a value of type bool, the function endeavors to obtain the
requested input. ...

It could be argued the current behaviour is correct, however constructing a istream rdbuf() == nullptr creates a sentry that returns false; its state is always bad in this case.

As mentioned in the bug report, after this change the 3 major implementations behave the same.

The setting of the state

When pubsync returned -1 it updated the local __state variable and returned. This early return caused the state up the istream not to be updated to the new state.

Fixes: #51497
Fixes: #51499


Full diff: https://github.com/llvm/llvm-project/pull/76467.diff

2 Files Affected:

  • (modified) libcxx/include/istream (+5-4)
  • (modified) libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp (+69-1)
diff --git a/libcxx/include/istream b/libcxx/include/istream
index 1c7fb992dff429..d030197a19c98b 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -1009,17 +1009,18 @@ basic_istream<_CharT, _Traits>& basic_istream<_CharT, _Traits>::unget() {
 template <class _CharT, class _Traits>
 int basic_istream<_CharT, _Traits>::sync() {
   ios_base::iostate __state = ios_base::goodbit;
-  int __r                   = 0;
   sentry __sen(*this, true);
+  if (this->rdbuf() == nullptr)
+    return -1;
+
+  int __r = 0;
   if (__sen) {
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     try {
 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
-      if (this->rdbuf() == nullptr)
-        return -1;
       if (this->rdbuf()->pubsync() == -1) {
         __state |= ios_base::badbit;
-        return -1;
+        __r = -1;
       }
 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     } catch (...) {
diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
index ec1195d6b32bd9..8209cfd3108921 100644
--- a/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
@@ -48,6 +48,18 @@ struct testbuf
     }
 };
 
+template <class CharT>
+struct testbuf_pubsync_error
+    : public std::basic_streambuf<CharT>
+{
+public:
+
+    testbuf_pubsync_error() {}
+protected:
+    virtual int sync() { return -1; }
+};
+
+
 #ifndef TEST_HAS_NO_EXCEPTIONS
 struct testbuf_exception { };
 
@@ -85,21 +97,62 @@ struct throwing_testbuf
 
 int main(int, char**)
 {
+    {
+        std::istream is(nullptr);
+        assert(is.sync() == -1);
+    }
     {
         testbuf<char> sb(" 123456789");
         std::istream is(&sb);
         assert(is.sync() == 0);
         assert(sync_called == 1);
     }
+    {
+        testbuf_pubsync_error<char> sb;
+        std::istream is(&sb);
+        is.exceptions(std::ios_base::failbit | std::ios_base::eofbit);
+        assert(is.sync() == -1);
+        assert( is.bad());
+        assert(!is.eof());
+        assert( is.fail());
+    }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    {
+        std::wistream is(nullptr);
+        assert(is.sync() == -1);
+    }
     {
         testbuf<wchar_t> sb(L" 123456789");
         std::wistream is(&sb);
         assert(is.sync() == 0);
         assert(sync_called == 2);
     }
+    {
+        testbuf_pubsync_error<wchar_t> sb;
+        std::wistream is(&sb);
+        is.exceptions(std::ios_base::failbit | std::ios_base::eofbit);
+        assert(is.sync() == -1);
+        assert( is.bad());
+        assert(!is.eof());
+        assert( is.fail());
+    }
 #endif
 #ifndef TEST_HAS_NO_EXCEPTIONS
+    {
+        testbuf_pubsync_error<char> sb;
+        std::istream is(&sb);
+        is.exceptions(std::ios_base::badbit);
+        bool threw = false;
+        try {
+            is.sync();
+        } catch (std::ios_base::failure const&) {
+            threw = true;
+        }
+        assert( is.bad());
+        assert(!is.eof());
+        assert( is.fail());
+        assert(threw);
+    }
     {
         throwing_testbuf<char> sb(" 123456789");
         std::basic_istream<char> is(&sb);
@@ -116,6 +169,21 @@ int main(int, char**)
         assert(threw);
     }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
+    {
+        testbuf_pubsync_error<wchar_t> sb;
+        std::wistream is(&sb);
+        is.exceptions(std::ios_base::badbit);
+        bool threw = false;
+        try {
+            is.sync();
+        } catch (std::ios_base::failure const&) {
+            threw = true;
+        }
+        assert( is.bad());
+        assert(!is.eof());
+        assert( is.fail());
+        assert(threw);
+    }
     {
         throwing_testbuf<wchar_t> sb(L" 123456789");
         std::basic_istream<wchar_t> is(&sb);
@@ -131,7 +199,7 @@ int main(int, char**)
         assert( is.fail());
         assert(threw);
     }
-#endif
+#endif // TEST_HAS_NO_WIDE_CHARACTERS
 #endif // TEST_HAS_NO_EXCEPTIONS
 
     return 0;

Copy link

github-actions bot commented Dec 27, 2023

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff b33b91a21788d439f49d6db4e7224c20f740f1a7 12ee3dde68f528a59da24e773144c0a537da8eec -- libcxx/include/istream libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
View the diff from clang-format here.
diff --git a/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp b/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
index 4fa58c0abf..0b89440a70 100644
--- a/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/input.streams/istream.unformatted/sync.pass.cpp
@@ -55,17 +55,14 @@ protected:
 };
 
 template <class CharT>
-struct testbuf_pubsync_error
-    : public std::basic_streambuf<CharT>
-{
+struct testbuf_pubsync_error : public std::basic_streambuf<CharT> {
 public:
+  testbuf_pubsync_error() {}
 
-    testbuf_pubsync_error() {}
 protected:
-    virtual int sync() { return -1; }
+  virtual int sync() { return -1; }
 };
 
-
 #ifndef TEST_HAS_NO_EXCEPTIONS
 struct testbuf_exception { };
 
@@ -103,10 +100,10 @@ protected:
 
 int main(int, char**)
 {
-    {
-        std::istream is(nullptr);
-        assert(is.sync() == -1);
-    }
+  {
+    std::istream is(nullptr);
+    assert(is.sync() == -1);
+  }
     {
         testbuf<char> sb(" 123456789");
         std::istream is(&sb);
@@ -114,18 +111,18 @@ int main(int, char**)
         assert(sync_called == 1);
     }
     {
-        testbuf_pubsync_error<char> sb;
-        std::istream is(&sb);
-        is.exceptions(std::ios_base::failbit | std::ios_base::eofbit);
-        assert(is.sync() == -1);
-        assert( is.bad());
-        assert(!is.eof());
-        assert( is.fail());
+      testbuf_pubsync_error<char> sb;
+      std::istream is(&sb);
+      is.exceptions(std::ios_base::failbit | std::ios_base::eofbit);
+      assert(is.sync() == -1);
+      assert(is.bad());
+      assert(!is.eof());
+      assert(is.fail());
     }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
     {
-        std::wistream is(nullptr);
-        assert(is.sync() == -1);
+      std::wistream is(nullptr);
+      assert(is.sync() == -1);
     }
     {
         testbuf<wchar_t> sb(L" 123456789");
@@ -134,30 +131,30 @@ int main(int, char**)
         assert(sync_called == 2);
     }
     {
-        testbuf_pubsync_error<wchar_t> sb;
-        std::wistream is(&sb);
-        is.exceptions(std::ios_base::failbit | std::ios_base::eofbit);
-        assert(is.sync() == -1);
-        assert( is.bad());
-        assert(!is.eof());
-        assert( is.fail());
+      testbuf_pubsync_error<wchar_t> sb;
+      std::wistream is(&sb);
+      is.exceptions(std::ios_base::failbit | std::ios_base::eofbit);
+      assert(is.sync() == -1);
+      assert(is.bad());
+      assert(!is.eof());
+      assert(is.fail());
     }
 #endif
 #ifndef TEST_HAS_NO_EXCEPTIONS
     {
-        testbuf_pubsync_error<char> sb;
-        std::istream is(&sb);
-        is.exceptions(std::ios_base::badbit);
-        bool threw = false;
-        try {
-            is.sync();
-        } catch (std::ios_base::failure const&) {
-            threw = true;
-        }
-        assert( is.bad());
-        assert(!is.eof());
-        assert( is.fail());
-        assert(threw);
+      testbuf_pubsync_error<char> sb;
+      std::istream is(&sb);
+      is.exceptions(std::ios_base::badbit);
+      bool threw = false;
+      try {
+        is.sync();
+      } catch (std::ios_base::failure const&) {
+        threw = true;
+      }
+      assert(is.bad());
+      assert(!is.eof());
+      assert(is.fail());
+      assert(threw);
     }
     {
         throwing_testbuf<char> sb(" 123456789");
@@ -176,19 +173,19 @@ int main(int, char**)
     }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
     {
-        testbuf_pubsync_error<wchar_t> sb;
-        std::wistream is(&sb);
-        is.exceptions(std::ios_base::badbit);
-        bool threw = false;
-        try {
-            is.sync();
-        } catch (std::ios_base::failure const&) {
-            threw = true;
-        }
-        assert( is.bad());
-        assert(!is.eof());
-        assert( is.fail());
-        assert(threw);
+      testbuf_pubsync_error<wchar_t> sb;
+      std::wistream is(&sb);
+      is.exceptions(std::ios_base::badbit);
+      bool threw = false;
+      try {
+        is.sync();
+      } catch (std::ios_base::failure const&) {
+        threw = true;
+      }
+      assert(is.bad());
+      assert(!is.eof());
+      assert(is.fail());
+      assert(threw);
     }
     {
         throwing_testbuf<wchar_t> sb(L" 123456789");
@@ -205,7 +202,7 @@ int main(int, char**)
         assert( is.fail());
         assert(threw);
     }
-#endif // TEST_HAS_NO_WIDE_CHARACTERS
+#  endif // TEST_HAS_NO_WIDE_CHARACTERS
 #endif // TEST_HAS_NO_EXCEPTIONS
 
     return 0;

This fixes two issues.

The return value
----------------

Based on the wording

[istream.unformatted]/37
  Effects: Behaves as an unformatted input function (as described above),
  except that it does not count the number of characters extracted and
  does not affect the value returned by subsequent calls to gcount().
  After constructing a sentry object, if rdbuf() is a null pointer,
  returns -1.

[istream.unformatted]/1
  ... It then creates an object of class sentry with the default argument
  noskipws (second) argument true. If the sentry object returns true, when
  converted to a value of type bool, the function endeavors to obtain the
  requested input. ...

It could be argued the current behaviour is correct, however constructing
a istream rdbuf() == nullptr creates a sentry that returns false; its
state is always bad in this case.

As mentioned in the bug report, after this change the 3 major implementations
behave the same.

The setting of the state
------------------------

When pubsync returned -1 it updated the local __state variable and
returned. This early return caused the state up the istream not to be
updated to the new state.

Fixes: llvm#51497
Fixes: llvm#51499
…ream.unformatted/sync.pass.cpp

Co-authored-by: Louis Dionne <[email protected]>
@mordante mordante merged commit 54daf6a into llvm:main Feb 18, 2024
@mordante mordante deleted the fixes_istream_sync branch February 18, 2024 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

sync() not setting rdstate on certain errors Calling sync() when rdbuf() is a null pointer should return -1.
3 participants