[libcxx-commits] [libcxx] [libc++] Switch to a new technique for std::to_chars availability (PR #234468)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 21 20:02:22 PDT 2026


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/234468

This PR switches std::to_chars to a new technique for producing
errors when an API is used that isn't available on the deployment
target. Our current usage of availability attributes has well known
limitations like https://llvm.org/PR61563, which has caused numerous
workarounds in the library or places like std::pmr where we simply
don't use availability markup.

Instead, this moves to a new way of producing a guaranteed error
(at link time), which has the semantics we're looking for without
the limitations. The downside is that the diagnostic quality is a
lot better with an availability attribute than with this technique.

>From bff5920d1c92ebd3be355c009c0bd7497c5e9255 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 21 Oct 2026 17:03:41 -0400
Subject: [PATCH 1/2] [libc++] Clean up leftover std::print availability
 workarounds

The availability markup for std::print was removed in 4a87db6b10a0 and the
corresponding macros were deleted entirely in 1616fbaccf82. However, several
functions in <print> were left templates to work around https://llvm.org/PR61563,
which is not necessary anymore.

Remove a few workarounds that are now unnecessary, and reword others that
are required due to format now, not print.
---
 libcxx/include/__ostream/print.h                  | 10 +++++-----
 libcxx/include/print                              | 15 ++++++---------
 libcxx/utils/libcxx/test/features/availability.py |  8 --------
 3 files changed, 11 insertions(+), 22 deletions(-)

diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index f9810a4d7d26a0..7470e4e4e4efeb 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -29,7 +29,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #  if _LIBCPP_STD_VER >= 23
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void
 __vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
   // [ostream.formatted.print]/3
@@ -64,7 +64,7 @@ __vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool _
   }
 }
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args) {
   std::__vprint_nonunicode(__os, __fmt, __args, false);
 }
@@ -87,7 +87,7 @@ _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 #    if _LIBCPP_HAS_UNICODE
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
 #      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
@@ -126,7 +126,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
 #      endif   // _WIN32
 }
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(ostream& __os, string_view __fmt, format_args __args) {
   std::__vprint_unicode(__os, __fmt, __args, false);
 }
@@ -159,7 +159,7 @@ _LIBCPP_HIDE_FROM_ABI void println(ostream& __os, format_string<_Args...> __fmt,
 #    endif // _LIBCPP_HAS_UNICODE
 }
 
-template <class _Void = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class _Void = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void println(ostream& __os) {
   std::print(__os, (_Void(), "\n"));
 }
diff --git a/libcxx/include/print b/libcxx/include/print
index e62a28321d6ead..54888c239a5183 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -208,7 +208,6 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
   std::__throw_system_error(std::ferror(__stream), "failed to write formatted output");
 }
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void __output_nonunicode(FILE* __stream, string_view __text) {
   _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
   size_t __size = std::fwrite(__text.data(), 1, __text.size(), __stream);
@@ -225,7 +224,6 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_nonunicode(FILE* __stream, string_vie
 // test this code path.
 
 #      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 __output_unicode_windows([[maybe_unused]] FILE* __stream, string_view __text) {
   // 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.
@@ -253,7 +251,6 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode_windows([[maybe_unused]] FILE
 }
 #      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 __output_unicode([[maybe_unused]] FILE* __stream, string_view __text) {
   _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
 
@@ -331,12 +328,12 @@ println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, format_string<_Args...> __fmt,
 #    endif // _LIBCPP_HAS_UNICODE
 }
 
-template <class _Void = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class _Void = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream) {
   std::print(__stream, (_Void(), "\n"));
 }
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void println() {
   println(stdout);
 }
