https://gcc.gnu.org/g:c8b388a94890b06d58a290dd39b9023cc4383c80
commit r16-4351-gc8b388a94890b06d58a290dd39b9023cc4383c80 Author: Tomasz KamiĆski <[email protected]> Date: Thu Oct 2 16:17:05 2025 +0200 libstdc++: Implement P3235R3 optimizations for std::print [PR121790] 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 <[email protected]> Diff: --- 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 + .../testsuite/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(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 1e2f45b0bf81..df047f1103bd 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -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 diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 1c0f43e465b5..83f1817bf8e4 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1865,7 +1865,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 7b97accc47e1..0d6692d244a6 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2083,9 +2083,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) */ diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 1d01bc39e9c3..ad29f9336e8f 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 1b76088b31b3..ade09f42ea8a 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 a57a5a08bc3c..88bc0d2cb6bf 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 491122293c5f..01e18ba171c0 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 94ded714e9e0..ccab1e44fbc4 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 3146f283944a..920f852f7944 100644 --- a/libstdc++-v3/include/std/vector +++ b/libstdc++-v3/include/std/vector @@ -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 diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc index cecc535f15f1..833727f4b418 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 3d1dd38d998f..c3e0d421d19f 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 a4e2dfb3321e..d9fc01ab8930 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 d3e089767bfe..a50c5b1033fb 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 ba6dae8935b6..001235ba6430 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 000000000000..8363eaafebcc --- /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 a20c074018e8..b84f84a3069f 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 000000000000..c7aac75cb83e --- /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 +
