This patch implements additional enable_nonlocking_formatter_optimization
specializations listed in P3235R3.

        PR libstdc++/121790

libstdc++-v3/ChangeLog:

        * include/bits/chrono_io.h
        (enable_nonlocking_formatter_optimization): Define specializations
        for chrono types.
        * include/bits/version.def (print): Bump.
        * include/bits/version.h: Regenerate.
        * include/std/format (enable_nonlocking_formatter_optimization):
        Define specializations for pair, tuple and ranges.
        * include/std/queue (enable_nonlocking_formatter_optimization):
        Define specializations for queue and priority_queue.
        * include/std/stack (enable_nonlocking_formatter_optimization):
        Define specialization for stack.
        * include/std/stacktrace (enable_nonlocking_formatter_optimization):
        Define specialization for basic_stacktrace and stacktrace_entry.
        * include/std/thread (enable_nonlocking_formatter_optimization):
        Define specialization for thread::id.
        * include/std/vector (enable_nonlocking_formatter_optimization):
        Define specialization for vector<bool>::reference.
        * testsuite/23_containers/vector/bool/format.cc: Test value of
        enable_nonlocking_formatter_optimization.
        * testsuite/30_threads/thread/id/output.cc: Likewise.
        * testsuite/std/format/ranges/adaptors.cc: Likewise.
        * testsuite/std/format/ranges/formatter.cc: Likewise.
        * testsuite/std/format/tuple.cc: Likewise.
        * testsuite/std/time/format/empty_spec.cc: Extract Rep class
        to custom_rep.h.
        * testsuite/std/time/format/custom_rep.h: Extracted from
        empty_spec.cc.
        * testsuite/std/time/format/nonlocking.cc: New test.
---
This patch tested on x86_64-linux locally. OK for trunk?

 libstdc++-v3/include/bits/chrono_io.h         | 172 ++++++++++++++++++
 libstdc++-v3/include/bits/version.def         |   2 +-
 libstdc++-v3/include/bits/version.h           |   4 +-
 libstdc++-v3/include/std/format               |  22 +++
 libstdc++-v3/include/std/queue                |  15 ++
 libstdc++-v3/include/std/stack                |   7 +
 libstdc++-v3/include/std/stacktrace           |  12 ++
 libstdc++-v3/include/std/thread               |   7 +
 libstdc++-v3/include/std/vector               |   7 +
 .../23_containers/vector/bool/format.cc       |   1 +
 .../testsuite/30_threads/thread/id/output.cc  |   5 +-
 .../testsuite/std/format/ranges/adaptors.cc   |  10 +
 .../testsuite/std/format/ranges/formatter.cc  |  14 +-
 libstdc++-v3/testsuite/std/format/tuple.cc    |  37 ++++
 .../testsuite/std/time/format/custom_rep.h    |  92 ++++++++++
 .../testsuite/std/time/format/empty_spec.cc   |  89 +--------
 .../testsuite/std/time/format/nonlocking.cc   | 164 +++++++++++++++++
 17 files changed, 567 insertions(+), 93 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/time/format/custom_rep.h
 create mode 100644 libstdc++-v3/testsuite/std/time/format/nonlocking.cc

