[libcxx-commits] [libcxx] [libc++] Optimize standard streams with sync_with_stdio(false) (PR #209161)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 21 12:55:48 PDT 2026


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209161

>From 059ec6cec9cb06a0be51d521c1945870a3ad2b0d Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH 1/2] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        |  15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  |  17 +++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |   5 +-
 libcxx/src/ios.cpp                            |   7 --
 libcxx/src/iostream.cpp                       | 102 ++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                |  37 +++++++
 .../clog.sync_with_stdio.sh.cpp               |  29 +++++
 .../cout.sync_with_stdio.sh.cpp               |  27 +++++
 .../wcin.sync_with_stdio.sh.cpp               |  44 ++++++++
 .../wclog.sync_with_stdio.sh.cpp              |  36 +++++++
 .../wcout.sync_with_stdio.sh.cpp              |  34 ++++++
 17 files changed, 340 insertions(+), 35 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..567c23717ed60 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,23 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
+
+  Fixes #21566
+
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}

>From ebf915fac915353eca504da6839c2a78b8a9fbfb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 14:16:12 -0400
Subject: [PATCH 2/2] Add more test coverage for sync_with_stdio stream
 switching

Extend the tests for the sync_with_stdio(false) optimization:

- ensure that the optimization is actually in effect by checking the
  type of the underlying buffer.

- verify that toggling the synchronization mode preserves per-stream state.

- cerr/wcerr are deliberately not switched to a filebuf, so check they keep
  producing correct output.

- check that C stdio and cout output interleave in order in synchronized  mode.

- read a large input to force the buffered filebuf to refill (underflow) several
  times in unsynchronized mode, and check EOF detection.
---
 .../sync_with_stdio.buffer_type.pass.cpp      |  70 +++++++++++
 .../cerr.sync_with_stdio.sh.cpp               |  38 ++++++
 .../narrow.stream.objects/cin.readmany.sh.cpp |  56 +++++++++
 .../cout.interleave_with_stdio.sh.cpp         |  34 ++++++
 .../sync_with_stdio.state.pass.cpp            | 109 ++++++++++++++++++
 .../wcerr.sync_with_stdio.sh.cpp              |  35 ++++++
 .../wide.stream.objects/wcin.readmany.sh.cpp  |  58 ++++++++++
 .../sync_with_stdio.pass.cpp                  |   2 +
 8 files changed, 402 insertions(+)
 create mode 100644 libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp

diff --git a/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
new file mode 100644
index 0000000000000..789ef630a2a2e
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// static bool ios_base::sync_with_stdio(bool sync = true);
+
+// This is a libc++ implementation detail: when sync_with_stdio(false) is used,
+// libc++ swaps the buffers of cin/cout/clog (and the wide equivalents) for a
+// basic_filebuf to avoid going through the C stdio layer. This test ensures
+// that this optimization is applied by inspecting the type of the underlying
+// buffer.
+
+// The optimization is only applied when we have a filesystem, since fstream
+// is not present otherwise. Also, the test itself requires RTTI.
+// UNSUPPORTED: no-filesystem, no-rtti
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  // By default the streams are synchronized, so they are not backed by a filebuf.
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning synchronization off switches cin/cout/clog to a filebuf, but leaves
+  // cerr untouched (it stays synchronized so it keeps flushing eagerly).
+  std::ios_base::sync_with_stdio(false);
+  assert(dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning it back on restores the original buffers.
+  std::ios_base::sync_with_stdio(true);
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..9f40e1ae04e67
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream cerr;
+
+// Unlike cin/cout/clog, std::cerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it keeps producing correctly-ordered output when
+// interleaved with the C stdio functions even after sync_with_stdio(false).
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  // interleave output with C stdio functions
+  std::fputs("1", stderr);
+  std::cerr << "2";
+  std::fprintf(stderr, "%d", 3);
+  std::cerr << 4;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
new file mode 100644
index 0000000000000..08f417b9ba451
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::cin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::cin.eof());
+  assert(!std::cin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..8497801f1778c
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// In synchronized mode, output written through std::cout and through the C stdio
+// functions must appear in the order in which it was written.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 123456 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  std::fputs("1", stdout);
+  std::cout << "2";
+  std::fprintf(stdout, "%d", 3);
+  std::cout << 4;
+  std::fputs("5", stdout);
+  std::cout << 6;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
new file mode 100644
index 0000000000000..e6f9ef3002396
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// Changing the synchronization mode with std::ios_base::sync_with_stdio must
+// preserve the per-stream state of the standard stream objects.
+
+#include <cassert>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int dummy       = 0;
+  const int index = std::ios_base::xalloc();
+
+  std::cin.iword(index)  = 11;
+  std::cout.iword(index) = 22;
+  std::cerr.iword(index) = 33;
+  std::clog.iword(index) = 44;
+  std::cout.pword(index) = &dummy;
+
+  std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::cout.precision(7);
+  std::cout.fill('*');
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::wcin.iword(index)  = 11;
+  std::wcout.iword(index) = 22;
+  std::wcerr.iword(index) = 33;
+  std::wclog.iword(index) = 44;
+  std::wcout.pword(index) = &dummy;
+
+  std::wcout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::wcout.precision(7);
+  std::wcout.fill(L'*');
+#endif
+
+  // Switch to the unsynchronized mode. This swaps the underlying buffers, but the
+  // stream objects and all their state must be preserved.
+  std::ios_base::sync_with_stdio(false);
+
+  assert(std::cin.iword(index) == 11);
+  assert(std::cout.iword(index) == 22);
+  assert(std::cerr.iword(index) == 33);
+  assert(std::clog.iword(index) == 44);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cin.good());
+  assert(std::cout.good());
+  assert(std::cin.rdbuf());
+  assert(std::cout.rdbuf());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcin.iword(index) == 11);
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcerr.iword(index) == 33);
+  assert(std::wclog.iword(index) == 44);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcin.good());
+  assert(std::wcout.good());
+  assert(std::wcin.rdbuf());
+  assert(std::wcout.rdbuf());
+#endif
+
+  // Switch back to synchronized mode and check that the state is still there.
+  std::ios_base::sync_with_stdio(true);
+
+  assert(std::cout.iword(index) == 22);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cout.good());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcout.good());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..2a65be4863bf5
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wostream wcerr;
+
+// Unlike wcin/wcout/wclog, std::wcerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it produces correct output after sync_with_stdio(false).
+// We can't check interleaving with C stdio functions here since C stdio uses narrow characters.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  std::wcerr << L"12" << 34;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
new file mode 100644
index 0000000000000..57113e21535e3
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wistream wcin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::wcin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::wcin.eof());
+  assert(!std::wcin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 0ec24b77560e5..3051f0dc3b1cb 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,6 +17,8 @@
 
 int main(int, char**)
 {
+    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+
     assert( std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(true));



More information about the libcxx-commits mailing list