[llvm-branch-commits] [libcxx] [libc++][format] Implements P3107R5 in <print>. (PR #130500)

Mark de Wever via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Mar 17 10:04:25 PDT 2025


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/130500

>From c132aa555a38efde9b04c2a3f435ba598778c28d Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 30 Mar 2024 17:35:56 +0100
Subject: [PATCH 1/3] [libc++][format] Implements P3107R5 in <print>.

The followup paper P3235R3 which is voted in as a DR changes the names
foo_locking to foo_buffered. These changes have been applied in this
patch.

Before
-------------------------------------------------------
Benchmark             Time             CPU   Iterations
-------------------------------------------------------
printf             71.3 ns         71.3 ns      9525175
print_string        226 ns          226 ns      3105850
print_stack         232 ns          232 ns      3026498
print_direct        530 ns          530 ns      1318447

After
-------------------------------------------------------
Benchmark             Time             CPU   Iterations
-------------------------------------------------------
printf             70.6 ns         70.6 ns      9789585
print_string        222 ns          222 ns      3147678
print_stack         227 ns          227 ns      3084767
print_direct        474 ns          474 ns      1472786

Note: The performance of libc++'s std::print is still extemely slow
compared to printf. Based on P3107R5 std::print should outperform
printf. The main culprit is the call to isatty, which is resolved
after implementing
LWG4044  Confusing requirements for std::print on POSIX platforms

Implements
- P3107R5 - Permit an efficient implementation of ``std::print``

Implements parts of
- P3235R3 std::print more types faster with less memory

Fixes: #105435
---
 libcxx/docs/ReleaseNotes/21.rst               |   1 +
 libcxx/include/__format/buffer.h              |   3 +
 libcxx/include/print                          | 270 +++++++++++++++++-
 libcxx/modules/std/print.inc                  |   1 +
 .../test/libcxx/system_reserved_names.gen.py  |   5 +
 .../test/libcxx/transitive_includes/cxx03.csv |   5 +
 .../test/libcxx/transitive_includes/cxx11.csv |   5 +
 .../test/libcxx/transitive_includes/cxx14.csv |   5 +
 .../test/libcxx/transitive_includes/cxx17.csv |   5 +
 .../test/libcxx/transitive_includes/cxx23.csv |   5 +-
 .../test/libcxx/transitive_includes/cxx26.csv |   4 +
 11 files changed, 296 insertions(+), 13 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index e7cfa625a132c..a1f30b26c5a1d 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -40,6 +40,7 @@ Implemented Papers
 
 - N4258: Cleaning-up noexcept in the Library (`Github <https://github.com/llvm/llvm-project/issues/99937>`__)
 - P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__)