diff --git a/libstdc++-v3/include/bits/chrono_io.h 
b/libstdc++-v3/include/bits/chrono_io.h
index 79a44d128b1..1540f53c21a 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -2232,6 +2232,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<typename _Rep, typename _Period>
+    constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::duration<_Rep, _Period>>
+      = enable_nonlocking_formatter_optimization<_Rep>;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::day, _CharT>
     {
@@ -2268,6 +2275,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::day> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::month, _CharT>
     {
@@ -2306,6 +2319,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::month> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::year, _CharT>
     {
@@ -2342,6 +2361,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::year> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::weekday, _CharT>
     {
@@ -2380,6 +2405,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::weekday> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::weekday_indexed, _CharT>
     {
@@ -2418,6 +2449,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::weekday_indexed> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::weekday_last, _CharT>
     {
@@ -2456,6 +2493,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::weekday_last> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::month_day, _CharT>
     {
@@ -2495,6 +2538,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::month_day> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::month_day_last, _CharT>
     {
@@ -2533,6 +2582,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::month_day_last> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::month_weekday, _CharT>
     {
@@ -2572,6 +2627,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::month_weekday> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::month_weekday_last, _CharT>
     {
@@ -2611,6 +2672,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::month_weekday_last> = 
true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::year_month, _CharT>
     {
@@ -2649,6 +2716,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::year_month> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::year_month_day, _CharT>
     {
@@ -2692,6 +2765,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::year_month_day> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::year_month_day_last, _CharT>
     {
@@ -2741,6 +2820,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::year_month_day_last> = 
true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::year_month_weekday, _CharT>
     {
@@ -2792,6 +2877,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::year_month_weekday> = 
true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::year_month_weekday_last, _CharT>
     {
@@ -2843,6 +2934,12 @@ namespace __format
       __format::__formatter_chrono<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::year_month_weekday_last> 
= true;
+#endif
+
   template<typename _Rep, typename _Period, __format::__char _CharT>
     struct formatter<chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>, _CharT>
     {
@@ -2887,6 +2984,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<_Duration>>
+        = true;
+#endif
+
 #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
   template<__format::__char _CharT>
     struct formatter<chrono::sys_info, _CharT>
@@ -2905,6 +3009,12 @@ namespace __format
       __format::__formatter_chrono_info<_CharT> _M_f;
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::sys_info> = true;
+#endif
+
   template<__format::__char _CharT>
     struct formatter<chrono::local_info, _CharT>
     {
@@ -2921,6 +3031,12 @@ namespace __format
     private:
       __format::__formatter_chrono_info<_CharT> _M_f;
     };
+
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<chrono::local_info> = true;
+#endif
 #endif
 
   template<typename _Duration, __format::__char _CharT>
@@ -2959,6 +3075,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::sys_time<_Duration>>
+       = true;
+#endif
+
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::utc_time<_Duration>, _CharT>
     {
@@ -3003,6 +3126,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::utc_time<_Duration>>
+       = true;
+#endif
+
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::tai_time<_Duration>, _CharT>
     {
@@ -3038,6 +3168,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::tai_time<_Duration>>
+       = true;
+#endif
+
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::gps_time<_Duration>, _CharT>
     {
@@ -3073,6 +3210,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::gps_time<_Duration>>
+       = true;
+#endif
+
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::file_time<_Duration>, _CharT>
     {
@@ -3108,6 +3252,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
      };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::file_time<_Duration>>
+        = true;
+#endif
+
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::local_time<_Duration>, _CharT>
     {
@@ -3142,6 +3293,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<chrono::local_time<_Duration>>
+        = true;
+#endif
+
   template<typename _Duration, __format::__char _CharT>
     struct formatter<chrono::__detail::__local_time_fmt<_Duration>, _CharT>
     {
@@ -3202,6 +3360,13 @@ namespace __format
       __format::__formatter_duration<_CharT> _M_f{__defSpec};
     };
 
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<
+       chrono::__detail::__local_time_fmt<_Duration>> = true;
+#endif
+
 #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
   template<typename _Duration, typename _TimeZonePtr, __format::__char _CharT>
     struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT>
@@ -3221,6 +3386,13 @@ namespace __format
          return _Base::format(__lf, __fc);
        }
     };
+
+#if __glibcxx_print >= 202406L
+   template<typename _Duration>
+     constexpr bool
+     enable_nonlocking_formatter_optimization<
+        chrono::zoned_time<_Duration, const chrono::time_zone*>> = true;
+#endif
 #endif
 
 namespace chrono
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index dc844811a73..d27a457c663 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1846,7 +1846,7 @@ ftms = {
 ftms = {
   name = print;
   values = {
-    v = 202403;
+    v = 202406;
     cxxmin = 23;
     hosted = yes;
   };
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 56730c626d4..60d0e79c4eb 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2065,9 +2065,9 @@
 
 #if !defined(__cpp_lib_print)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
-#  define __glibcxx_print 202403L
+#  define __glibcxx_print 202406L
 #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_print)
-#   define __cpp_lib_print 202403L
+#   define __cpp_lib_print 202406L
 #  endif
 # endif
 #endif /* !defined(__cpp_lib_print) && defined(__glibcxx_want_print) */
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 1d01bc39e9c..ad29f9336e8 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -5872,6 +5872,14 @@ namespace __format
        { return this->_M_format_elems(__p.first, __p.second, __fc); }
     };
 
+#if __glibcxx_print >= 202406L
+  template<typename _Fp, typename _Sp>
+    constexpr bool enable_nonlocking_formatter_optimization<pair<_Fp, _Sp>>
+    // TODO this should have remove_cvref_t.
+      = enable_nonlocking_formatter_optimization<_Fp>
+        && enable_nonlocking_formatter_optimization<_Sp>;
+#endif
+
   template<__format::__char _CharT, formattable<_CharT>... _Tps>
     struct formatter<tuple<_Tps...>, _CharT>
       : __format::__tuple_formatter<_CharT, remove_cvref_t<_Tps>...>
@@ -5890,6 +5898,13 @@ namespace __format
        { return this->_M_format(__t, index_sequence_for<_Tps...>(), __fc); }
     };
 
+#if __glibcxx_print >= 202406L
+  template<typename... _Tps>
+    // TODO this should have remove_cvref_t.
+    constexpr bool enable_nonlocking_formatter_optimization<tuple<_Tps...>>
+      = (enable_nonlocking_formatter_optimization<_Tps> && ...);
+#endif
+
   // [format.range.formatter], class template range_formatter
   template<typename _Tp, __format::__char _CharT>
     requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>
@@ -6184,6 +6199,13 @@ namespace __format
                          range_formatter<_Vt, _CharT>>;
       _Formatter_under _M_under;
     };
+
+#if __glibcxx_print >= 202406L
+  template<ranges::input_range _Rg>
+    requires (format_kind<_Rg> != range_format::disabled)
+    constexpr bool enable_nonlocking_formatter_optimization<_Rg> = false;
+#endif
+
 #endif // C++23 formatting ranges
 #undef _GLIBCXX_WIDEN
 
diff --git a/libstdc++-v3/include/std/queue b/libstdc++-v3/include/std/queue
index 1b76088b31b..ade09f42ea8 100644
--- a/libstdc++-v3/include/std/queue
+++ b/libstdc++-v3/include/std/queue
@@ -112,6 +112,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       range_formatter<_Tp, _CharT> _M_f;
     };
 
+#if __glibcxx_print >= 202406L
+  template<typename _Tp, typename _Container>
+    constexpr bool
+    // TODO should be false
+    enable_nonlocking_formatter_optimization<queue<_Tp, _Container>> = true;
+#endif
+
   template<__format::__char _CharT, typename _Tp,
           formattable<_CharT> _Container, typename _Compare>
     struct formatter<priority_queue<_Tp, _Container, _Compare>, _CharT>
@@ -146,6 +153,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       range_formatter<_Tp, _CharT> _M_f;
     };
 
+#if __glibcxx_print >= 202406L
+  template<typename _Tp, typename _Container, typename _Comparator>
+    constexpr bool
+    // TODO should be false
+    enable_nonlocking_formatter_optimization<
+      priority_queue<_Tp, _Container, _Comparator>> = true;
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // __glibcxx_format_ranges
diff --git a/libstdc++-v3/include/std/stack b/libstdc++-v3/include/std/stack
index a57a5a08bc3..88bc0d2cb6b 100644
--- a/libstdc++-v3/include/std/stack
+++ b/libstdc++-v3/include/std/stack
@@ -105,6 +105,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Standard uses formatter<ref_view<_Container>, _CharT>.
       range_formatter<_Tp, _CharT> _M_f;
     };
+
+#if __glibcxx_print >= 202406L
+  template<typename _Tp, typename _Container>
+    constexpr bool
+    // TODO should be false
+    enable_nonlocking_formatter_optimization<stack<_Tp, _Container>> = true;
+#endif
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // __glibcxx_format_ranges
diff --git a/libstdc++-v3/include/std/stacktrace 
b/libstdc++-v3/include/std/stacktrace
index 491122293c5..01e18ba171c 100644
--- a/libstdc++-v3/include/std/stacktrace
+++ b/libstdc++-v3/include/std/stacktrace
@@ -765,6 +765,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __format::_Spec<char> _M_spec;
     };
 
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<stacktrace_entry> = true;
+#endif
+
   template<typename _Allocator>
     class formatter<basic_stacktrace<_Allocator>>
     {
@@ -790,6 +796,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
     };
 
+#if __glibcxx_print >= 202406L
+  template<typename _Allocator>
+    constexpr bool
+    enable_nonlocking_formatter_optimization<basic_stacktrace<_Allocator>> = 
true;
+#endif
+
   namespace pmr
   {
     using stacktrace
diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread
index 94ded714e9e..ccab1e44fbc 100644
--- a/libstdc++-v3/include/std/thread
+++ b/libstdc++-v3/include/std/thread
@@ -384,6 +384,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     private:
       __format::_Spec<_CharT> _M_spec;
     };
+
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<thread::id> = true;
+#endif
+
 #endif // __cpp_lib_formatters
 
   /// @} group threads
diff --git a/libstdc++-v3/include/std/vector b/libstdc++-v3/include/std/vector
index cdc30cbff6d..846c12cbacd 100644
--- a/libstdc++-v3/include/std/vector
+++ b/libstdc++-v3/include/std/vector
@@ -171,6 +171,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     private:
       __format::__formatter_int<_CharT> _M_f;
     };
+
+#if __glibcxx_print >= 202406L
+  template<>
+    inline constexpr bool
+    enable_nonlocking_formatter_optimization<_GLIBCXX_STD_C::_Bit_reference> = 
true;
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // __glibcxx_format_ranges
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc 
b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
index cecc535f15f..833727f4b41 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
@@ -7,6 +7,7 @@
 
 static_assert(!std::formattable<std::vector<bool>::reference, int>);
 static_assert(!std::formattable<std::vector<bool>::reference, char32_t>);
+static_assert(std::enable_nonlocking_formatter_optimization<std::vector<bool>::reference>);
 
 template<typename... Args>
 bool
diff --git a/libstdc++-v3/testsuite/30_threads/thread/id/output.cc 
b/libstdc++-v3/testsuite/30_threads/thread/id/output.cc
index 3d1dd38d998..c3e0d421d19 100644
--- a/libstdc++-v3/testsuite/30_threads/thread/id/output.cc
+++ b/libstdc++-v3/testsuite/30_threads/thread/id/output.cc
@@ -81,7 +81,6 @@ void
 test02()
 {
 #if __cpp_lib_formatters >= 202302
-
   static_assert( 
std::is_default_constructible_v<std::formatter<std::thread::id, char>> );
 
   std::thread t1([]{});
@@ -155,6 +154,10 @@ test02()
 #endif
 }
 
+#if __cplusplus >= 202302L
+static_assert(std::enable_nonlocking_formatter_optimization<std::thread::id>);
+#endif
+
 int main()
 {
   test01();
diff --git a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc 
b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
index a4e2dfb3321..d9fc01ab893 100644
--- a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
+++ b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
@@ -121,6 +121,16 @@ test_output()
 
   // Formatter check if container is formattable, not container elements.
   static_assert(!std::formattable<Adaptor<int, NotFormattableCont<int>>, 
CharT>);
+
+  // TODO should be false
+  static_assert(std::enable_nonlocking_formatter_optimization<
+                 Adaptor<int>>);
+  static_assert(std::enable_nonlocking_formatter_optimization<
+                 Adaptor<MutFormat>>);
+  static_assert(std::enable_nonlocking_formatter_optimization<
+                 Adaptor<int, std::deque<int>>>);
+  static_assert(std::enable_nonlocking_formatter_optimization<
+                 Adaptor<int, NotFormattableCont<int>>>);
 }
 
 template<template<typename Tp, typename Cont = std::vector<Tp>> class Adaptor>
diff --git a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc 
b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc
index d3e089767bf..a50c5b1033f 100644
--- a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc
+++ b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc
@@ -4,6 +4,7 @@
 #include <format>
 #include <testsuite_hooks.h>
 #include <vector>
+#include <span>
 
 #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
 #define WIDEN(S) WIDEN_(CharT, S)
@@ -145,7 +146,7 @@ struct MyFlatMap : std::flat_map<int, int>
 
 template<typename CharT>
 struct std::formatter<MyFlatMap, CharT>
-  // This cannot apply format BitVector const&, because formatted type would
+  // We cannot format MyFlatMap const&, because formatted type would
   // be std::pair<int const&, int const&>, and formatter for
   // pair<int const&, int> cannot format it.
   : std::range_formatter<MyFlatMap::reference>
@@ -161,10 +162,21 @@ void test_const_ref_type_mismatch()
 template<typename T, typename CharT>
 using VectorFormatter = std::formatter<std::vector<T>, CharT>;
 
+template<template<typename> typename Range>
+void test_nonblocking()
+{
+  static_assert(!std::enable_nonlocking_formatter_optimization<
+                 Range<int>>);
+}
+
 int main()
 {
   test_outputs<std::range_formatter>();
   test_outputs<VectorFormatter>();
   test_nested();
   test_const_ref_type_mismatch();
+
+  test_nonblocking<std::span>();
+  test_nonblocking<std::vector>();
+  test_nonblocking<MyVector>();
 }
diff --git a/libstdc++-v3/testsuite/std/format/tuple.cc 
b/libstdc++-v3/testsuite/std/format/tuple.cc
index ba6dae8935b..001235ba643 100644
--- a/libstdc++-v3/testsuite/std/format/tuple.cc
+++ b/libstdc++-v3/testsuite/std/format/tuple.cc
@@ -341,6 +341,40 @@ void test_padding()
   VERIFY( check_elems(resv) );
 }
 
+struct Custom {};
+
+template<typename CharT>
+struct std::formatter<Custom, CharT>
+{
+  constexpr std::basic_format_parse_context<CharT>::iterator
+  parse(const std::basic_format_parse_context<CharT>& pc)
+  { return pc.begin();  }
+
+  template<typename Out>
+  typename std::basic_format_context<Out, CharT>::iterator
+  format(Custom, const std::basic_format_context<Out, CharT>& fc) const
+  { return fc.out(); }
+};
+
+template<template<typename...> typename Tuple>
+void test_nonblocking()
+{
+  static_assert(std::enable_nonlocking_formatter_optimization<
+                 Tuple<int, float>>);
+  // TODO missing remove_cv_ref
+  static_assert(!std::enable_nonlocking_formatter_optimization<
+                 Tuple<const int, const float>>);
+  static_assert(!std::enable_nonlocking_formatter_optimization<
+                 Tuple<int&, float&>>);
+
+  static_assert(!std::enable_nonlocking_formatter_optimization<
+                 Tuple<Custom, float>>);
+  static_assert(!std::enable_nonlocking_formatter_optimization<
+                 Tuple<const Custom, const float>>);
+  static_assert(!std::enable_nonlocking_formatter_optimization<
+                 Tuple<Custom&, float&>>);
+}
+
 int main()
 {
   test_format_string();
@@ -348,4 +382,7 @@ int main()
   test_outputs<wchar_t>();
   test_nested();
   test_padding();
+
+  test_nonblocking<std::pair>();
+  test_nonblocking<std::tuple>();
 }
diff --git a/libstdc++-v3/testsuite/std/time/format/custom_rep.h 
b/libstdc++-v3/testsuite/std/time/format/custom_rep.h
new file mode 100644
index 00000000000..8363eaafebc
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/format/custom_rep.h
@@ -0,0 +1,92 @@
+#include <chrono>
+#include <ostream>
+
+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
+#define WIDEN(S) WIDEN_(CharT, S)
+
+template<typename Ret = void, typename Under = long>
+struct Rep
+{
+  using Return
+    = std::conditional_t<std::is_void_v<Ret>, Rep, Ret>;
+
+  Rep(Under v = 0) : val(v) {}
+
+  template<typename ORet, typename OUnder>
+  Rep(Rep<ORet, OUnder> o) : val(o.val) {}
+
+  operator Under() const
+  { return val; }
+
+  Return
+  operator+() const
+  { return val; }
+
+  Rep
+  operator-() const
+  { return -val; }
+
+  friend Rep
+  operator+(Rep lhs, Rep rhs)
+  { return lhs.val + rhs.val; }
+
+  friend Rep
+  operator-(Rep lhs, Rep rhs)
+  { return lhs.val - rhs.val; }
+
+  friend Rep
+  operator*(Rep lhs, Rep rhs)
+  { return lhs.val * rhs.val; }
+
+  friend Rep
+  operator/(Rep lhs, Rep rhs)
+  { return lhs.val / rhs.val; }
+
+  friend auto operator<=>(Rep, Rep) = default;
+
+  template<typename CharT>
+  friend std::basic_ostream<CharT>&
+  operator<<(std::basic_ostream<CharT>& os, const Rep& t)
+  { return os << t.val << WIDEN("[via <<]"); }
+
+  Under val;
+};
+
+template<typename Ret, typename Under1, typename Under2>
+struct std::common_type<Rep<Ret, Under1>, Rep<Ret, Under2>>
+{
+  using type = Rep<Ret, std::common_type_t<Under1, Under2>>;
+};
+
+template<typename Ret, typename Under, typename Other>
+  requires std::is_integral_v<Other>
+struct std::common_type<Rep<Ret, Under>, Other>
+{
+  using type = Rep<Ret, std::common_type_t<Under, Other>>;
+};
+
+template<typename Ret, typename Under, typename Other>
+  requires std::is_integral_v<Other>
+struct std::common_type<Other, Rep<Ret, Under>>
+  : std::common_type<Rep<Ret, Under>, Other>
+{ };
+
+template<typename Ret, typename Under>
+struct std::numeric_limits<Rep<Ret, Under>>
+  : std::numeric_limits<Under>
+{ };
+
+template<typename Ret, typename Under, typename CharT>
+struct std::formatter<Rep<Ret, Under>, CharT>
+  : std::formatter<Under, CharT>
+{
+  template<typename Out>
+  typename std::basic_format_context<Out, CharT>::iterator
+  format(const Rep<Ret>& t, std::basic_format_context<Out, CharT>& ctx) const
+  {
+    constexpr std::basic_string_view<CharT> suffix = WIDEN("[via format]");
+    auto out = std::formatter<Under, CharT>::format(t.val, ctx);
+    return std::ranges::copy(suffix, out).out;
+  }
+};
+
diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc 
b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
index a20c074018e..b84f84a3069 100644
--- a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
+++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
@@ -6,11 +6,10 @@
 #include <ranges>
 #include <sstream>
 #include <testsuite_hooks.h>
+#include "custom_rep.h"
 
 using namespace std::chrono;
 
-#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
-#define WIDEN(S) WIDEN_(CharT, S)
 
 template<typename CharT, typename T>
 void
@@ -77,92 +76,6 @@ test_padding()
   VERIFY( res == WIDEN("==16 is not a valid month==") );
 }
 
-template<typename Ret = void, typename Under = long>
-struct Rep
-{
-  using Return
-    = std::conditional_t<std::is_void_v<Ret>, Rep, Ret>;
-
-  Rep(Under v = 0) : val(v) {}
-
-  template<typename ORet, typename OUnder>
-  Rep(Rep<ORet, OUnder> o) : val(o.val) {}
-
-  operator Under() const
-  { return val; }
-
-  Return
-  operator+() const
-  { return val; }
-
-  Rep
-  operator-() const
-  { return -val; }
-
-  friend Rep
-  operator+(Rep lhs, Rep rhs)
-  { return lhs.val + rhs.val; }
-
-  friend Rep
-  operator-(Rep lhs, Rep rhs)
-  { return lhs.val - rhs.val; }
-
-  friend Rep
-  operator*(Rep lhs, Rep rhs)
-  { return lhs.val * rhs.val; }
-
-  friend Rep
-  operator/(Rep lhs, Rep rhs)
-  { return lhs.val / rhs.val; }
-
-  friend auto operator<=>(Rep, Rep) = default;
-
-  template<typename CharT>
-  friend std::basic_ostream<CharT>&
-  operator<<(std::basic_ostream<CharT>& os, const Rep& t)
-  { return os << t.val << WIDEN("[via <<]"); }
-
-  Under val;
-};
-
-template<typename Ret, typename Under1, typename Under2>
-struct std::common_type<Rep<Ret, Under1>, Rep<Ret, Under2>>
-{
-  using type = Rep<Ret, std::common_type_t<Under1, Under2>>;
-};
-
-template<typename Ret, typename Under, typename Other>
-  requires std::is_integral_v<Other>
-struct std::common_type<Rep<Ret, Under>, Other>
-{
-  using type = Rep<Ret, std::common_type_t<Under, Other>>;
-};
-
-template<typename Ret, typename Under, typename Other>
-  requires std::is_integral_v<Other>
-struct std::common_type<Other, Rep<Ret, Under>>
-  : std::common_type<Rep<Ret, Under>, Other>
-{ };
-
-template<typename Ret, typename Under>
-struct std::numeric_limits<Rep<Ret, Under>>
-  : std::numeric_limits<Under>
-{ };
-
-template<typename Ret, typename Under, typename CharT>
-struct std::formatter<Rep<Ret, Under>, CharT>
-  : std::formatter<Under, CharT>
-{
-  template<typename Out>
-  typename std::basic_format_context<Out, CharT>::iterator
-  format(const Rep<Ret>& t, std::basic_format_context<Out, CharT>& ctx) const
-  {
-    constexpr std::basic_string_view<CharT> suffix = WIDEN("[via format]");
-    auto out = std::formatter<Under, CharT>::format(t.val, ctx);
-    return std::ranges::copy(suffix, out).out;
-  }
-};
-
 using deciseconds = duration<seconds::rep, std::deci>;
 
 template<typename CharT>
diff --git a/libstdc++-v3/testsuite/std/time/format/nonlocking.cc 
b/libstdc++-v3/testsuite/std/time/format/nonlocking.cc
new file mode 100644
index 00000000000..c7aac75cb83
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/time/format/nonlocking.cc
@@ -0,0 +1,164 @@
+// { dg-do compile { target c++23 } }
+
+#include <format>
+#include <chrono>
+#include "custom_rep.h"
+
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::day>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::month>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::year>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::weekday>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::weekday_indexed>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::weekday_last>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::month_day>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::month_day_last>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::month_weekday>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::month_weekday_last>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::year_month>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::year_month_day>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::year_month_day_last>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::year_month_weekday>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::year_month_weekday_last>);
+
+#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::local_info>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::sys_info>);
+#endif
+
+template<typename Duration>
+using local_time_fmt 
+  = 
decltype(std::chrono::local_time_format(std::chrono::local_time<Duration>{}));
+ 
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::seconds>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::duration<float>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::duration<long long, std::mega>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::local_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::sys_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::utc_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::gps_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::tai_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::file_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               local_time_fmt<std::chrono::seconds>>);
+
+using BufferedDuration = std::chrono::duration<Rep<void, int>>;
+
+static_assert(!std::enable_nonlocking_formatter_optimization<
+               BufferedDuration>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::local_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::sys_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::utc_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::gps_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::tai_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::file_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               local_time_fmt<BufferedDuration>>);
+
+template<>
+inline constexpr bool
+  std::enable_nonlocking_formatter_optimization<Rep<void, long>> = true;
+
+using NonBufferedRep = std::chrono::duration<Rep<void, long>>;
+
+static_assert(std::enable_nonlocking_formatter_optimization<
+               NonBufferedRep>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::local_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::sys_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::utc_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::gps_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::tai_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::file_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               local_time_fmt<NonBufferedRep>>);
+
+using NonBufferedDuration = std::chrono::duration<Rep<void, short>>;
+
+template<>
+inline constexpr bool
+  std::enable_nonlocking_formatter_optimization<NonBufferedDuration> = true;
+
+static_assert(std::enable_nonlocking_formatter_optimization<
+               NonBufferedDuration>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::local_time<NonBufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::sys_time<NonBufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::utc_time<NonBufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::gps_time<NonBufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::tai_time<NonBufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::file_time<NonBufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               local_time_fmt<NonBufferedDuration>>);
+
+#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<std::chrono::seconds>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<BufferedDuration>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<NonBufferedRep>>);
+static_assert(std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<NonBufferedDuration>>);
+
+struct MyTimeZone : std::chrono::time_zone
+{};
+
+template<>
+struct std::chrono::zoned_traits<MyTimeZone>
+{
+  static const MyTimeZone* default_zone();
+  static const MyTimeZone* locate_zone(std::string_view name);
+};     
+
+static_assert(!std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<std::chrono::seconds, MyTimeZone>>);
+static_assert(!std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<BufferedDuration, MyTimeZone>>);
+static_assert(!std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<NonBufferedRep, MyTimeZone>>);
+static_assert(!std::enable_nonlocking_formatter_optimization<
+               std::chrono::zoned_time<NonBufferedDuration, MyTimeZone>>);
+#endif
+
-- 
2.51.0

Reply via email to