@@ -347,26 +344,26 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void println(format_string<_Args...>
 }
 
 #    if _LIBCPP_HAS_UNICODE
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void
 vprint_unicode(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
   __print::__output_unicode(__stream, std::vformat(__fmt, __args));
 }
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) 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);
 }
 
 #    endif // _LIBCPP_HAS_UNICODE
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void
 vprint_nonunicode(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
   __print::__output_nonunicode(__stream, std::vformat(__fmt, __args));
 }
 
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(string_view __fmt, format_args __args) {
   std::vprint_nonunicode(stdout, __fmt, __args);
 }
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index b07a0762357c1c..8ea76197446eee 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -179,14 +179,6 @@
             cfg.available_features,
         ),
     ),
-    # Tests that require support for <print> and std::print in <ostream> in the built library.
-    Feature(
-        name="availability-print-missing",
-        when=lambda cfg: BooleanExpression.evaluate(
-            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-18)",
-            cfg.available_features,
-        ),
-    ),
     # Tests that require time zone database support in the built library
     Feature(
         name="availability-tzdb-missing",

>From 904b0961ba717b70d3e9516adec2b96d44516263 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 21 Oct 2026 18:28:29 -0400
Subject: [PATCH 2/2] [libc++] Switch to a new technique for std::to_chars
 availability

This PR switches std::to_chars to a new technique for producing
errors when an API is used that isn't available on the deployment
target. Our current usage of availability attributes has well known
limitations like https://llvm.org/PR61563, which has caused numerous
workarounds in the library or places like std::pmr where we simply
don't use availability markup.

Instead, this moves to a new way of producing a guaranteed error
(at link time), which has the semantics we're looking for without
the limitations. The downside is that the diagnostic quality is a
lot better with an availability attribute than with this technique.
---
 .../__charconv/to_chars_floating_point.h      | 79 +++++++++++++++----
 libcxx/include/__configuration/availability.h |  1 -
 libcxx/include/__format/format_functions.h    | 12 +--
 libcxx/include/__ostream/print.h              | 10 +--
 libcxx/include/print                          | 12 +--
 .../ostream.syn/includes.compile.pass.cpp     |  2 -
 libcxx/test/libcxx/transitive_includes.gen.py |  2 +-
 .../utilities/format/nodiscard.verify.cpp     |  3 -
 .../print.fun/includes.compile.pass.cpp       |  2 -
 .../time.clock.local/ostream.verify.cpp       |  3 -
 .../bug_81590.compile.pass.cpp                |  2 -
 .../format.functions/format.locale.verify.cpp |  3 -
 .../format/format.functions/format.verify.cpp |  3 -
 .../format_to.locale.verify.cpp               |  3 -
 .../format.functions/format_to.verify.cpp     |  3 -
 .../format_to_n.locale.verify.cpp             |  3 -
 .../format.functions/format_to_n.verify.cpp   |  3 -
 .../formatted_size.locale.verify.cpp          |  3 -
 .../formatted_size.verify.cpp                 |  3 -
 ...d_width_precision_allowed_types.verify.cpp |  2 -
 .../format.functions.format.verify.cpp        |  3 -
 21 files changed, 79 insertions(+), 78 deletions(-)

diff --git a/libcxx/include/__charconv/to_chars_floating_point.h b/libcxx/include/__charconv/to_chars_floating_point.h
index ca997f85526f9d..4dab2cfa8b0a63 100644
--- a/libcxx/include/__charconv/to_chars_floating_point.h
+++ b/libcxx/include/__charconv/to_chars_floating_point.h
@@ -19,39 +19,86 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
-_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
-to_chars(char* __first, char* __last, float __value);
+#  if _LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT
+
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
-to_chars(char* __first, char* __last, double __value);
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result to_chars(char* __first, char* __last, float __value);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
-to_chars(char* __first, char* __last, long double __value);
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result to_chars(char* __first, char* __last, double __value);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
-to_chars(char* __first, char* __last, float __value, chars_format __fmt);
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result to_chars(char* __first, char* __last, long double __value);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
-to_chars(char* __first, char* __last, double __value, chars_format __fmt);
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result to_chars(char* __first, char* __last, float __value, chars_format __fmt);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result to_chars(char* __first, char* __last, double __value, chars_format __fmt);
+
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result
 to_chars(char* __first, char* __last, long double __value, chars_format __fmt);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result
 to_chars(char* __first, char* __last, float __value, chars_format __fmt, int __precision);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result
 to_chars(char* __first, char* __last, double __value, chars_format __fmt, int __precision);
 
-_LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI to_chars_result
+_LIBCPP_EXPORTED_FROM_ABI to_chars_result
 to_chars(char* __first, char* __last, long double __value, chars_format __fmt, int __precision);
-#endif // _LIBCPP_STD_VER >= 17
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+
+#  else
+
+// When the floating-point overloads of std::to_chars are not provided by the built library on the deployment
+// target, we define them as inline functions that forward to a function that doesn't exist anywhere. Using
+// them then results in a link-time error that mentions the cause of the problem. This is used instead of
+// availability attributes because Clang diagnoses them too eagerly (http://llvm.org/PR61563). With this approach,
+// there is only an error when the code is actually emitted.
+to_chars_result __to_chars_floating_point_is_not_available_on_this_deployment_target();
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, float) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, double) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, long double) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, float, chars_format) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, double, chars_format) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, long double, chars_format) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, float, chars_format, int) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, double, chars_format, int) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+_LIBCPP_HIDE_FROM_ABI inline to_chars_result to_chars(char*, char*, long double, chars_format, int) {
+  return std::__to_chars_floating_point_is_not_available_on_this_deployment_target();
+}
+
+#  endif // _LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT
+
+#endif // _LIBCPP_STD_VER >= 17
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___CHARCONV_TO_CHARS_FLOATING_POINT_H
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 805a9edab14bbf..06457703fe99d4 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -344,7 +344,6 @@
 // This controls the availability of floating-point std::to_chars functions.
 // These overloads were added later than the integer overloads.
 #define _LIBCPP_AVAILABILITY_HAS_TO_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_14
