aboutsummaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
authorMark de Wever <koraq@xs4all.nl>2023-04-21 08:09:06 +0200
committerMark de Wever <koraq@xs4all.nl>2023-07-18 20:07:11 +0200
commit3f65f718332c96c7ececcd9cde84be740076a4f1 (patch)
tree22b322df2ba07e679758bbdac2e994019f4af342 /libcxx
parentb88db47bd2085c2c8a246751feb63a3ac20a3665 (diff)
downloadllvm-3f65f718332c96c7ececcd9cde84be740076a4f1.zip
llvm-3f65f718332c96c7ececcd9cde84be740076a4f1.tar.gz
llvm-3f65f718332c96c7ececcd9cde84be740076a4f1.tar.bz2
[libc++][print] Adds FILE functions.
Drive-by fix to make sure the __retarget_buffer works correctly whan using a hint of 1. This was discovered in one of the new tests. Drive-by fixes __retarget_buffer when initialized with size 1. Implements parts of - P2093R14 Formatted output - P2539R4 Should the output of std::print to a terminal be synchronized with the underlying stream? Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D150044
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/docs/FeatureTestMacroTable.rst2
-rw-r--r--libcxx/docs/Status/Cxx23Papers.csv4
-rw-r--r--libcxx/docs/Status/FormatIssues.csv8
-rw-r--r--libcxx/docs/Status/FormatPaper.csv2
-rw-r--r--libcxx/include/__format/buffer.h10
-rw-r--r--libcxx/include/print247
-rw-r--r--libcxx/include/version2
-rw-r--r--libcxx/modules/std/print.cppm2
-rw-r--r--libcxx/src/CMakeLists.txt1
-rw-r--r--libcxx/src/print.cpp62
-rw-r--r--libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp73
-rw-r--r--libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp133
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx03.csv11
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx11.csv11
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx14.csv11
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx17.csv11
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx20.csv11
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx23.csv11
-rw-r--r--libcxx/test/libcxx/transitive_includes/cxx26.csv11
-rw-r--r--libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp90
-rw-r--r--libcxx/test/std/input.output/iostream.format/print.fun/print.file.pass.cpp139
-rw-r--r--libcxx/test/std/input.output/iostream.format/print.fun/print_tests.h83
-rw-r--r--libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp142
-rw-r--r--libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.file.pass.cpp143
-rw-r--r--libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.file.pass.cpp150
-rw-r--r--libcxx/test/std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp43
-rw-r--r--libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp80
-rw-r--r--libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp43
-rw-r--r--libcxx/test/std/utilities/format/format.functions/fill.unicode.pass.cpp1
-rwxr-xr-xlibcxx/utils/ci/run-buildbot1
-rwxr-xr-xlibcxx/utils/generate_feature_test_macro_components.py6
-rw-r--r--libcxx/utils/libcxx/test/header_information.py3
32 files changed, 1533 insertions, 14 deletions
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index deb3b3d..14a251a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -340,6 +340,8 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_out_ptr`` *unimplemented*
--------------------------------------------------- -----------------
+ ``__cpp_lib_print`` *unimplemented*
+ --------------------------------------------------- -----------------
``__cpp_lib_ranges_as_rvalue`` ``202207L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_chunk`` *unimplemented*
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index b8bbe69..c8a8c9f 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -59,7 +59,7 @@
"`P1467R9 <https://wg21.link/P1467R9>`__","LWG","Extended ``floating-point`` types and standard names","July 2022","",""
"`P1642R11 <https://wg21.link/P1642R11>`__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","",""
"`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
-"`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","",""
+"`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","","|In Progress|"
"`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","","","|ranges|"
"`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","|Complete|","16.0","|format| |ranges|"
@@ -101,7 +101,7 @@
"`P2167R3 <https://wg21.link/P2167R3>`__","LWG", "Improved Proposed Wording for LWG 2114", "November 2022","","",""
"`P2396R1 <https://wg21.link/P2396R1>`__","LWG", "Concurrency TS 2 fixes ", "November 2022","","","|concurrency TS|"
"`P2505R5 <https://wg21.link/P2505R5>`__","LWG", "Monadic Functions for ``std::expected``", "November 2022","|Complete|","17.0",""
-"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","","","|format|"
+"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","|In Progress|","","|format|"
"`P2602R2 <https://wg21.link/P2602R2>`__","LWG", "Poison Pills are Too Toxic", "November 2022","","","|ranges|"
"`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
"","","","","","",""
diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv
index 5fc53ec..a6cde93 100644
--- a/libcxx/docs/Status/FormatIssues.csv
+++ b/libcxx/docs/Status/FormatIssues.csv
@@ -5,16 +5,16 @@ Number,Name,Standard,Assignee,Status,First released version
`P1868 <https://wg21.link/P1868>`_,"width: clarifying units of width and precision in std::format (Implements the unicode support.)","C++20",Mark de Wever,|Complete|,14.0
`P2216 <https://wg21.link/P2216>`_,"std::format improvements","C++20",Mark de Wever,|Complete|,15.0
`P2418 <https://wg21.link/P2418>`__,"Add support for ``std::generator``-like types to ``std::format``","C++20",Mark de Wever,|Complete|,15.0
-"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23",Mark de Wever,|In Progress|,
+"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23",Mark de Wever,|In Progress|
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","C++23","Mark de Wever","|Complete|",16.0
"`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|Complete|",15.0
"`P2585R0 <https://wg21.link/P2585R0>`__","Improving default container formatting","C++23","Mark de Wever","|Complete|",17.0
-"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","C++23","Mark de Wever"
+"`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","C++23","Mark de Wever","|In Progress|"
"`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","C++23","Mark de Wever",""
"`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","C++23","Mark de Wever","|Complete|",17.0
"`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","C++23","Mark de Wever","|Complete|",17.0
-"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","C++23","Mark de Wever","|In progress|"
-"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","C++26","Mark de Wever","|Complete|", 17.0
+"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","C++23","Mark de Wever","|In Progress|"
+"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","C++26","Mark de Wever","|Complete|",17.0
"`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","C++26","","",
"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","C++26","","",
`P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,
diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv
index 9fbf2be..4d44db8 100644
--- a/libcxx/docs/Status/FormatPaper.csv
+++ b/libcxx/docs/Status/FormatPaper.csv
@@ -48,5 +48,5 @@ Section,Description,Dependencies,Assignee,Status,First released version
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output"
`[print.fun] <https://wg21.link/print.fun>`__,"Output to ``stdout``",,Mark de Wever,|In Progress|,
-`[print.fun] <https://wg21.link/print.fun>`__,"Output to ``FILE*``",,Mark de Wever,,
+`[print.fun] <https://wg21.link/print.fun>`__,"Output to ``FILE*``",,Mark de Wever,|Complete|, 17.0
`[ostream.formatted.print] <https://wg21.link/ostream.formatted.print>`__,"Output to ``ostream``",,Mark de Wever
diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index f43fd13..45f9da8 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -529,6 +529,7 @@ public:
struct __iterator {
using difference_type = ptrdiff_t;
+ using value_type = _CharT;
_LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer)
: __buffer_(std::addressof(__buffer)) {}
@@ -551,7 +552,14 @@ public:
__retarget_buffer& operator=(const __retarget_buffer&) = delete;
_LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) {
- auto __result = std::__allocate_at_least(__alloc_, __size_hint ? __size_hint : 256 / sizeof(_CharT));
+ // When the initial size is very small a lot of resizes happen
+ // when elements added. So use a hard-coded minimum size.
+ //
+ // Note a size < 2 will not work
+ // - 0 there is no buffer, while push_back requires 1 empty element.
+ // - 1 multiplied by the grow factor is 1 and thus the buffer never
+ // grows.
+ auto __result = std::__allocate_at_least(__alloc_, std::max(__size_hint, 256 / sizeof(_CharT)));
__ptr_ = __result.ptr;
__capacity_ = __result.count;
}
diff --git a/libcxx/include/print b/libcxx/include/print
index 6fcd53c..97f0047 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -10,19 +10,67 @@
#ifndef _LIBCPP_PRINT
#define _LIBCPP_PRINT
+/*
+namespace std {
+ // [print.fun], print functions
+ template<class... Args>
+ void print(FILE* stream, format_string<Args...> fmt, Args&&... args);
+
+ template<class... Args>
+ void println(FILE* stream, format_string<Args...> fmt, Args&&... args);
+
+ void vprint_unicode(FILE* stream, string_view fmt, format_args args);
+
+ void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
+}
+*/
+
#include <__assert> // all public C++ headers provide the assertion handler
#include <__concepts/same_as.h>
#include <__config>
+#include <__format/buffer.h>
+#include <__format/format_arg_store.h>
+#include <__format/format_args.h>
+#include <__format/format_context.h>
+#include <__format/format_error.h>
+#include <__format/format_functions.h>
#include <__format/unicode.h>
+#include <__system_error/system_error.h>
+#include <__utility/forward.h>
+#include <cerrno>
+#include <cstdio>
+#include <string>
#include <string_view>
#include <version>
+#if __has_include(<unistd.h>)
+# include <unistd.h>
+#endif
+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
+#ifdef _WIN32
+_LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
+
+# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+// A wrapper for WriteConsoleW which is used to write to the Windows
+// console. This function is in the dylib to avoid pulling in windows.h
+// in the library headers. The function itself uses some private parts
+// of the dylib too.
+//
+// The function does not depend on the language standard used. Guarding
+// it with C++23 would fail since the dylib is currently built using C++20.
+//
+// Note the function is only implemented on the Windows platform.
+_LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
+# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+
+#endif // _WIN32
+
#if _LIBCPP_STD_VER >= 23
# ifndef _LIBCPP_HAS_NO_UNICODE
@@ -68,7 +116,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __encode(_OutIt&, char32_t) = delete;
template <class _OutIt>
requires __utf16_code_unit<iter_value_t<_OutIt>>
_LIBCPP_HIDE_FROM_ABI constexpr void __encode(_OutIt& __out_it, char32_t __value) {
- _LIBCPP_ASSERT(__is_scalar_value(__value), "an invalid unicode scalar value results in invalid UTF-16");
+ _LIBCPP_ASSERT_UNCATEGORIZED(__is_scalar_value(__value), "an invalid unicode scalar value results in invalid UTF-16");
if (__value < 0x10000) {
*__out_it++ = __value;
@@ -83,7 +131,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __encode(_OutIt& __out_it, char32_t __value
template <class _OutIt>
requires __utf32_code_unit<iter_value_t<_OutIt>>
_LIBCPP_HIDE_FROM_ABI constexpr void __encode(_OutIt& __out_it, char32_t __value) {
- _LIBCPP_ASSERT(__is_scalar_value(__value), "an invalid unicode scalar value results in invalid UTF-32");
+ _LIBCPP_ASSERT_UNCATEGORIZED(__is_scalar_value(__value), "an invalid unicode scalar value results in invalid UTF-32");
*__out_it++ = __value;
}
@@ -101,6 +149,9 @@ _LIBCPP_HIDE_FROM_ABI constexpr _OutIt __transcode(_InIt __first, _InIt __last,
// Note if P2728 is accepted types like int may become valid. In that case
// the __code_point_view should use a span. Libc++ will remove support for
// char_traits<int>.
+
+ // TODO PRINT Validate with clang-tidy
+ // NOLINTNEXTLINE(bugprone-dangling-handle)
basic_string_view<iter_value_t<_InIt>> __data{__first, __last};
__code_point_view<iter_value_t<_InIt>> __view{__data.begin(), __data.end()};
while (!__view.__at_end())
@@ -112,6 +163,198 @@ _LIBCPP_HIDE_FROM_ABI constexpr _OutIt __transcode(_InIt __first, _InIt __last,
# endif // _LIBCPP_HAS_NO_UNICODE
+namespace __print {
+
+// [print.fun]/2
+// Effects: If the ordinary literal encoding ([lex.charset]) is UTF-8, equivalent to:
+// vprint_unicode(stream, fmt.str, make_format_args(args...));
+// Otherwise, equivalent to:
+// vprint_nonunicode(stream, fmt.str, make_format_args(args...));
+//
+// Based on the compiler and its compilation flags this value is or is
+// not true. As mentioned in P2093R14 this only affects Windows. The
+// test below could also be done for
+// - GCC using __GNUC_EXECUTION_CHARSET_NAME
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+// - Clang using __clang_literal_encoding__
+// https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros
+// (note at the time of writing Clang is hard-coded to UTF-8.)
+//
+
+# ifdef _LIBCPP_HAS_NO_UNICODE
+inline constexpr bool __use_unicode = false;
+# elif defined(_MSVC_EXECUTION_CHARACTER_SET)
+// This is the same test MSVC STL uses in their implementation of <print>
+// See: https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
+inline constexpr bool __use_unicode = _MSVC_EXECUTION_CHARACTER_SET == 65001;
+# else
+inline constexpr bool __use_unicode = true;
+# endif
+
+_LIBCPP_HIDE_FROM_ABI inline bool __is_terminal(FILE* __stream) {
+# ifdef _WIN32
+ return std::__is_windows_terminal(__stream);
+# elif __has_include(<unistd.h>)
+ return isatty(fileno(__stream));
+# else
+# error "Provide a way to determine whether a FILE* is a terminal"
+# endif
+}
+
+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) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__stream, "__stream is 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");
+ }
+}
+
+# ifndef _LIBCPP_HAS_NO_UNICODE
+
+// Note these helper functions are mainly used to aid testing.
+// On POSIX systems and Windows the output is no longer considered a
+// 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);
+}
+
+# ifndef _LIBCPP_HAS_NO_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) {
+ 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(_WIN32)
+ 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_NO_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([[maybe_unused]] FILE* __stream,
+ [[maybe_unused]] string_view __fmt,
+ [[maybe_unused]] format_args __args,
+ [[maybe_unused]] bool __write_nl) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__stream, "__stream is a valid pointer to an output C stream");
+
+ // [print.fun]
+ // 7 - Effects: If stream refers to a terminal capable of displaying
+ // Unicode, writes out to the terminal using the native Unicode
+ // API; if out contains invalid code units, the behavior is
+ // undefined and implementations are encouraged to diagnose it.
+ // Otherwise writes out to stream unchanged. If the native
+ // Unicode API is used, the function flushes stream before
+ // writing out.
+ // 8 - Throws: Any exception thrown by the call to vformat
+ // ([format.err.report]). system_error if writing to the terminal
+ // or stream fails. May throw bad_alloc.
+ // 9 - Recommended practice: If invoking the native Unicode API
+ // requires transcoding, implementations should substitute
+ // invalid code units with U+FFFD replacement character per the
+ // Unicode Standard, Chapter 3.9 U+FFFD Substitution in
+ // Conversion.
+
+ // On non-Windows platforms the Unicode API is the normal file I/O API
+ // so there the call can be forwarded to the non_unicode API. On
+ // Windows there is a different API. This API requires transcoding.
+
+# ifndef _WIN32
+ __print::__vprint_unicode_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+# elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+ __print::__vprint_unicode_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_NO_UNICODE
+
+} // namespace __print
+
+template <class... _Args>
+_LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
+# ifndef _LIBCPP_HAS_NO_UNICODE
+ if constexpr (__print::__use_unicode)
+ __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);
+# else // _LIBCPP_HAS_NO_UNICODE
+ __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+# endif // _LIBCPP_HAS_NO_UNICODE
+}
+
+template <class... _Args>
+_LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
+# ifndef _LIBCPP_HAS_NO_UNICODE
+ // 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)
+ __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_NO_UNICODE
+ __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+# endif // _LIBCPP_HAS_NO_UNICODE
+}
+
+# ifndef _LIBCPP_HAS_NO_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);
+}
+# endif // _LIBCPP_HAS_NO_UNICODE
+
+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);
+}
+
#endif // _LIBCPP_STD_VER >= 23
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/version b/libcxx/include/version
index 4091a49..6f588c8 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -149,6 +149,7 @@ __cpp_lib_optional 202110L <optional>
__cpp_lib_out_ptr 202106L <memory>
__cpp_lib_parallel_algorithm 201603L <algorithm> <numeric>
__cpp_lib_polymorphic_allocator 201902L <memory_resource>
+__cpp_lib_print 202207L <ostream> <print>
__cpp_lib_quoted_string_io 201304L <iomanip>
__cpp_lib_ranges 202207L <algorithm> <functional> <iterator>
<memory> <ranges>
@@ -429,6 +430,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# undef __cpp_lib_optional
# define __cpp_lib_optional 202110L
// # define __cpp_lib_out_ptr 202106L
+// # define __cpp_lib_print 202207L
# define __cpp_lib_ranges_as_rvalue 202207L
// # define __cpp_lib_ranges_chunk 202202L
// # define __cpp_lib_ranges_chunk_by 202202L
diff --git a/libcxx/modules/std/print.cppm b/libcxx/modules/std/print.cppm
index 21de1cd..9d089a6 100644
--- a/libcxx/modules/std/print.cppm
+++ b/libcxx/modules/std/print.cppm
@@ -12,12 +12,10 @@ module;
export module std:print;
export namespace std {
-#if 0
// [print.fun], print functions
using std::print;
using std::println;
using std::vprint_nonunicode;
using std::vprint_unicode;
-#endif
} // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 9091311..35b4665 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -41,6 +41,7 @@ set(LIBCXX_SOURCES
new_handler.cpp
new_helpers.cpp
optional.cpp
+ print.cpp
random_shuffle.cpp
ryu/d2fixed.cpp
ryu/d2s.cpp
diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp
new file mode 100644
index 0000000..3d8a4c2
--- /dev/null
+++ b/libcxx/src/print.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <cstdlib>
+#include <print>
+
+#if defined(_LIBCPP_WIN32API)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <io.h>
+# include <windows.h>
+
+# include <__system_error/system_error.h>
+
+# include "filesystem/error.h"
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#ifdef _WIN32
+_LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream) {
+ // Note the Standard does this in one call, but it's unclear whether
+ // an invalid handle is allowed when calling GetConsoleMode.
+ //
+ // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=msvc-170
+ // https://learn.microsoft.com/en-us/windows/console/getconsolemode
+ intptr_t __handle = _get_osfhandle(fileno(__stream));
+ if (__handle == -1)
+ return false;
+
+ unsigned long __mode;
+ return GetConsoleMode(reinterpret_cast<void*>(__handle), &__mode);
+}
+
+# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+_LIBCPP_EXPORTED_FROM_ABI void
+__write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wstring_view __view) {
+ // https://learn.microsoft.com/en-us/windows/console/writeconsole
+ if (WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fileno(__stream))), // clang-format aid
+ __view.data(),
+ __view.size(),
+ nullptr,
+ nullptr) == 0) {
+# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ // There is no __throw_system_error overload that takes an error code.
+ throw system_error{filesystem::detail::make_windows_error(GetLastError()), "failed to write formatted output"};
+# else // _LIBCPP_HAS_NO_EXCEPTIONS
+ std::abort();
+# endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ }
+}
+# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+
+#endif // _WIN32
+
+_LIBCPP_END_NAMESPACE_STD
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
new file mode 100644
index 0000000..4817111
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+// 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: GCC-ALWAYS_INLINE-FIXME
+
+// XFAIL: availability-fp_to_chars-missing
+
+// REQUIRES: has-unix-headers
+
+// <print>
+
+// Tests the implementation of
+// void __print::__vprint_unicode_posix(FILE* __stream, string_view __fmt,
+// format_args __args, bool __write_nl,
+// bool __is_terminal);
+//
+// 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. By testing this function we can "force" emulate a
+// terminal.
+// Note __write_nl is tested by the public API.
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstdio>
+#include <print>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+ std::array<char, 100> buffer;
+ std::ranges::fill(buffer, '*');
+
+ FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
+ assert(file);
+
+ // Test the file is buffered.
+ std::fprintf(file, "Hello");
+ assert(std::ftell(file) == 5);
+#if defined(TEST_HAS_GLIBC) && \
+ !(__has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer))
+ assert(std::ranges::all_of(buffer, [](char c) { return c == '*'; }));
+#endif
+
+ // Test writing to a "non-terminal" stream does not flush.
+ std::__print::__vprint_unicode_posix(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))
+ assert(std::ranges::all_of(buffer, [](char c) { return c == '*'; }));
+#endif
+
+ // Test writing to a "terminal" stream flushes before writing.
+ std::__print::__vprint_unicode_posix(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)
+ // glibc does not flush after a write.
+ assert(buffer[11] != '!');
+#endif
+
+ // Test everything is written when closing the stream.
+ std::fclose(file);
+ assert(std::string_view(buffer.data(), buffer.data() + 12) == "Hello world!");
+
+ 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
new file mode 100644
index 0000000..f85609f
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+// 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: no-wide-characters
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// Clang modules do not work with the definiton of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
+// XFAIL: modules-build
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <print>
+
+// Tests the implementation of
+// void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt,
+// format_args __args, bool __write_nl,
+// bool __is_terminal);
+//
+// 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. By testing this function we can "force" emulate a
+// terminal.
+// Note __write_nl is tested by the public API.
+
+#include <string_view>
+#include <cstdio>
+#include <algorithm>
+#include <cassert>
+
+void write_to_console(FILE*, std::wstring_view data);
+#define _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION ::write_to_console
+#include <print>
+
+#include "test_macros.h"
+#include "filesystem_test_helper.h"
+#include "make_string.h"
+
+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";
+
+void write_to_console(FILE*, std::wstring_view data) {
+ assert(calling);
+ assert(data == expected);
+}
+
+scoped_test_env env;
+std::string filename = env.create_file("output.txt");
+
+static void test_basics() {
+ FILE* file = std::fopen(filename.c_str(), "wb");
+ assert(file);
+
+ // Test writing to a "non-terminal" stream does not call WriteConsoleW.
+ std::__print::__vprint_unicode_windows(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);
+}
+
+// When the output is a file the data is written as-is.
+// When the output is a "terminal" invalid UTF-8 input is flagged.
+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);
+ assert(std::ftell(file) == static_cast<long>(input.size()));
+ std::fclose(file);
+
+ file = std::fopen(filename.c_str(), "rb");
+ assert(file);
+
+ std::vector<char> buffer(input.size());
+ size_t read = fread(buffer.data(), 1, buffer.size(), file);
+ assert(read == input.size());
+ assert(input == std::string_view(buffer.begin(), buffer.end()));
+ std::fclose(file);
+
+ // *** Terminal ***
+ expected = output;
+ std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true);
+}
+
+static void test() {
+ // *** Test valid UTF-8 ***
+#define TEST(S) test(SV(S), S)
+ TEST("hello world");
+
+ // copied from benchmarks/std_format_spec_string_unicode.bench.cpp
+ TEST("Lorem ipsum dolor sit amet, ne sensibus evertitur aliquando his. Iuvaret fabulas qui ex.");
+ TEST("Lōrem ipsūm dolor sīt æmeÞ, ea vel nostrud feuġǣit, muciūs tēmporiȝusrefērrēnÞur no mel.");
+ TEST("Лорем ипсум долор сит амет, еу диам тамяуам принципес вис, еяуидем цонцептам диспутандо");
+ TEST("入ト年媛ろ舗学ラロ準募ケカ社金スノ屋検れう策他セヲシ引口ぎ集7独ぱクふ出車ぽでぱ円輪ルノ受打わ。");
+ TEST("\U0001f636\u200d\U0001f32b\ufe0f");
+#undef TEST
+
+ // *** Test invalid utf-8 ***
+ test(SV("\ufffd"), "\xc3");
+ test(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
+
+ // beyond valid values
+ test(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
+ test(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");
+}
+
+int main(int, char**) {
+ test_basics();
+ test();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 7a6ba4f..53e4aa4 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -634,11 +634,22 @@ ostream string
ostream type_traits
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index cfce8c0..29b323b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -635,11 +635,22 @@ ostream string
ostream type_traits
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 7c6e8c9..1a03dd9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -637,11 +637,22 @@ ostream string
ostream type_traits
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 7c6e8c9..1a03dd9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -637,11 +637,22 @@ ostream string
ostream type_traits
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 603d789..55860c7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -643,11 +643,22 @@ ostream string
ostream type_traits
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index c1cd8e9..3fa2d94 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -452,11 +452,22 @@ ostream streambuf
ostream string
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index c1cd8e9..3fa2d94 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -452,11 +452,22 @@ ostream streambuf
ostream string
ostream typeinfo
ostream version
+print array
+print cerrno
+print cmath
print cstddef
print cstdint
+print cstdio
+print cstdlib
print initializer_list
print limits
+print locale
+print new
+print optional
+print stdexcept
+print string
print string_view
+print tuple
print version
queue compare
queue cstddef
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
new file mode 100644
index 0000000..d1593c4
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -0,0 +1,90 @@
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// XFAIL: msvc, target={{.+}}-windows-gnu
+// XFAIL: availability-fp_to_chars-missing
+
+// <print>
+
+// The FILE returned by fmemopen does not have file descriptor.
+// This means the test could fail when the implementation uses a
+// function that requires a file descriptor, for example write.
+//
+// This tests all print functions which takes a FILE* as argument.
+
+// template<class... Args>
+// void print(FILE* stream, format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+// void println(FILE* stream, format_string<Args...> fmt, Args&&... args);
+// void vprint_unicode(FILE* stream, string_view fmt, format_args args);
+// void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
+
+#include <array>
+#include <cstdio>
+#include <cassert>
+#include <print>
+
+static void test_print() {
+ std::array<char, 100> buffer{0};
+
+ FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
+ assert(file);
+
+ std::print(file, "hello world{}", '!');
+ long pos = std::ftell(file);
+ std::fclose(file);
+
+ assert(pos > 0);
+ assert(std::string_view(buffer.data(), pos) == "hello world!");
+}
+
+static void test_println() {
+ std::array<char, 100> buffer{0};
+
+ FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
+ assert(file);
+
+ std::println(file, "hello world{}", '!');
+ long pos = std::ftell(file);
+ std::fclose(file);
+
+ assert(pos > 0);
+ assert(std::string_view(buffer.data(), pos) == "hello world!\n");
+}
+
+static void test_vprint_unicode() {
+ std::array<char, 100> buffer{0};
+
+ FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
+ assert(file);
+
+ std::vprint_unicode(file, "hello world{}", std::make_format_args('!'));
+ long pos = std::ftell(file);
+ std::fclose(file);
+
+ assert(pos > 0);
+ assert(std::string_view(buffer.data(), pos) == "hello world!");
+}
+
+static void test_vprint_nonunicode() {
+ std::array<char, 100> buffer{0};
+
+ FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
+ assert(file);
+
+ std::vprint_nonunicode(file, "hello world{}", std::make_format_args('!'));
+ long pos = std::ftell(file);
+ std::fclose(file);
+
+ assert(pos > 0);
+ assert(std::string_view(buffer.data(), pos) == "hello world!");
+}
+
+int main(int, char**) {
+ test_print();
+ test_println();
+ test_vprint_unicode();
+ test_vprint_nonunicode();
+
+ return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/print.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/print.file.pass.cpp
new file mode 100644
index 0000000..9fc2fcc
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/print.file.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <print>
+
+// template<class... Args>
+// void print(FILE* stream, format_string<Args...> fmt, 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_format_string.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, test_format_string<char, Args...> fmt, Args&&... args) {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ std::print(file, fmt, std::forward<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.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
+ // After P2216 most exceptions thrown by std::format become ill-formed.
+ // Therefore this tests does nothing.
+ // A basic ill-formed test is done in format.verify.cpp
+ // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+};
+
+// 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::print(file, "hello"));
+}
+#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) {
+#ifdef _AIX
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Broken pipe"};
+#else
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Operation not permitted"};
+#endif
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::print(file, "hello"));
+}
+
+static void test_new_line() {
+ // Text does newline translation.
+ {
+ FILE* file = fopen(filename.c_str(), "w");
+ assert(file);
+
+ std::print(file, "\n");
+#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::print(file, "\n");
+ 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/print_tests.h b/libcxx/test/std/input.output/iostream.format/print.fun/print_tests.h
new file mode 100644
index 0000000..46b6eeb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/print_tests.h
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
+#define TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
+
+template <class TestFunction, class ExceptionTest>
+void print_tests(TestFunction check, ExceptionTest check_exception) {
+ // *** Test escaping ***
+
+ check("{", "{{");
+ check("{:^}", "{{:^}}");
+ check("{: ^}", "{{:{}^}}", ' ');
+ check("{:{}^}", "{{:{{}}^}}");
+ check("{:{ }^}", "{{:{{{}}}^}}", ' ');
+
+ // *** Test argument ID ***
+ check("hello false true", "hello {0:} {1:}", false, true);
+ check("hello true false", "hello {1:} {0:}", false, true);
+
+ // *** Test many arguments ***
+ check(
+ "1234567890\t1234567890",
+ "{}{}{}{}{}{}{}{}{}{}\t{}{}{}{}{}{}{}{}{}{}",
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 0);
+
+ // *** Test embedded NUL character ***
+ using namespace std::literals;
+ check("hello\0world"sv, "hello{}{}", '\0', "world");
+ check("hello\0world"sv, "hello\0{}"sv, "world");
+ check("hello\0world"sv, "hello{}", "\0world"sv);
+
+ // *** Test Unicode ***
+ // 2-byte code points
+ check("\u00a1"sv, "{}"sv, "\u00a1"); // INVERTED EXCLAMATION MARK
+ check("\u07ff"sv, "{:}"sv, "\u07ff"); // NKO TAMAN SIGN
+
+ // 3-byte code points
+ check("\u0800"sv, "{}"sv, "\u0800"); // SAMARITAN LETTER ALAF
+ check("\ufffd"sv, "{}"sv, "\ufffd"); // REPLACEMENT CHARACTER
+
+ // 4-byte code points
+ check("\U00010000"sv, "{}"sv, "\U00010000"); // LINEAR B SYLLABLE B008 A
+ check("\U0010FFFF"sv, "{}"sv, "\U0010FFFF"); // Undefined Character
+
+ // *** Test invalid format strings ***
+ check_exception("The format string terminates at a '{'", "{");
+ check_exception("The replacement field misses a terminating '}'", "{:", 42);
+
+ check_exception("The format string contains an invalid escape sequence", "}");
+ check_exception("The format string contains an invalid escape sequence", "{:}-}", 42);
+
+ check_exception("The format string contains an invalid escape sequence", "} ");
+ check_exception("The arg-id of the format-spec starts with an invalid character", "{-", 42);
+ check_exception("Argument index out of bounds", "hello {}");
+ check_exception("Argument index out of bounds", "hello {0}");
+ check_exception("Argument index out of bounds", "hello {1}", 42);
+}
+
+#endif // TEST_STD_INPUT_OUTPUT_IOSTREAM_FORMAT_PRINT_FUN_PRINT_TESTS_H
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp
new file mode 100644
index 0000000..7c46d35
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/println.file.pass.cpp
@@ -0,0 +1,142 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <print>
+
+// template<class... Args>
+// void println(FILE* stream, format_string<Args...> fmt, 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 <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_format_string.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 e, test_format_string<char, Args...> fmt, Args&&... args) {
+ std::string expected = std::string{e} + '\n';
+
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ std::println(file, fmt, std::forward<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.get(), "\nExpected output ", expected, "\nActual output ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>(std::string_view, std::string_view, Args&&...) {
+ // After P2216 most exceptions thrown by std::format become ill-formed.
+ // Therefore this tests does nothing.
+ // A basic ill-formed test is done in format.verify.cpp
+ // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument.
+};
+
+// 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::println(file, "hello"));
+}
+#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) {
+#ifdef _AIX
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Broken pipe"};
+#else
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Operation not permitted"};
+#endif
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::println(file, "hello"));
+}
+
+static void test_new_line() {
+ // Text does newline translation.
+ {
+ FILE* file = fopen(filename.c_str(), "w");
+ assert(file);
+
+ std::println(file, "");
+#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::println(file, "");
+ 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_nonunicode.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.file.pass.cpp
new file mode 100644
index 0000000..6953558
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.file.pass.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <print>
+
+// void vprint_nonunicode(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(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(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(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) {
+#ifdef _AIX
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Broken pipe"};
+#else
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Operation not permitted"};
+#endif
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_nonunicode(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(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(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.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.file.pass.cpp
new file mode 100644
index 0000000..c26ed0b
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.file.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+// 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
+
+// XFAIL: availability-fp_to_chars-missing
+
+// <print>
+
+// void vprint_unicode(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(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(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(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) {
+#ifdef _AIX
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Broken pipe"};
+#else
+ [[maybe_unused]] std::string_view what{"failed to write formatted output: Operation not permitted"};
+#endif
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_unicode(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(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(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/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 234653c..7203220 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,6 +19,7 @@
/* Constant Value
__cpp_lib_char8_t 201907L [C++20]
+ __cpp_lib_print 202207L [C++23]
*/
#include <ostream>
@@ -30,18 +31,30 @@
# error "__cpp_lib_char8_t should not be defined before c++20"
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
#elif TEST_STD_VER == 14
# ifdef __cpp_lib_char8_t
# error "__cpp_lib_char8_t should not be defined before c++20"
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
#elif TEST_STD_VER == 17
# ifdef __cpp_lib_char8_t
# error "__cpp_lib_char8_t should not be defined before c++20"
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
#elif TEST_STD_VER == 20
# if defined(__cpp_char8_t)
@@ -57,6 +70,10 @@
# endif
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
#elif TEST_STD_VER == 23
# if defined(__cpp_char8_t)
@@ -72,6 +89,19 @@
# endif
# endif
+# if !defined(_LIBCPP_VERSION)
+# 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"
+# endif
+# else // _LIBCPP_VERSION
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
#elif TEST_STD_VER > 23
# if defined(__cpp_char8_t)
@@ -87,5 +117,18 @@
# endif
# endif
+# if !defined(_LIBCPP_VERSION)
+# 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"
+# endif
+# else // _LIBCPP_VERSION
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
#endif // TEST_STD_VER > 23
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
new file mode 100644
index 0000000..ae5cc89
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+//
+// clang-format off
+
+// <print>
+
+// Test the feature test macros defined by <print>
+
+/* Constant Value
+ __cpp_lib_print 202207L [C++23]
+*/
+
+#include <print>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 14
+
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
+#elif TEST_STD_VER == 23
+
+# if !defined(_LIBCPP_VERSION)
+# 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"
+# endif
+# else // _LIBCPP_VERSION
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#elif TEST_STD_VER > 23
+
+# if !defined(_LIBCPP_VERSION)
+# 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"
+# endif
+# else // _LIBCPP_VERSION
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
+#endif // TEST_STD_VER > 23
+
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 e969d22..75991c2 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
@@ -138,6 +138,7 @@
__cpp_lib_out_ptr 202106L [C++23]
__cpp_lib_parallel_algorithm 201603L [C++17]
__cpp_lib_polymorphic_allocator 201902L [C++20]
+ __cpp_lib_print 202207L [C++23]
__cpp_lib_quoted_string_io 201304L [C++14]
__cpp_lib_ranges 202207L [C++20]
__cpp_lib_ranges_as_rvalue 202207L [C++23]
@@ -674,6 +675,10 @@
# error "__cpp_lib_polymorphic_allocator should not be defined before c++20"
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_quoted_string_io
# error "__cpp_lib_quoted_string_io should not be defined before c++14"
# endif
@@ -1421,6 +1426,10 @@
# error "__cpp_lib_polymorphic_allocator should not be defined before c++20"
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
# ifndef __cpp_lib_quoted_string_io
# error "__cpp_lib_quoted_string_io should be defined in c++14"
# endif
@@ -2342,6 +2351,10 @@
# error "__cpp_lib_polymorphic_allocator should not be defined before c++20"
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
# ifndef __cpp_lib_quoted_string_io
# error "__cpp_lib_quoted_string_io should be defined in c++17"
# endif
@@ -3530,6 +3543,10 @@
# endif
# endif
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined before c++23"
+# endif
+
# ifndef __cpp_lib_quoted_string_io
# error "__cpp_lib_quoted_string_io should be defined in c++20"
# endif
@@ -4889,6 +4906,19 @@
# endif
# endif
+# if !defined(_LIBCPP_VERSION)
+# 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"
+# endif
+# else // _LIBCPP_VERSION
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
# ifndef __cpp_lib_quoted_string_io
# error "__cpp_lib_quoted_string_io should be defined in c++23"
# endif
@@ -6428,6 +6458,19 @@
# endif
# endif
+# if !defined(_LIBCPP_VERSION)
+# 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"
+# endif
+# else // _LIBCPP_VERSION
+# ifdef __cpp_lib_print
+# error "__cpp_lib_print should not be defined because it is unimplemented in libc++!"
+# endif
+# endif
+
# ifndef __cpp_lib_quoted_string_io
# error "__cpp_lib_quoted_string_io should be defined in c++26"
# endif
diff --git a/libcxx/test/std/utilities/format/format.functions/fill.unicode.pass.cpp b/libcxx/test/std/utilities/format/format.functions/fill.unicode.pass.cpp
index 9fe478a..c9d80f6 100644
--- a/libcxx/test/std/utilities/format/format.functions/fill.unicode.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/fill.unicode.pass.cpp
@@ -6,7 +6,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: libcpp-has-no-incomplete-format
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
// This version runs the test when the platform has Unicode support.
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6934634a..c8954d3 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -248,6 +248,7 @@ check-generated-output)
--exclude 'ostream.pass.cpp' \
--exclude 'std_format_spec_string_unicode.bench.cpp' \
--exclude 'transcoding.pass.cpp' \
+ --exclude 'vprint_unicode_windows.pass.cpp' \
--exclude 'underflow.pass.cpp' \
|| false
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 759badf..67545f2 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -772,6 +772,12 @@ feature_test_macros = [
"libcxx_guard": "!defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR)",
},
{
+ "name": "__cpp_lib_print",
+ "values": {"c++23": 202207},
+ "headers": ["ostream", "print"],
+ "unimplemented": True,
+ },
+ {
"name": "__cpp_lib_quoted_string_io",
"values": {"c++14": 201304},
"headers": ["iomanip"],
diff --git a/libcxx/utils/libcxx/test/header_information.py b/libcxx/utils/libcxx/test/header_information.py
index 5d3bc8b..8a02e69 100644
--- a/libcxx/utils/libcxx/test/header_information.py
+++ b/libcxx/utils/libcxx/test/header_information.py
@@ -41,10 +41,11 @@ lit_header_restrictions = {
"iostream": "// UNSUPPORTED: no-localization",
"istream": "// UNSUPPORTED: no-localization",
"latch": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
- "locale.h": "// UNSUPPORTED: no-localization",
"locale": "// UNSUPPORTED: no-localization",
+ "locale.h": "// UNSUPPORTED: no-localization",
"mutex": "// UNSUPPORTED: no-threads, c++03",
"ostream": "// UNSUPPORTED: no-localization",
+ "print": "// UNSUPPORTED: availability-fp_to_chars-missing", # TODO PRINT investigate
"regex": "// UNSUPPORTED: no-localization",
"semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",