__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
__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>
{
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>
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
{
__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>
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
ftms = {
name = print;
values = {
- v = 202403;
+ v = 202406;
cxxmin = 23;
hosted = yes;
};
#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) */
{ 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>...>
{ 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>
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
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>
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
// 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
__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>>
{
}
};
+#if __glibcxx_print >= 202406L
+ template<typename _Allocator>
+ constexpr bool
+ enable_nonlocking_formatter_optimization<basic_stacktrace<_Allocator>> = true;
+#endif
+
namespace pmr
{
using stacktrace
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
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
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
test02()
{
#if __cpp_lib_formatters >= 202302
-
static_assert( std::is_default_constructible_v<std::formatter<std::thread::id, char>> );
std::thread t1([]{});
#endif
}
+#if __cplusplus >= 202302L
+static_assert(std::enable_nonlocking_formatter_optimization<std::thread::id>);
+#endif
+
int main()
{
test01();
// 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>
#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)
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>
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>();
}
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();
test_outputs<wchar_t>();
test_nested();
test_padding();
+
+ test_nonblocking<std::pair>();
+ test_nonblocking<std::tuple>();
}
--- /dev/null
+#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;
+ }
+};
+
#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
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>
--- /dev/null
+// { 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
+