-#define _LIBCPP_AVAILABILITY_TO_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_14_ATTRIBUTE
 
 // Enable additional explicit instantiations of iostreams components. This
 // reduces the number of weak definitions generated in programs that use
diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h
index 8f214a493a49ec..29093340660457 100644
--- a/libcxx/include/__format/format_functions.h
+++ b/libcxx/include/__format/format_functions.h
@@ -489,8 +489,7 @@ template <class _CharT>
 }
 } // namespace __format
 
-// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
-// fires too eagerly, see http://llvm.org/PR61563.
+// This is a template to avoid instantiating the formatting machinery in every TU that includes this header.
 template <class = void>
 [[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI string vformat(string_view __fmt, format_args __args) {
   auto __result = __format::__try_constant_folding(__fmt, __args);
@@ -502,8 +501,7 @@ template <class = void>
 }
 
 #  if _LIBCPP_HAS_WIDE_CHARACTERS
-// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
-// fires too eagerly, see http://llvm.org/PR61563.
+// This is a template to avoid instantiating the formatting machinery in every TU that includes this header.
 template <class = void>
 [[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI wstring
 vformat(wstring_view __fmt, wformat_args __args) {
@@ -627,8 +625,7 @@ format_to(_OutIt __out_it, locale __loc, wformat_string<_Args...> __fmt, _Args&&
 }
 #    endif
 
-// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
-// fires too eagerly, see http://llvm.org/PR61563.
+// This is a template to avoid instantiating the formatting machinery in every TU that includes this header.
 template <class = void>
 [[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI string
 vformat(locale __loc, string_view __fmt, format_args __args) {
@@ -638,8 +635,7 @@ vformat(locale __loc, string_view __fmt, format_args __args) {
 }
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
-// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
-// fires too eagerly, see http://llvm.org/PR61563.
+// This is a template to avoid instantiating the formatting machinery in every TU that includes this header.
 template <class = void>
 [[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI wstring
 vformat(locale __loc, wstring_view __fmt, wformat_args __args) {
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 7470e4e4e4efeb..965a55cb079522 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -29,7 +29,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #  if _LIBCPP_STD_VER >= 23
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void
 __vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
   // [ostream.formatted.print]/3
@@ -64,7 +64,7 @@ __vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool _
   }
 }
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args) {
   std::__vprint_nonunicode(__os, __fmt, __args, false);
 }
@@ -87,7 +87,7 @@ _LIBCPP_EXPORTED_FROM_ABI FILE* __get_ostream_file(ostream& __os);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 #    if _LIBCPP_HAS_UNICODE
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
 #      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
@@ -126,7 +126,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
 #      endif   // _WIN32
 }
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(ostream& __os, string_view __fmt, format_args __args) {
   std::__vprint_unicode(__os, __fmt, __args, false);
 }
@@ -159,7 +159,7 @@ _LIBCPP_HIDE_FROM_ABI void println(ostream& __os, format_string<_Args...> __fmt,
 #    endif // _LIBCPP_HAS_UNICODE
 }
 
-template <class _Void = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class _Void = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void println(ostream& __os) {
   std::print(__os, (_Void(), "\n"));
 }
diff --git a/libcxx/include/print b/libcxx/include/print
index 54888c239a5183..13576634f99d8f 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -328,12 +328,12 @@ println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, format_string<_Args...> __fmt,
 #    endif // _LIBCPP_HAS_UNICODE
 }
 
-template <class _Void = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class _Void = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream) {
   std::print(__stream, (_Void(), "\n"));
 }
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void println() {
   println(stdout);
 }
@@ -344,26 +344,26 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void println(format_string<_Args...>
 }
 
 #    if _LIBCPP_HAS_UNICODE
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void
 vprint_unicode(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
   __print::__output_unicode(__stream, std::vformat(__fmt, __args));
 }
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(string_view __fmt, format_args __args) {
   std::vprint_unicode(stdout, __fmt, __args);
 }
 
 #    endif // _LIBCPP_HAS_UNICODE
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void
 vprint_nonunicode(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
   __print::__output_nonunicode(__stream, std::vformat(__fmt, __args));
 }
 
-template <class = void> // TODO: to_chars(floating-point) markup fires too eagerly (http://llvm.org/PR61563).
+template <class = void> // Template to avoid eagerly instantiating std::vformat, which is costly
 _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(string_view __fmt, format_args __args) {
   std::vprint_nonunicode(stdout, __fmt, __args);
 }
diff --git a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.syn/includes.compile.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.syn/includes.compile.pass.cpp
index 20b8d187c0acbd..6093038c1ba85b 100644
--- a/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.syn/includes.compile.pass.cpp
+++ b/libcxx/test/libcxx/input.output/iostream.format/output.streams/ostream.syn/includes.compile.pass.cpp
@@ -10,8 +10,6 @@
 // UNSUPPORTED: no-filesystem
 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
 
-// XFAIL: availability-fp_to_chars-missing
-
 // <ostream>
 
 // The Standard does indirectly require that <ostream> includes <format>.
diff --git a/libcxx/test/libcxx/transitive_includes.gen.py b/libcxx/test/libcxx/transitive_includes.gen.py
index 20d5e981ccba4f..f8aa826da7f772 100644
--- a/libcxx/test/libcxx/transitive_includes.gen.py
+++ b/libcxx/test/libcxx/transitive_includes.gen.py
@@ -41,7 +41,7 @@
     "future": "// UNSUPPORTED: no-threads, c++03",
     "latch": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "mutex": "// UNSUPPORTED: no-threads, c++03",
-    "print": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14, c++17, c++20, availability-fp_to_chars-missing",  # TODO PRINT investigate
+    "print": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14, c++17, c++20",
     "semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
     "stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
diff --git a/libcxx/test/libcxx/utilities/format/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/format/nodiscard.verify.cpp
index cf2a5602ef6a11..152dd3912d4349 100644
--- a/libcxx/test/libcxx/utilities/format/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/utilities/format/nodiscard.verify.cpp
@@ -8,9 +8,6 @@
 
 // Check that format functions are marked [[nodiscard]] as a conforming extension
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <format>
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/includes.compile.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/includes.compile.pass.cpp
index 7d17d5066613b7..d5d2bb2f5f35a4 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/includes.compile.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/includes.compile.pass.cpp
@@ -10,8 +10,6 @@
 // UNSUPPORTED: no-filesystem
 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
 
-// XFAIL: availability-fp_to_chars-missing
-
 // <print>
 
 // The Standard does indirectly require that <print> includes <format>. However
diff --git a/libcxx/test/std/time/time.clock/time.clock.local/ostream.verify.cpp b/libcxx/test/std/time/time.clock/time.clock.local/ostream.verify.cpp
index aa348b06947aad..47ca1d106061ff 100644
--- a/libcxx/test/std/time/time.clock/time.clock.local/ostream.verify.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.local/ostream.verify.cpp
@@ -10,9 +10,6 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // <chrono>
 
 // class system_clock;
diff --git a/libcxx/test/std/utilities/format/format.functions/bug_81590.compile.pass.cpp b/libcxx/test/std/utilities/format/format.functions/bug_81590.compile.pass.cpp
index 23dd9fe841b904..f64a2925f5bad3 100644
--- a/libcxx/test/std/utilities/format/format.functions/bug_81590.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/bug_81590.compile.pass.cpp
@@ -8,8 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// XFAIL: availability-fp_to_chars-missing
-
 // The sample code is based on the bug report
 // https://llvm.org/PR81590
 //
diff --git a/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp
index 63e6f8f65588aa..52e89343c6cdf8 100644
--- a/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format.locale.verify.cpp
@@ -9,9 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: no-localization
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.functions/format.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format.verify.cpp
index 9e1f86b474b868..443d35b15cc874 100644
--- a/libcxx/test/std/utilities/format/format.functions/format.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format.verify.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp
index 488110ab2aa2b8..1b323ea18ccda3 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.verify.cpp
@@ -9,9 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: no-localization
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp
index 671359a3469976..ee8672663f10eb 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to.verify.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // <format>
 
 // Basic test to validate ill-formed code is properly detected.
diff --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp
index ebe6ff081ac117..672ea43f962dc8 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.verify.cpp
@@ -9,9 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: no-localization
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp
index ccea8d4eea7cf9..7bf82e3e3cff5c 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.verify.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp
index d8374435bf1ac4..405be91eec999c 100644
--- a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.verify.cpp
@@ -9,9 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: no-localization
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp
index 8420a03c16d62d..0ceeee93c038c2 100644
--- a/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.verify.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 // Basic test to validate ill-formed code is properly detected.
 
 // <format>
diff --git a/libcxx/test/std/utilities/format/format.string/format.string.std/lwg3720_arg_id_width_precision_allowed_types.verify.cpp b/libcxx/test/std/utilities/format/format.string/format.string.std/lwg3720_arg_id_width_precision_allowed_types.verify.cpp
index b5a893fc1172f8..77ec1607d1f3b4 100644
--- a/libcxx/test/std/utilities/format/format.string/format.string.std/lwg3720_arg_id_width_precision_allowed_types.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.string/format.string.std/lwg3720_arg_id_width_precision_allowed_types.verify.cpp
@@ -8,8 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// XFAIL: availability-fp_to_chars-missing
-
 // <format>
 
 // [format.string.std]/8
diff --git a/libcxx/test/std/utilities/format/format.tuple/format.functions.format.verify.cpp b/libcxx/test/std/utilities/format/format.tuple/format.functions.format.verify.cpp
index 3d7cdaa475db07..27a56a22e385e4 100644
--- a/libcxx/test/std/utilities/format/format.tuple/format.functions.format.verify.cpp
+++ b/libcxx/test/std/utilities/format/format.tuple/format.functions.format.verify.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
 #include <format>
 
 #include <utility>



More information about the libcxx-commits mailing list