]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Implement P3235R3 optimizations for std::print [PR121790]
authorTomasz Kamiński <tkaminsk@redhat.com>
Thu, 2 Oct 2025 14:17:05 +0000 (16:17 +0200)
committerTomasz Kamiński <tkaminsk@redhat.com>
Fri, 10 Oct 2025 07:13:00 +0000 (09:13 +0200)
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.

Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
17 files changed:
libstdc++-v3/include/bits/chrono_io.h
libstdc++-v3/include/bits/version.def
libstdc++-v3/include/bits/version.h
libstdc++-v3/include/std/format
libstdc++-v3/include/std/queue
libstdc++-v3/include/std/stack
libstdc++-v3/include/std/stacktrace
libstdc++-v3/include/std/thread
libstdc++-v3/include/std/vector
libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
libstdc++-v3/testsuite/30_threads/thread/id/output.cc
libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
libstdc++-v3/testsuite/std/format/ranges/formatter.cc
libstdc++-v3/testsuite/std/format/tuple.cc
libstdc++-v3/testsuite/std/time/format/custom_rep.h [new file with mode: 0644]
libstdc++-v3/testsuite/std/time/format/empty_spec.cc
libstdc++-v3/testsuite/std/time/format/nonlocking.cc [new file with mode: 0644]

index 1e2f45b0bf8109b629ca3586bceaabf3230753b7..df047f1103bdd0ee03ae0250983c16b046da64e6 100644 (file)
@@ -2234,6 +2234,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>
     {
@@ -2270,6 +2277,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>
     {
@@ -2308,6 +2321,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>
     {
@@ -2344,6 +2363,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>
     {
@@ -2382,6 +2407,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>
     {
@@ -2420,6 +2451,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>
     {
@@ -2458,6 +2495,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>
     {
@@ -2497,6 +2540,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>
     {
@@ -2535,6 +2584,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>
     {
@@ -2574,6 +2629,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>
     {
@@ -2613,6 +2674,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>
     {
@@ -2651,6 +2718,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>
     {
@@ -2693,6 +2766,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>
     {
@@ -2740,6 +2819,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>
     {
@@ -2795,6 +2880,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>
     {
@@ -2846,6 +2937,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>
     {
@@ -2890,6 +2987,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>
@@ -2908,6 +3012,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>
     {
@@ -2924,6 +3034,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>
@@ -2962,6 +3078,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>
     {
@@ -3006,6 +3129,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>
     {
@@ -3041,6 +3171,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>
     {
@@ -3076,6 +3213,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>
     {
@@ -3111,6 +3255,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>
     {
@@ -3145,6 +3296,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>
     {
@@ -3205,6 +3363,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>
@@ -3224,6 +3389,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
index 1c0f43e465b50a0763535b428039f91bf631df98..83f1817bf8e4a54ede5b27638ff0532b2dab6be0 100644 (file)
@@ -1865,7 +1865,7 @@ ftms = {
 ftms = {
   name = print;
   values = {
-    v = 202403;
+    v = 202406;
     cxxmin = 23;
     hosted = yes;
   };
index 7b97accc47e12ba798c8155e7953dd3c58210e8d..0d6692d244a675314a48a80d094adbee67287c81 100644 (file)
 
 #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) */
index 1d01bc39e9c38686502f01dc38608ae2f9d45383..ad29f9336e8f667d5dd3dcca941dcca6c79e51b4 100644 (file)
@@ -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
 
index 1b76088b31b3da956e5ff83af5db94b51f7bf379..ade09f42ea8aed97c4298862c8292911696d61eb 100644 (file)
@@ -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
index a57a5a08bc3c3ccdc20ab13f92572e5962f79348..88bc0d2cb6bf571f05c02d3a05593792a8bc17d5 100644 (file)
@@ -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
index 491122293c5f094426a963b2c3a45b53108e9ba9..01e18ba171c0e5c2c4e8f46ef67d65d16b480d86 100644 (file)
@@ -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
index 94ded714e9e0369a3892864ab44ecef18d33095d..ccab1e44fbc4ae4d29e9d6eb4ee6617458469e00 100644 (file)
@@ -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
index 3146f283944affb4d8601d9364d4c844ca3e37a6..920f852f79448fe1c434285fc8aca51e80462614 100644 (file)
@@ -169,6 +169,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
index cecc535f15f10c2b69b5195ad4f6150cc479709e..833727f4b418e634489bf0c418154e39b6b1fb02 100644 (file)
@@ -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
index 3d1dd38d998ff574de17b3b8ba07d1b5815bb786..c3e0d421d19f3f30a13b658bf15ab77e2f1ff99b 100644 (file)
@@ -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();
index a4e2dfb3321e6adf2d5c6a51c589e8d2ed73ea51..d9fc01ab8930111f4942d0844f93708e66bd7813 100644 (file)
@@ -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>
index d3e089767bfedde45bb46edca00b72cbbf1ba258..a50c5b1033fb1260bb8fb1ee6b544fecf8f45470 100644 (file)
@@ -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>();
 }
index ba6dae8935b68457664b14b9292eb370ccc6da15..001235ba6430303fecfd081f15e6d3fa895f1c61 100644 (file)
@@ -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 (file)
index 0000000..8363eaa
--- /dev/null
@@ -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;
+  }
+};
+
index a20c074018e8b83ea84c491bc8dec8e71c000604..b84f84a3069fb3bf06b32af6a0d0e3cfedf153bf 100644 (file)
@@ -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 (file)
index 0000000..c7aac75
--- /dev/null
@@ -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
+