+- P3107R5 - Permit an efficient implementation of ``std::print`` (`Github <https://github.com/llvm/llvm-project/issues/105435>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index c88b7f3222010..d6e4ddc840e2d 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/copy_n.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/for_each.h>
 #include <__algorithm/max.h>
 #include <__algorithm/min.h>
 #include <__algorithm/ranges_copy.h>
@@ -34,11 +35,13 @@
 #include <__memory/construct_at.h>
 #include <__memory/destroy.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__system_error/system_error.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/conditional.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <stdexcept>
+#include <stdio.h> // Uses the POSIX/Windows unlocked stream I/O
 #include <string_view>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/print b/libcxx/include/print
index 1794d6014efcd..f6d03edfbd4bc 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -27,9 +27,11 @@ namespace std {
 
   void vprint_unicode(string_view fmt, format_args args);
   void vprint_unicode(FILE* stream, string_view fmt, format_args args);
+  void vprint_unicode_buffered(FILE* stream, string_view fmt, format_args args);
 
   void vprint_nonunicode(string_view fmt, format_args args);
   void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
+  void vprint_nonunicode_buffered(FILE* stream, string_view fmt, format_args args);
 }
 */
 
@@ -41,6 +43,7 @@ namespace std {
 #  include <__config>
 #  include <__system_error/throw_system_error.h>
 #  include <__utility/forward.h>
+#  include <__utility/move.h>
 #  include <cerrno>
 #  include <cstdio>
 #  include <format>
@@ -52,6 +55,9 @@ namespace std {
 #    pragma GCC system_header
 #  endif
 
+_LIBCPP_PUSH_MACROS
+#  include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 #  ifdef _LIBCPP_WIN32API
@@ -213,6 +219,122 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
 #    endif
 }
 
+_LIBCPP_HIDE_FROM_ABI inline void __flockfile(FILE* __stream) {
+#    if defined(_LIBCPP_WIN32API)
+  ::_lock_file(__stream);
+#    elif __has_include(<unistd.h>)
+  ::flockfile(__stream);
+#    else
+#      error "Provide a way to do unlocked stream I/O operations"
+#    endif
+}
+_LIBCPP_HIDE_FROM_ABI inline void __funlockfile(FILE* __stream) {
+#    if defined(_LIBCPP_WIN32API)
+  ::_unlock_file(__stream);
+#    elif __has_include(<unistd.h>)
+  ::funlockfile(__stream);
+#    else
+#      error "Provide a way to do unlocked stream I/O operations"
+#    endif
+}
+
+_LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked(FILE* __stream) {
+#    if defined(_LIBCPP_WIN32API)
+  return ::_fflush_nolock(__stream);
+#    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
+  // There is no fflush_unlocked on these systems.
+  // This funcion is not part of POSIX.
+  return ::fflush(__stream);
+#    elif __has_include(<unistd.h>)
+  return ::fflush_unlocked(__stream);
+#    else
+#      error "Provide a way to do unlocked stream I/O operations"
+#    endif
+}
+
+// Note for our use-case __size is always 1;
+_LIBCPP_HIDE_FROM_ABI inline size_t
+__fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t __n, FILE* __stream) {
+#    if defined(_LIBCPP_WIN32API)
+  return ::_fwrite_nolock(__buffer, __size, __n, __stream);
+#    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
+  // There is no fwrite_unlocked on these systems.
+  // This funcion is not part of POSIX.
+  auto __b = static_cast<const char*>(__buffer);
+  for (size_t __i = 0; __i < __n; ++__i, ++__b)
+    // Unqualified since  putc_unlocked is a macro on AIX.
+    if (putc_unlocked(*__b, __stream) == EOF)
+      return __i;
+  return __n;
+#    elif __has_include(<unistd.h>)
+  return ::fwrite_unlocked(__buffer, __size, __n, __stream);
+#    else
+#      error "Provide a way to do unlocked stream I/O operations"
+#    endif
+}
+
+// This "buffer" is not a typical buffer but an adaptor for FILE*
+//
+// This adaptor locks the file stream, allowing it to use unlocked I/O.
+// This is used by the *_buffered functions in <print>. The print functions have
+// no wchar_t support so char is hard-coded. Since the underlaying I/O functions
+// encode narrow or wide in their name this avoids some `if constexpr` branches.
+//
+// The underlying functions for unlocked I/O are not in the C Standard, and
+// their names differ between POSIX and Windows, therefore the functions are
+// wrapped in this class.
+class __file_stream_buffer : public __format::__output_buffer<char> {
+public:
+  using value_type = char;
+
+  __file_stream_buffer(const __file_stream_buffer&)           = delete;
+  __file_stream_buffer operator=(const __file_stream_buffer&) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer(FILE* __stream)
+      : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr}, __stream_(__stream) {
+    __print::__flockfile(__stream_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer() { __print::__funlockfile(__stream_); }
+
+  // In order to ensure all data is written this function needs to be called.
+  //
+  // The class wraps C based APIs that never throw. However the Standard
+  // requires exceptions to be throw when a write operation fails. Therefore
+  // this function should be called before the class is destroyed.
+  _LIBCPP_HIDE_FROM_ABI void __write_internal_buffer() && { __write_buffer(); }
+
+private:
+  FILE* __stream_;
+
+  // This class uses a fixed size buffer and appends the elements in
+  // __buffer_size chunks. An alternative would be to use an allocating buffer
+  // and append the output in a single write operation. Benchmarking showed no
+  // performance difference.
+  static constexpr size_t __buffer_size = 256;
+  char __small_buffer_[__buffer_size];
+
+  _LIBCPP_HIDE_FROM_ABI void __write_buffer() {
+    size_t __n    = this->__size();
+    size_t __size = __print::__fwrite_unlocked(__small_buffer_, 1, __n, __stream_);
+    if (__size < __n) {
+      if (std::feof(__stream_))
+        std::__throw_system_error(EIO, "EOF while writing the formatted output");
+      std::__throw_system_error(std::ferror(__stream_), "failed to write formatted output");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void __prepare_write() {
+    __write_buffer();
+    this->__buffer_flushed();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void
+  __prepare_write(__output_buffer<char>& __buffer, [[maybe_unused]] size_t __size_hint) {
+    static_cast<__file_stream_buffer&>(__buffer).__prepare_write();
+  }
+};
+
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void
 __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
@@ -229,6 +351,26 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
   }
 }
 
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered(
+    __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
+  std::__format::__vformat_to(basic_format_parse_context{__fmt, __args.__size()},
+                              std::__format_context_create(__buffer.__make_output_iterator(), __args));
+  if (__write_nl)
+    __buffer.push_back('\n');
+}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void
+__vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
+  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+  __print::__file_stream_buffer __buffer(__stream);
+
+  __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
+
+  std::move(__buffer).__write_internal_buffer();
+}
+
 #    if _LIBCPP_HAS_UNICODE
 
 // Note these helper functions are mainly used to aid testing.
@@ -246,10 +388,27 @@ __vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bo
   __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
 }
 
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix(
+    FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+  __print::__file_stream_buffer __buffer(__stream);
+
+  // TODO PRINT Should flush errors throw too?
+  if (__is_terminal)
+    __print::__fflush_unlocked(__stream);
+
+  __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
+
+  std::move(__buffer).__write_internal_buffer();
+}
 #      if _LIBCPP_HAS_WIDE_CHARACTERS
+
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void
 __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+
   if (!__is_terminal)
     return __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
 
@@ -284,6 +443,49 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
                              "__write_to_windows_console is not available.");
 #        endif
 }
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows(
+    FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+
+  if (!__is_terminal)
+    return __print::__vprint_nonunicode_buffered(__stream, __fmt, __args, __write_nl);
+
+  [[maybe_unused]] __print::__file_stream_buffer __b(__stream);
+
+  // TODO PRINT Should flush errors throw too?
+  __print::__fflush_unlocked(__stream);
+
+  string __str = std::vformat(__fmt, __args);
+  // UTF-16 uses the same number or less code units than UTF-8.
+  // However the size of the code unit is 16 bits instead of 8 bits.
+  //
+  // The buffer uses the worst-case estimate and should never resize.
+  // However when the string is large this could lead to OOM. Using a
+  // smaller size might work, but since the buffer uses a grow factor
+  // the final size might be larger when the estimate is wrong.
+  //
+  // TODO PRINT profile and improve the speed of this code.
+  __format::__retarget_buffer<wchar_t> __buffer{__str.size()};
+  __unicode::__transcode(__str.begin(), __str.end(), __buffer.__make_output_iterator());
+  if (__write_nl)
+    __buffer.push_back(L'\n');
+
+  [[maybe_unused]] wstring_view __view = __buffer.__view();
+
+  // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
+  // the behavior in the test. This is not part of the public API.
+#        ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
+  _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
+#        elif defined(_LIBCPP_WIN32API)
+  std::__write_to_windows_console(__stream, __view);
+#        else
+  std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
+                             "__write_to_windows_console is not available.");
+#        endif
+}
+
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -324,6 +526,23 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
 #      endif
 }
 
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered(
+    [[maybe_unused]] FILE* __stream,
+    [[maybe_unused]] string_view __fmt,
+    [[maybe_unused]] format_args __args,
+    [[maybe_unused]] bool __write_nl) {
+  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+
+#      ifndef _LIBCPP_WIN32API
+  __print::__vprint_unicode_buffered_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+#      elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+  __print::__vprint_unicode_buffered_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+#      else
+#        error "Windows builds with wchar_t disabled are not supported."
+#      endif
+}
+
 #    endif // _LIBCPP_HAS_UNICODE
 
 } // namespace __print
@@ -331,13 +550,23 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
 template <class... _Args>
 _LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
 #    if _LIBCPP_HAS_UNICODE
-  if constexpr (__print::__use_unicode_execution_charset)
-    __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
-  else
-    __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+  constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
 #    else  // _LIBCPP_HAS_UNICODE
-  __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+  constexpr bool __use_unicode = false;
 #    endif // _LIBCPP_HAS_UNICODE
+  constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
+
+  if constexpr (__use_unicode) {
+    if constexpr (__locksafe)
+      __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
+    else
+      __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+  } else {
+    if constexpr (__locksafe)
+      __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
+    else
+      __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+  }
 }
 
 template <class... _Args>
@@ -348,16 +577,26 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
 template <class... _Args>
 _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
 #    if _LIBCPP_HAS_UNICODE
+  constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
+#    else  // _LIBCPP_HAS_UNICODE
+  constexpr bool __use_unicode = false;
+#    endif // _LIBCPP_HAS_UNICODE
+  constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
+
   // Note the wording in the Standard is inefficient. The output of
   // std::format is a std::string which is then copied. This solution
   // just appends a newline at the end of the output.
-  if constexpr (__print::__use_unicode_execution_charset)
-    __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-  else
-    __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-#    else  // _LIBCPP_HAS_UNICODE
-  __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-#    endif // _LIBCPP_HAS_UNICODE
+  if constexpr (__use_unicode) {
+    if constexpr (__locksafe)
+      __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
+    else
+      __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+  } else {
+    if constexpr (__locksafe)
+      __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
+    else
+      __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+  }
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -381,6 +620,11 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __f
   __print::__vprint_unicode(__stream, __fmt, __args, false);
 }
 
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered(FILE* __stream, string_view __fmt, format_args __args) {
+  __print::__vprint_unicode_buffered(__stream, __fmt, __args, false);
+}
+
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(string_view __fmt, format_args __args) {
   std::vprint_unicode(stdout, __fmt, __args);
@@ -402,6 +646,8 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(string_view __fmt, format_ar
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_PRINT
diff --git a/libcxx/modules/std/print.inc b/libcxx/modules/std/print.inc
index 5354025ca8bd8..9c6d8b20a22d8 100644
--- a/libcxx/modules/std/print.inc
+++ b/libcxx/modules/std/print.inc
@@ -16,6 +16,7 @@ export namespace std {
   using std::vprint_nonunicode;
 #  if _LIBCPP_HAS_UNICODE
   using std::vprint_unicode;
+  using std::vprint_unicode_buffered;
 #  endif // _LIBCPP_HAS_UNICODE
 #endif   // _LIBCPP_STD_VER >= 23
 } // namespace std
diff --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py
index f01126249c881..304c803b76c3d 100644
--- a/libcxx/test/libcxx/system_reserved_names.gen.py
+++ b/libcxx/test/libcxx/system_reserved_names.gen.py
@@ -119,6 +119,11 @@
 #define __acquire SYSTEM_RESERVED_NAME
 #define __release SYSTEM_RESERVED_NAME
 
+// Android and FreeBSD use this for __attribute__((__unused__))
+#if !defined(__FreeBSD__)  && !defined(__ANDROID__)
+#define __unused SYSTEM_RESERVED_NAME
+#endif
+
 // These names are not reserved, so the user can macro-define them.
 // These are intended to find improperly _Uglified template parameters.
 #define A SYSTEM_RESERVED_NAME
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index ec5db90597d92..7d52c9fe13594 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2024,6 +2028,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index ec5db90597d92..7d52c9fe13594 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2024,6 +2028,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 95024df0590b8..951a785cf077b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2064,6 +2068,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index a3518f7f62ecb..7742c23b796f7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2077,6 +2081,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 17972b8453743..167c79130bfbf 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -556,7 +556,6 @@ istream ios
 istream iosfwd
 istream limits
 istream locale
-
 istream ratio
 istream stdexcept
 istream streambuf
@@ -765,6 +764,7 @@ queue deque
 queue initializer_list
 queue iosfwd
 queue limits
+queue optional
 queue stdexcept
 queue string
 queue string_view
@@ -831,6 +831,7 @@ regex deque
 regex initializer_list
 regex iosfwd
 regex limits
+regex optional
 regex stdexcept
 regex string
 regex string_view
@@ -1075,6 +1076,7 @@ thread iosfwd
 thread istream
 thread limits
 thread locale
+thread optional
 thread ratio
 thread sstream
 thread stdexcept
@@ -1146,6 +1148,7 @@ vector cwctype
 vector initializer_list
 vector iosfwd
 vector limits
+vector optional
 vector stdexcept
 vector string
 vector string_view
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 00ab78e61a457..2bc61974a2ad8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -763,6 +763,7 @@ queue deque
 queue initializer_list
 queue iosfwd
 queue limits
+queue optional
 queue stdexcept
 queue string
 queue string_view
@@ -829,6 +830,7 @@ regex deque
 regex initializer_list
 regex iosfwd
 regex limits
+regex optional
 regex stdexcept
 regex string
 regex string_view
@@ -1073,6 +1075,7 @@ thread iosfwd
 thread istream
 thread limits
 thread locale
+thread optional
 thread ratio
 thread sstream
 thread stdexcept
@@ -1144,6 +1147,7 @@ vector cwctype
 vector initializer_list
 vector iosfwd
 vector limits
+vector optional
 vector stdexcept
 vector string
 vector string_view

>From 4fcce8dc4c58ccc092087d89c4ee73c326165373 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Thu, 13 Mar 2025 18:44:10 +0100
Subject: [PATCH 2/3] Cleanups and improvements.

---
 libcxx/include/__format/buffer.h              |   3 -
 libcxx/include/__ostream/print.h              |   4 +-
 libcxx/include/print                          | 257 +++++++-----------
 libcxx/modules/std/print.inc                  |   1 +
 .../print.fun/vprint_unicode_posix.pass.cpp   |  13 +-
 .../print.fun/vprint_unicode_windows.pass.cpp |  49 ++--
 .../test/libcxx/transitive_includes/cxx23.csv |   4 -
 .../test/libcxx/transitive_includes/cxx26.csv |   4 -
 .../vprint_nonunicode_buffered.file.pass.cpp  | 149 ++++++++++
 .../vprint_unicode_buffered.file.pass.cpp     | 156 +++++++++++
 10 files changed, 454 insertions(+), 186 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp

diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index d6e4ddc840e2d..c88b7f3222010 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -12,7 +12,6 @@
 
 #include <__algorithm/copy_n.h>
 #include <__algorithm/fill_n.h>
-#include <__algorithm/for_each.h>
 #include <__algorithm/max.h>
 #include <__algorithm/min.h>
 #include <__algorithm/ranges_copy.h>
@@ -35,13 +34,11 @@
 #include <__memory/construct_at.h>
 #include <__memory/destroy.h>
 #include <__memory/uninitialized_algorithms.h>
-#include <__system_error/system_error.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/conditional.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <stdexcept>
-#include <stdio.h> // Uses the POSIX/Windows unlocked stream I/O
 #include <string_view>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index a4d7506cffc48..b5ce81ea5a10a 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -111,9 +111,9 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
     ostream::sentry __s(__os);
     if (__s) {
 #        ifndef _LIBCPP_WIN32API
-      __print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true);
+      __print::__vprint_unicode_posix<__print::__lock_policy::__stdio>(__file, __fmt, __args, __write_nl, true);
 #        elif _LIBCPP_HAS_WIDE_CHARACTERS
-    __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true);
+      __print::__vprint_unicode_windows<__print::__lock_policy::__stdio>(__file, __fmt, __args, __write_nl, true);
 #        else
 #          error "Windows builds with wchar_t disabled are not supported."
 #        endif
diff --git a/libcxx/include/print b/libcxx/include/print
index f6d03edfbd4bc..3372b08bbcad1 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -258,11 +258,11 @@ __fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t _
 #    if defined(_LIBCPP_WIN32API)
   return ::_fwrite_nolock(__buffer, __size, __n, __stream);
 #    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
-  // There is no fwrite_unlocked on these systems.
-  // This funcion is not part of POSIX.
+  // The function fwrite_unlocked is not part of POSIX and not available on
+  // these systems.
   auto __b = static_cast<const char*>(__buffer);
   for (size_t __i = 0; __i < __n; ++__i, ++__b)
-    // Unqualified since  putc_unlocked is a macro on AIX.
+    // Unqualified since putc_unlocked is a macro on AIX.
     if (putc_unlocked(*__b, __stream) == EOF)
       return __i;
   return __n;
@@ -273,17 +273,37 @@ __fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t _
 #    endif
 }
 
-// This "buffer" is not a typical buffer but an adaptor for FILE*
-//
-// This adaptor locks the file stream, allowing it to use unlocked I/O.
-// This is used by the *_buffered functions in <print>. The print functions have
-// no wchar_t support so char is hard-coded. Since the underlaying I/O functions
-// encode narrow or wide in their name this avoids some `if constexpr` branches.
+enum class __lock_policy {
+  // The locking is done manually, which allows calling the *_unlocked functions.
+  //
+  // These are used in the *_buffered overloads.
+  __manual,
+  // The locking is done inside the stdio library.
+  __stdio,
+
+};
+
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI int __fflush(FILE* __stream) {
+  if constexpr (__policy == __lock_policy::__manual)
+    return __print ::__fflush_unlocked(__stream);
+  else if constexpr (__policy == __lock_policy::__stdio)
+    return std::fflush(__stream);
+  else
+#    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+    sizeof(__lock_policy) == 0
+#    else
+    static_assert(false, "Unsupported policy");
+#    endif
+}
+
+// This "buffer" is not a typical buffer but an buffered adaptor for FILE*
 //
-// The underlying functions for unlocked I/O are not in the C Standard, and
-// their names differ between POSIX and Windows, therefore the functions are
-// wrapped in this class.
-class __file_stream_buffer : public __format::__output_buffer<char> {
+// Based on the __lock_policy it will leave the locking to stdio or manually
+// locks the stream and then uses unlocked stdio functions. This policy makes
+// the print functions "foo" and "foo_buffered" to be templated.
+template <__lock_policy __policy>
+class _LIBCPP_TEMPLATE_VIS __file_stream_buffer : public __format::__output_buffer<char> {
 public:
   using value_type = char;
 
@@ -292,10 +312,26 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer(FILE* __stream)
       : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr}, __stream_(__stream) {
-    __print::__flockfile(__stream_);
+    if constexpr (__policy == __lock_policy::__manual)
+      __print::__flockfile(__stream_);
+    else if constexpr (__policy != __lock_policy::__stdio)
+#    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+      sizeof(__lock_policy) == 0
+#    else
+      static_assert(false, "Unsupported policy");
+#    endif
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer() { __print::__funlockfile(__stream_); }
+  _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer() {
+    if constexpr (__policy == __lock_policy::__manual)
+      __print::__funlockfile(__stream_);
+    else if constexpr (__policy != __lock_policy::__stdio)
+#    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+      sizeof(__lock_policy) == 0
+#    else
+      static_assert(false, "Unsupported policy");
+#    endif
+  }
 
   // In order to ensure all data is written this function needs to be called.
   //
@@ -316,7 +352,19 @@ private:
 
   _LIBCPP_HIDE_FROM_ABI void __write_buffer() {
     size_t __n    = this->__size();
-    size_t __size = __print::__fwrite_unlocked(__small_buffer_, 1, __n, __stream_);
+    size_t __size = [&] {
+      if constexpr (__policy == __lock_policy::__manual)
+        return __print::__fwrite_unlocked(__small_buffer_, 1, __n, __stream_);
+      else if constexpr (__policy == __lock_policy::__stdio)
+        return std::fwrite(__small_buffer_, 1, __n, __stream_);
+      else
+#    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+        sizeof(__lock_policy) == 0
+#    else
+        static_assert(false, "Unsupported policy");
+#    endif
+    }();
+
     if (__size < __n) {
       if (std::feof(__stream_))
         std::__throw_system_error(EIO, "EOF while writing the formatted output");
@@ -331,42 +379,19 @@ private:
 
   _LIBCPP_HIDE_FROM_ABI static void
   __prepare_write(__output_buffer<char>& __buffer, [[maybe_unused]] size_t __size_hint) {
-    static_cast<__file_stream_buffer&>(__buffer).__prepare_write();
+    static_cast<__file_stream_buffer<__policy>&>(__buffer).__prepare_write();
   }
 };
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
-__vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
   _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-  string __str = std::vformat(__fmt, __args);
-  if (__write_nl)
-    __str.push_back('\n');
-
-  size_t __size = fwrite(__str.data(), 1, __str.size(), __stream);
-  if (__size < __str.size()) {
-    if (std::feof(__stream))
-      std::__throw_system_error(EIO, "EOF while writing the formatted output");
-    std::__throw_system_error(std::ferror(__stream), "failed to write formatted output");
-  }
-}
+  __print::__file_stream_buffer<__policy> __buffer(__stream);
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered(
-    __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
   std::__format::__vformat_to(basic_format_parse_context{__fmt, __args.__size()},
                               std::__format_context_create(__buffer.__make_output_iterator(), __args));
   if (__write_nl)
     __buffer.push_back('\n');
-}
-
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
-__vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
-  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-  __print::__file_stream_buffer __buffer(__stream);
-
-  __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
 
   std::move(__buffer).__write_internal_buffer();
 }
@@ -378,84 +403,33 @@ __vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __ar
 // terminal when the output is redirected. Typically during testing the
 // output is redirected to be able to capture it. This makes it hard to
 // test this code path.
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
-__vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
-  // TODO PRINT Should flush errors throw too?
-  if (__is_terminal)
-    std::fflush(__stream);
-
-  __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
-}
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix(
-    FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void
+__vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
   _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-  __print::__file_stream_buffer __buffer(__stream);
 
   // TODO PRINT Should flush errors throw too?
   if (__is_terminal)
-    __print::__fflush_unlocked(__stream);
-
-  __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
+    __print::__fflush<__policy>(__stream);
 
-  std::move(__buffer).__write_internal_buffer();
+  __print::__vprint_nonunicode<__policy>(__stream, __fmt, __args, __write_nl);
 }
+
 #      if _LIBCPP_HAS_WIDE_CHARACTERS
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void
 __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
   _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
 
   if (!__is_terminal)
-    return __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
-
-  // TODO PRINT Should flush errors throw too?
-  std::fflush(__stream);
-
-  string __str = std::vformat(__fmt, __args);
-  // UTF-16 uses the same number or less code units than UTF-8.
-  // However the size of the code unit is 16 bits instead of 8 bits.
-  //
-  // The buffer uses the worst-case estimate and should never resize.
-  // However when the string is large this could lead to OOM. Using a
-  // smaller size might work, but since the buffer uses a grow factor
-  // the final size might be larger when the estimate is wrong.
-  //
-  // TODO PRINT profile and improve the speed of this code.
-  __format::__retarget_buffer<wchar_t> __buffer{__str.size()};
-  __unicode::__transcode(__str.begin(), __str.end(), __buffer.__make_output_iterator());
-  if (__write_nl)
-    __buffer.push_back(L'\n');
-
-  [[maybe_unused]] wstring_view __view = __buffer.__view();
-
-  // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
-  // the behavior in the test. This is not part of the public API.
-#        ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
-  _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-#        elif defined(_LIBCPP_WIN32API)
-  std::__write_to_windows_console(__stream, __view);
-#        else
-  std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
-                             "__write_to_windows_console is not available.");
-#        endif
-}
-
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows(
-    FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
-  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-
-  if (!__is_terminal)
-    return __print::__vprint_nonunicode_buffered(__stream, __fmt, __args, __write_nl);
+    return __print::__vprint_nonunicode<__policy>(__stream, __fmt, __args, __write_nl);
 
-  [[maybe_unused]] __print::__file_stream_buffer __b(__stream);
+  [[maybe_unused]] __print::__file_stream_buffer<__policy> __b(__stream);
 
   // TODO PRINT Should flush errors throw too?
-  __print::__fflush_unlocked(__stream);
+  __print::__fflush<__policy>(__stream);
 
   string __str = std::vformat(__fmt, __args);
   // UTF-16 uses the same number or less code units than UTF-8.
@@ -488,8 +462,8 @@ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows(
 
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void
 __vprint_unicode([[maybe_unused]] FILE* __stream,
                  [[maybe_unused]] string_view __fmt,
                  [[maybe_unused]] format_args __args,
@@ -518,31 +492,13 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
   // Windows there is a different API. This API requires transcoding.
 
 #      ifndef _LIBCPP_WIN32API
-  __print::__vprint_unicode_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+  __print::__vprint_unicode_posix<__policy>(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
 #      elif _LIBCPP_HAS_WIDE_CHARACTERS
-  __print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+  __print::__vprint_unicode_windows<__policy>(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
 #      else
 #        error "Windows builds with wchar_t disabled are not supported."
 #      endif
 }
-
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered(
-    [[maybe_unused]] FILE* __stream,
-    [[maybe_unused]] string_view __fmt,
-    [[maybe_unused]] format_args __args,
-    [[maybe_unused]] bool __write_nl) {
-  _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-
-#      ifndef _LIBCPP_WIN32API
-  __print::__vprint_unicode_buffered_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
-#      elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
-  __print::__vprint_unicode_buffered_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
-#      else
-#        error "Windows builds with wchar_t disabled are not supported."
-#      endif
-}
-
 #    endif // _LIBCPP_HAS_UNICODE
 
 } // namespace __print
@@ -556,17 +512,13 @@ _LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt,
 #    endif // _LIBCPP_HAS_UNICODE
   constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
 
-  if constexpr (__use_unicode) {
-    if constexpr (__locksafe)
-      __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
-    else
-      __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
-  } else {
-    if constexpr (__locksafe)
-      __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
-    else
-      __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
-  }
+  using enum __print::__lock_policy;
+  if constexpr (__use_unicode)
+    __print::__vprint_unicode<__locksafe ? __manual : __stdio>(
+        __stream, __fmt.get(), std::make_format_args(__args...), false);
+  else
+    __print::__vprint_nonunicode<__locksafe ? __manual : __stdio>(
+        __stream, __fmt.get(), std::make_format_args(__args...), false);
 }
 
 template <class... _Args>
@@ -586,17 +538,13 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt
   // Note the wording in the Standard is inefficient. The output of
   // std::format is a std::string which is then copied. This solution
   // just appends a newline at the end of the output.
-  if constexpr (__use_unicode) {
-    if constexpr (__locksafe)
-      __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
-    else
-      __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-  } else {
-    if constexpr (__locksafe)
-      __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
-    else
-      __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-  }
+  using enum __print::__lock_policy;
+  if constexpr (__use_unicode)
+    __print::__vprint_unicode<__locksafe ? __manual : __stdio>(
+        __stream, __fmt.get(), std::make_format_args(__args...), true);
+  else
+    __print::__vprint_nonunicode<__locksafe ? __manual : __stdio>(
+        __stream, __fmt.get(), std::make_format_args(__args...), true);
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -617,12 +565,12 @@ _LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __a
 #    if _LIBCPP_HAS_UNICODE
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __fmt, format_args __args) {
-  __print::__vprint_unicode(__stream, __fmt, __args, false);
+  __print::__vprint_unicode<__print::__lock_policy::__stdio>(__stream, __fmt, __args, false);
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered(FILE* __stream, string_view __fmt, format_args __args) {
-  __print::__vprint_unicode_buffered(__stream, __fmt, __args, false);
+  __print::__vprint_unicode<__print::__lock_policy::__manual>(__stream, __fmt, __args, false);
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -634,7 +582,12 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(string_view __fmt, format_args
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args) {
-  __print::__vprint_nonunicode(__stream, __fmt, __args, false);
+  __print::__vprint_nonunicode< __print::__lock_policy::__stdio >(__stream, __fmt, __args, false);
+}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __args) {
+  __print::__vprint_nonunicode<__print::__lock_policy::__manual>(__stream, __fmt, __args, false);
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/modules/std/print.inc b/libcxx/modules/std/print.inc
index 9c6d8b20a22d8..123bce409a06a 100644
--- a/libcxx/modules/std/print.inc
+++ b/libcxx/modules/std/print.inc
@@ -14,6 +14,7 @@ export namespace std {
   using std::println;
 
   using std::vprint_nonunicode;
+  using ::std::vprint_nonunicode_buffered;
 #  if _LIBCPP_HAS_UNICODE
   using std::vprint_unicode;
   using std::vprint_unicode_buffered;
diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
index b89d02ba99425..db92811f803fa 100644
--- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
@@ -21,6 +21,7 @@
 // <print>
 
 // Tests the implementation of
+//   template <__print::__lock_policy __policy>
 //   void __print::__vprint_unicode_posix(FILE* __stream, string_view __fmt,
 //                                        format_args __args, bool __write_nl,
 //                                        bool __is_terminal);
@@ -39,7 +40,8 @@
 
 #include "test_macros.h"
 
-int main(int, char**) {
+template <std::__print::__lock_policy policy>
+static void test() {
   std::array<char, 100> buffer;
   std::ranges::fill(buffer, '*');
 
@@ -55,7 +57,7 @@ int main(int, char**) {
 #endif
 
   // Test writing to a "non-terminal" stream does not flush.
-  std::__print::__vprint_unicode_posix(file, " world", std::make_format_args(), false, false);
+  std::__print::__vprint_unicode_posix<policy>(file, " world", std::make_format_args(), false, false);
   assert(std::ftell(file) == 11);
 #if defined(TEST_HAS_GLIBC) &&                                                                                         \
     !(__has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer))
@@ -63,7 +65,7 @@ int main(int, char**) {
 #endif
 
   // Test writing to a "terminal" stream flushes before writing.
-  std::__print::__vprint_unicode_posix(file, "!", std::make_format_args(), false, true);
+  std::__print::__vprint_unicode_posix<policy>(file, "!", std::make_format_args(), false, true);
   assert(std::ftell(file) == 12);
   assert(std::string_view(buffer.data(), buffer.data() + 11) == "Hello world");
 #if defined(TEST_HAS_GLIBC)
@@ -74,6 +76,11 @@ int main(int, char**) {
   // Test everything is written when closing the stream.
   std::fclose(file);
   assert(std::string_view(buffer.data(), buffer.data() + 12) == "Hello world!");
+}
+
+int main(int, char**) {
+  test<std::__print::__lock_policy::__manual>();
+  test<std::__print::__lock_policy::__stdio>();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
index bcd1d05a3aeeb..8028d7c04b85b 100644
--- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
@@ -20,6 +20,7 @@
 // <print>
 
 // Tests the implementation of
+//   template <__print::__lock_policy __policy>
 //   void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt,
 //                                          format_args __args, bool __write_nl,
 //                                          bool __is_terminal);
@@ -47,8 +48,8 @@ TEST_GCC_DIAGNOSTIC_IGNORED("-Wuse-after-free")
 
 #define SV(S) MAKE_STRING_VIEW(wchar_t, S)
 
-bool calling               = false;
-std::wstring_view expected = L" world";
+bool calling = true;
+std::wstring_view expected;
 
 void write_to_console(FILE*, std::wstring_view data) {
   assert(calling);
@@ -58,29 +59,35 @@ void write_to_console(FILE*, std::wstring_view data) {
 scoped_test_env env;
 std::string filename = env.create_file("output.txt");
 
+template <std::__print::__lock_policy policy>
 static void test_basics() {
   FILE* file = std::fopen(filename.c_str(), "wb");
   assert(file);
 
+  calling  = false;
+  expected = L" world";
+
   // Test writing to a "non-terminal" stream does not call WriteConsoleW.
-  std::__print::__vprint_unicode_windows(file, "Hello", std::make_format_args(), false, false);
+  std::__print::__vprint_unicode_windows<policy>(file, "Hello", std::make_format_args(), false, false);
   assert(std::ftell(file) == 5);
 
   // It's not possible to reliably test whether writing to a "terminal" stream
   // flushes before writing. Testing flushing a closed stream worked on some
   // platforms, but was unreliable.
   calling = true;
-  std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false, true);
+  std::__print::__vprint_unicode_windows<policy>(file, " world", std::make_format_args(), false, true);
+  std::fclose(file);
 }
 
 // When the output is a file the data is written as-is.
 // When the output is a "terminal" invalid UTF-8 input is flagged.
+template <std::__print::__lock_policy policy>
 static void test(std::wstring_view output, std::string_view input) {
   // *** File ***
   FILE* file = std::fopen(filename.c_str(), "wb");
   assert(file);
 
-  std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, false);
+  std::__print::__vprint_unicode_windows<policy>(file, input, std::make_format_args(), false, false);
   assert(std::ftell(file) == static_cast<long>(input.size()));
   std::fclose(file);
 
@@ -95,12 +102,13 @@ static void test(std::wstring_view output, std::string_view input) {
 
   // *** Terminal ***
   expected = output;
-  std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true);
+  std::__print::__vprint_unicode_windows<policy>(file, input, std::make_format_args(), false, true);
 }
 
+template <std::__print::__lock_policy policy>
 static void test() {
   // *** Test valid UTF-8 ***
-#define TEST(S) test(SV(S), S)
+#define TEST(S) test<policy>(SV(S), S)
   TEST("hello world");
 
   // copied from benchmarks/std_format_spec_string_unicode.bench.cpp
@@ -112,24 +120,29 @@ static void test() {
 #undef TEST
 
   // *** Test invalid utf-8 ***
-  test(SV("\ufffd"), "\xc3");
-  test(SV("\ufffd("), "\xc3\x28");
+  test<policy>(SV("\ufffd"), "\xc3");
+  test<policy>(SV("\ufffd("), "\xc3\x28");
 
   // surrogate range
-  test(SV("\ufffd"), "\xed\xa0\x80"); // U+D800
-  test(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF
-  test(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00
-  test(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF
+  test<policy>(SV("\ufffd"), "\xed\xa0\x80"); // U+D800
+  test<policy>(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF
+  test<policy>(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00
+  test<policy>(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF
 
   // beyond valid values
-  test(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
-  test(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF
+  test<policy>(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
+  test<policy>(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF
 
   // Validates  http://unicode.org/review/pr-121.html option 3.
-  test(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62");
+  test<policy>(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62");
 }
 
 int main(int, char**) {
-  test_basics();
-  test();
+  test_basics<std::__print::__lock_policy::__manual>();
+  test<std::__print::__lock_policy::__manual>();
+
+  test_basics<std::__print::__lock_policy::__stdio>();
+  test<std::__print::__lock_policy::__stdio>();
+
+  return 0;
 }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 167c79130bfbf..a26aeeebef75e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -764,7 +764,6 @@ queue deque
 queue initializer_list
 queue iosfwd
 queue limits
-queue optional
 queue stdexcept
 queue string
 queue string_view
@@ -831,7 +830,6 @@ regex deque
 regex initializer_list
 regex iosfwd
 regex limits
-regex optional
 regex stdexcept
 regex string
 regex string_view
@@ -1076,7 +1074,6 @@ thread iosfwd
 thread istream
 thread limits
 thread locale
-thread optional
 thread ratio
 thread sstream
 thread stdexcept
@@ -1148,7 +1145,6 @@ vector cwctype
 vector initializer_list
 vector iosfwd
 vector limits
-vector optional
 vector stdexcept
 vector string
 vector string_view
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 2bc61974a2ad8..00ab78e61a457 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -763,7 +763,6 @@ queue deque
 queue initializer_list
 queue iosfwd
 queue limits
-queue optional
 queue stdexcept
 queue string
 queue string_view
@@ -830,7 +829,6 @@ regex deque
 regex initializer_list
 regex iosfwd
 regex limits
-regex optional
 regex stdexcept
 regex string
 regex string_view
@@ -1075,7 +1073,6 @@ thread iosfwd
 thread istream
 thread limits
 thread locale
-thread optional
 thread ratio
 thread sstream
 thread stdexcept
@@ -1147,7 +1144,6 @@ vector cwctype
 vector initializer_list
 vector iosfwd
 vector limits
-vector optional
 vector stdexcept
 vector string
 vector string_view
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp
new file mode 100644
index 0000000000000..a5a35bf8bedb1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-filesystem
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Enable again
+// https://reviews.llvm.org/D150044
+// https://lab.llvm.org/buildbot/#/builders/237/builds/3578
+// UNSUPPORTED: asan, hwasan, msan
+
+// XFAIL: availability-fp_to_chars-missing
+
+// The error exception has no system error string.
+// XFAIL: LIBCXX-ANDROID-FIXME
+
+// <print>
+
+// void vprint_nonunicode_buffered(FILE* stream, string_view fmt, format_args args);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <print>
+#include <string_view>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "print_tests.h"
+#include "test_macros.h"
+
+scoped_test_env env;
+std::string filename = env.create_file("output.txt");
+
+auto test_file = []<class... Args>(std::string_view expected, std::string_view fmt, Args&&... args) {
+  FILE* file = fopen(filename.c_str(), "wb");
+  assert(file);
+
+  std::vprint_nonunicode_buffered(file, fmt, std::make_format_args(args...));
+  std::fclose(file);
+
+  std::ifstream stream{filename.c_str(), std::ios_base::in | std::ios_base::binary};
+  std::string out(std::istreambuf_iterator<char>{stream}, {});
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>([[maybe_unused]] std::string_view what,
+                                        [[maybe_unused]] std::string_view fmt,
+                                        [[maybe_unused]] Args&&... args) {
+  FILE* file = fopen(filename.c_str(), "wb");
+  assert(file);
+
+  TEST_VALIDATE_EXCEPTION(
+      std::format_error,
+      [&]([[maybe_unused]] const std::format_error& e) {
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED(
+                "\nFormat string   ", fmt, "\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      std::vprint_nonunicode_buffered(file, fmt, std::make_format_args(args...)));
+
+  fclose(file);
+};
+
+// Glibc fails writing to a wide stream.
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+static void test_wide_stream() {
+  FILE* file = fopen(filename.c_str(), "wb");
+  assert(file);
+
+  int mode = std::fwide(file, 1);
+  assert(mode > 0);
+
+  TEST_VALIDATE_EXCEPTION(
+      std::system_error,
+      [&]([[maybe_unused]] const std::system_error& e) {
+        [[maybe_unused]] std::string_view what{"failed to write formatted output"};
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      std::vprint_nonunicode_buffered(file, "hello", std::make_format_args()));
+}
+#endif // defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+
+static void test_read_only() {
+  FILE* file = fopen(filename.c_str(), "r");
+  assert(file);
+
+  TEST_VALIDATE_EXCEPTION(
+      std::system_error,
+      [&]([[maybe_unused]] const std::system_error& e) {
+        [[maybe_unused]] std::string_view what{
+            "failed to write formatted output: " TEST_IF_AIX("Broken pipe", "Operation not permitted")};
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      std::vprint_nonunicode_buffered(file, "hello", std::make_format_args()));
+}
+
+static void test_new_line() {
+  // Text does newline translation.
+  {
+    FILE* file = fopen(filename.c_str(), "w");
+    assert(file);
+
+    std::vprint_nonunicode_buffered(file, "\n", std::make_format_args());
+#ifndef _WIN32
+    assert(std::ftell(file) == 1);
+#else
+    assert(std::ftell(file) == 2);
+#endif
+  }
+  // Binary no newline translation.
+  {
+    FILE* file = fopen(filename.c_str(), "wb");
+    assert(file);
+
+    std::vprint_nonunicode_buffered(file, "\n", std::make_format_args());
+    assert(std::ftell(file) == 1);
+  }
+}
+
+int main(int, char**) {
+  print_tests(test_file, test_exception);
+
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  test_wide_stream();
+#endif
+  test_read_only();
+  test_new_line();
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp
new file mode 100644
index 0000000000000..23d77b5079a08
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-filesystem
+// UNSUPPORTED: libcpp-has-no-unicode
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Enable again
+// https://reviews.llvm.org/D150044
+// https://lab.llvm.org/buildbot/#/builders/237/builds/3578
+// UNSUPPORTED: asan, hwasan, msan
+
+// XFAIL: availability-fp_to_chars-missing
+
+// The error exception has no system error string.
+// XFAIL: LIBCXX-ANDROID-FIXME
+
+// <print>
+
+// void vprint_unicode_buffered(FILE* stream, string_view fmt, format_args args);
+
+// In the library when the stdout is redirected to a file it is no
+// longer considered a terminal and the special terminal handling is no
+// longer executed. There are tests in
+//   libcxx/test/libcxx/input.output/iostream.format/print.fun/
+// to validate that behaviour
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <print>
+#include <string_view>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "print_tests.h"
+#include "test_macros.h"
+
+scoped_test_env env;
+std::string filename = env.create_file("output.txt");
+
+auto test_file = []<class... Args>(std::string_view expected, std::string_view fmt, Args&&... args) {
+  FILE* file = fopen(filename.c_str(), "wb");
+  assert(file);
+
+  std::vprint_unicode_buffered(file, fmt, std::make_format_args(args...));
+  std::fclose(file);
+
+  std::ifstream stream{filename.c_str(), std::ios_base::in | std::ios_base::binary};
+  std::string out(std::istreambuf_iterator<char>{stream}, {});
+  TEST_REQUIRE(out == expected,
+               TEST_WRITE_CONCATENATED(
+                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>([[maybe_unused]] std::string_view what,
+                                        [[maybe_unused]] std::string_view fmt,
+                                        [[maybe_unused]] Args&&... args) {
+  FILE* file = fopen(filename.c_str(), "wb");
+  assert(file);
+
+  TEST_VALIDATE_EXCEPTION(
+      std::format_error,
+      [&]([[maybe_unused]] const std::format_error& e) {
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED(
+                "\nFormat string   ", fmt, "\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      std::vprint_unicode_buffered(file, fmt, std::make_format_args(args...)));
+
+  fclose(file);
+};
+
+// Glibc fails writing to a wide stream.
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+static void test_wide_stream() {
+  FILE* file = fopen(filename.c_str(), "wb");
+  assert(file);
+
+  int mode = std::fwide(file, 1);
+  assert(mode > 0);
+
+  TEST_VALIDATE_EXCEPTION(
+      std::system_error,
+      [&]([[maybe_unused]] const std::system_error& e) {
+        [[maybe_unused]] std::string_view what{"failed to write formatted output"};
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      std::vprint_unicode_buffered(file, "hello", std::make_format_args()));
+}
+#endif // defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+
+static void test_read_only() {
+  FILE* file = fopen(filename.c_str(), "r");
+  assert(file);
+
+  TEST_VALIDATE_EXCEPTION(
+      std::system_error,
+      [&]([[maybe_unused]] const std::system_error& e) {
+        [[maybe_unused]] std::string_view what{
+            "failed to write formatted output: " TEST_IF_AIX("Broken pipe", "Operation not permitted")};
+        TEST_LIBCPP_REQUIRE(
+            e.what() == what,
+            TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception   ", e.what(), '\n'));
+      },
+      std::vprint_unicode_buffered(file, "hello", std::make_format_args()));
+}
+
+static void test_new_line() {
+  // Text does newline translation.
+  {
+    FILE* file = fopen(filename.c_str(), "w");
+    assert(file);
+
+    std::vprint_unicode_buffered(file, "\n", std::make_format_args());
+#ifndef _WIN32
+    assert(std::ftell(file) == 1);
+#else
+    assert(std::ftell(file) == 2);
+#endif
+  }
+  // Binary no newline translation.
+  {
+    FILE* file = fopen(filename.c_str(), "wb");
+    assert(file);
+
+    std::vprint_unicode_buffered(file, "\n", std::make_format_args());
+    assert(std::ftell(file) == 1);
+  }
+}
+
+int main(int, char**) {
+  print_tests(test_file, test_exception);
+
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  test_wide_stream();
+#endif
+  test_read_only();
+  test_new_line();
+
+  return 0;
+}

>From b190535b0c056366cc764241a67d053193b054aa Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 16 Mar 2025 18:24:40 +0100
Subject: [PATCH 3/3] Final polish and fixes.

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/print                          | 34 +++++++++----------
 libcxx/include/version                        |  4 +--
 .../test/libcxx/system_reserved_names.gen.py  |  5 ---
 .../ostream.version.compile.pass.cpp          | 10 +++---
 .../print.version.compile.pass.cpp            | 10 +++---
 .../version.version.compile.pass.cpp          | 10 +++---
 .../generate_feature_test_macro_components.py |  3 +-
 9 files changed, 36 insertions(+), 44 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index dcf9838edd74b..952043f652c27 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -358,7 +358,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_out_ptr``                                      ``202106L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_print``                                        ``202207L``
+    ``__cpp_lib_print``                                        ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges``                                       ``202406L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 1436db6cf2b45..5ccb60e9cf66b 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -50,7 +50,7 @@
 "`P2867R2 <https://wg21.link/P2867R2>`__","Remove Deprecated ``strstreams`` >From C++26","2024-03 (Tokyo)","|Complete|","19",""
 "`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","","",""
 "`P2872R3 <https://wg21.link/P2872R3>`__","Remove ``wstring_convert`` From C++26","2024-03 (Tokyo)","|Complete|","19",""
-"`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","",""
+"`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","|Complete|","21",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","",""
 "`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","",""
diff --git a/libcxx/include/print b/libcxx/include/print
index 3372b08bbcad1..8cc7c07aa0d2a 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -241,7 +241,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __funlockfile(FILE* __stream) {
 _LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked(FILE* __stream) {
 #    if defined(_LIBCPP_WIN32API)
   return ::_fflush_nolock(__stream);
-#    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
+#    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__) || defined(__APPLE__)
   // There is no fflush_unlocked on these systems.
   // This funcion is not part of POSIX.
   return ::fflush(__stream);
@@ -257,7 +257,7 @@ _LIBCPP_HIDE_FROM_ABI inline size_t
 __fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t __n, FILE* __stream) {
 #    if defined(_LIBCPP_WIN32API)
   return ::_fwrite_nolock(__buffer, __size, __n, __stream);
-#    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
+#    elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__) || defined(__APPLE__)
   // The function fwrite_unlocked is not part of POSIX and not available on
   // these systems.
   auto __b = static_cast<const char*>(__buffer);
@@ -291,7 +291,7 @@ _LIBCPP_HIDE_FROM_ABI int __fflush(FILE* __stream) {
     return std::fflush(__stream);
   else
 #    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
-    sizeof(__lock_policy) == 0
+    static_assert(sizeof(__lock_policy) == 0, "Unsupported policy");
 #    else
     static_assert(false, "Unsupported policy");
 #    endif
@@ -316,7 +316,7 @@ public:
       __print::__flockfile(__stream_);
     else if constexpr (__policy != __lock_policy::__stdio)
 #    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
-      sizeof(__lock_policy) == 0
+      static_assert(sizeof(__lock_policy) == 0, "Unsupported policy");
 #    else
       static_assert(false, "Unsupported policy");
 #    endif
@@ -327,7 +327,7 @@ public:
       __print::__funlockfile(__stream_);
     else if constexpr (__policy != __lock_policy::__stdio)
 #    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
-      sizeof(__lock_policy) == 0
+      static_assert(sizeof(__lock_policy) == 0, "Unsupported policy");
 #    else
       static_assert(false, "Unsupported policy");
 #    endif
@@ -359,7 +359,7 @@ private:
         return std::fwrite(__small_buffer_, 1, __n, __stream_);
       else
 #    if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
-        sizeof(__lock_policy) == 0
+        static_assert(sizeof(__lock_policy) == 0, "Unsupported policy");
 #    else
         static_assert(false, "Unsupported policy");
 #    endif
@@ -505,20 +505,19 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
 
 template <class... _Args>
 _LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
-#    if _LIBCPP_HAS_UNICODE
-  constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
-#    else  // _LIBCPP_HAS_UNICODE
-  constexpr bool __use_unicode = false;
-#    endif // _LIBCPP_HAS_UNICODE
   constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
 
   using enum __print::__lock_policy;
-  if constexpr (__use_unicode)
+#    if _LIBCPP_HAS_UNICODE
+  if constexpr (__print::__use_unicode_execution_charset)
     __print::__vprint_unicode<__locksafe ? __manual : __stdio>(
         __stream, __fmt.get(), std::make_format_args(__args...), false);
   else
+#    endif // _LIBCPP_HAS_UNICODE
+  {
     __print::__vprint_nonunicode<__locksafe ? __manual : __stdio>(
         __stream, __fmt.get(), std::make_format_args(__args...), false);
+  }
 }
 
 template <class... _Args>
@@ -528,23 +527,22 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
 
 template <class... _Args>
 _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
-#    if _LIBCPP_HAS_UNICODE
-  constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
-#    else  // _LIBCPP_HAS_UNICODE
-  constexpr bool __use_unicode = false;
-#    endif // _LIBCPP_HAS_UNICODE
   constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
 
   // Note the wording in the Standard is inefficient. The output of
   // std::format is a std::string which is then copied. This solution
   // just appends a newline at the end of the output.
   using enum __print::__lock_policy;
-  if constexpr (__use_unicode)
+#    if _LIBCPP_HAS_UNICODE
+  if constexpr (__print::__use_unicode_execution_charset)
     __print::__vprint_unicode<__locksafe ? __manual : __stdio>(
         __stream, __fmt.get(), std::make_format_args(__args...), true);
   else
+#    endif // _LIBCPP_HAS_UNICODE
+  {
     __print::__vprint_nonunicode<__locksafe ? __manual : __stdio>(
         __stream, __fmt.get(), std::make_format_args(__args...), true);
+  }
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/include/version b/libcxx/include/version
index 63ead9fd5d29d..f2f3352815dbc 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -185,7 +185,7 @@ __cpp_lib_out_ptr                                       202311L <memory>
 __cpp_lib_parallel_algorithm                            201603L <algorithm> <numeric>
 __cpp_lib_philox_engine                                 202406L <random>
 __cpp_lib_polymorphic_allocator                         201902L <memory_resource>
-__cpp_lib_print                                         202207L <ostream> <print>
+__cpp_lib_print                                         202403L <ostream> <print>
 __cpp_lib_quoted_string_io                              201304L <iomanip>
 __cpp_lib_ranges                                        202406L <algorithm> <functional> <iterator>
                                                                 <memory> <ranges>
@@ -502,7 +502,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_optional                             202110L
 # define __cpp_lib_out_ptr                              202106L
 # if _LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT
-#   define __cpp_lib_print                              202207L
+#   define __cpp_lib_print                              202403L
 # endif
 # undef  __cpp_lib_ranges
 # define __cpp_lib_ranges                               202406L
diff --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py
index 304c803b76c3d..f01126249c881 100644
--- a/libcxx/test/libcxx/system_reserved_names.gen.py
+++ b/libcxx/test/libcxx/system_reserved_names.gen.py
@@ -119,11 +119,6 @@
 #define __acquire SYSTEM_RESERVED_NAME
 #define __release SYSTEM_RESERVED_NAME
 
-// Android and FreeBSD use this for __attribute__((__unused__))
-#if !defined(__FreeBSD__)  && !defined(__ANDROID__)
-#define __unused SYSTEM_RESERVED_NAME
-#endif
-
 // These names are not reserved, so the user can macro-define them.
 // These are intended to find improperly _Uglified template parameters.
 #define A SYSTEM_RESERVED_NAME
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp
index 61d7747e4b6d1..16bcb02c2516f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp
@@ -19,7 +19,7 @@
 
 /*  Constant             Value
     __cpp_lib_char8_t    201907L [C++20]
-    __cpp_lib_print      202207L [C++23]
+    __cpp_lib_print      202403L [C++23]
 */
 
 #include <ostream>
@@ -93,8 +93,8 @@
 #   ifndef __cpp_lib_print
 #     error "__cpp_lib_print should be defined in c++23"
 #   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++23"
+#   if __cpp_lib_print != 202403L
+#     error "__cpp_lib_print should have the value 202403L in c++23"
 #   endif
 # else
 #   ifdef __cpp_lib_print
@@ -121,8 +121,8 @@
 #   ifndef __cpp_lib_print
 #     error "__cpp_lib_print should be defined in c++26"
 #   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++26"
+#   if __cpp_lib_print != 202403L
+#     error "__cpp_lib_print should have the value 202403L in c++26"
 #   endif
 # else
 #   ifdef __cpp_lib_print
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
index c9743cf41ef1e..de04d0bf02ba8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
@@ -18,7 +18,7 @@
 // Test the feature test macros defined by <print>
 
 /*  Constant           Value
-    __cpp_lib_print    202207L [C++23]
+    __cpp_lib_print    202403L [C++23]
 */
 
 #include <print>
@@ -54,8 +54,8 @@
 #   ifndef __cpp_lib_print
 #     error "__cpp_lib_print should be defined in c++23"
 #   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++23"
+#   if __cpp_lib_print != 202403L
+#     error "__cpp_lib_print should have the value 202403L in c++23"
 #   endif
 # else
 #   ifdef __cpp_lib_print
@@ -69,8 +69,8 @@
 #   ifndef __cpp_lib_print
 #     error "__cpp_lib_print should be defined in c++26"
 #   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++26"
+#   if __cpp_lib_print != 202403L
+#     error "__cpp_lib_print should have the value 202403L in c++26"
 #   endif
 # else
 #   ifdef __cpp_lib_print
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 1e4465d515e6b..6e2d3eec7f366 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -169,7 +169,7 @@
     __cpp_lib_parallel_algorithm                            201603L [C++17]
     __cpp_lib_philox_engine                                 202406L [C++26]
     __cpp_lib_polymorphic_allocator                         201902L [C++20]
-    __cpp_lib_print                                         202207L [C++23]
+    __cpp_lib_print                                         202403L [C++23]
     __cpp_lib_quoted_string_io                              201304L [C++14]
     __cpp_lib_ranges                                        202110L [C++20]
                                                             202406L [C++23]
@@ -5683,8 +5683,8 @@
 #   ifndef __cpp_lib_print
 #     error "__cpp_lib_print should be defined in c++23"
 #   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++23"
+#   if __cpp_lib_print != 202403L
+#     error "__cpp_lib_print should have the value 202403L in c++23"
 #   endif
 # else
 #   ifdef __cpp_lib_print
@@ -7567,8 +7567,8 @@
 #   ifndef __cpp_lib_print
 #     error "__cpp_lib_print should be defined in c++26"
 #   endif
-#   if __cpp_lib_print != 202207L
-#     error "__cpp_lib_print should have the value 202207L in c++26"
+#   if __cpp_lib_print != 202403L
+#     error "__cpp_lib_print should have the value 202403L in c++26"
 #   endif
 # else
 #   ifdef __cpp_lib_print
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 8bf7633e985d5..7a0f31e6b7463 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1010,8 +1010,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_print",
             "values": {
-                "c++23": 202207,
-                # "c++26": 202403, # P3107R5: Permit an efficient implementation of std::print
+                "c++23": 202403,
                 # "c++26": 202406, # P3235R3 std::print more types faster with less memory
             },
             "headers": ["ostream", "print"],



More information about the llvm-branch-commits mailing list