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