https://gcc.gnu.org/g:ac0a04b7a254fb8e1d8d7088336bcb4375807b1e
commit r16-1099-gac0a04b7a254fb8e1d8d7088336bcb4375807b1e Author: Tomasz Kamiński <tkami...@redhat.com> Date: Wed Jun 4 11:05:11 2025 +0200 libstdc++: Fix format call and test formatting with empty specs for durations. This patches fixes an obvious error, where the output iterator argument was missing for call to format_to, when duration with custom representation types are used. It's also adding the test for behavior of ostream operator and the formatting with empty chron-spec for the chrono types. Current coverage is: * duration and hh_mm_ss in this commit, * calendar types in r16-1016-g28a17985dd34b7. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__formatter_chrono:_M_s): Add missing __out argument to format_to call. * testsuite/std/time/format/empty_spec.cc: New test. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Signed-off-by: Tomasz Kamiński <tkami...@redhat.com> Diff: --- libstdc++-v3/include/bits/chrono_io.h | 3 +- .../testsuite/std/time/format/empty_spec.cc | 271 +++++++++++++++++++++ 2 files changed, 273 insertions(+), 1 deletion(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 346eb8b3c33b..239f9c780094 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -1296,7 +1296,8 @@ namespace __format else { auto __str = std::format(_S_empty_spec, __ss.count()); - __out = std::format_to(_GLIBCXX_WIDEN("{:0>{}s}"), + __out = std::format_to(std::move(__out), + _GLIBCXX_WIDEN("{:0>{}s}"), __str, __hms.fractional_width); } diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc index 322faa1939d9..46942dc30fc6 100644 --- a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc +++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc @@ -1,7 +1,9 @@ // { dg-do run { target c++20 } } +// { dg-require-effective-target hosted } // { dg-timeout-factor 2 } #include <chrono> +#include <ranges> #include <sstream> #include <testsuite_hooks.h> @@ -49,6 +51,274 @@ void verify(const T& t, const _CharT* str) VERIFY( res == str ); } +template<typename Ret = void> +struct Rep +{ + using Return + = std::conditional_t<std::is_void_v<Ret>, Rep, Ret>; + + Rep(long v = 0) : val(v) {} + + operator long() 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 <<]"); } + + long val; +}; + +template<typename Ret, typename Other> + requires std::is_integral_v<Other> +struct std::common_type<Rep<Ret>, Other> +{ + using type = Rep<Ret>; +}; + +template<typename Ret, typename Other> + requires std::is_integral_v<Other> +struct std::common_type<Other, Rep<Ret>> + : std::common_type<Rep<Ret>, Other> +{ }; + +template<typename Ret> +struct std::numeric_limits<Rep<Ret>> + : std::numeric_limits<long> +{ }; + +template<typename Ret, typename _CharT> +struct std::formatter<Rep<Ret>, _CharT> + : std::formatter<long, _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<long, _CharT>::format(t.val, ctx); + return std::ranges::copy(suffix, out).out; + } +}; + +using deciseconds = duration<seconds::rep, std::deci>; + +template<typename _CharT> +void +test_duration() +{ + std::basic_string<_CharT> res; + + const milliseconds di(40); + verify( di, WIDEN("40ms") ); + res = std::format(WIDEN("{:>6}"), di); + VERIFY( res == WIDEN(" 40ms") ); + + verify( -di, WIDEN("-40ms") ); + res = std::format(WIDEN("{:>6}"), -di); + VERIFY( res == WIDEN(" -40ms") ); + + const duration<double> df(11.22); + verify( df, WIDEN("11.22s") ); + res = std::format(WIDEN("{:=^12}"), df); + VERIFY( res == WIDEN("===11.22s===") ); + + verify( -df, WIDEN("-11.22s") ); + res = std::format(WIDEN("{:=^12}"), -df); + VERIFY( res == WIDEN("==-11.22s===") ); +} + +template<typename _CharT> +void +test_duration_cust() +{ + std::basic_string<_CharT> res; + const duration<char, std::ratio<1, 10>> charRep(123); + verify( charRep, WIDEN("123ds") ); + + // +asLong returns long, so formatted as long + const duration<Rep<long>> asLong(20); + verify( asLong, WIDEN("20s") ); + res = std::format(WIDEN("{:>6}"), asLong); + VERIFY( res == WIDEN(" 20s") ); + + verify( -asLong, WIDEN("-20s") ); + res = std::format(WIDEN("{:>6}"), -asLong); + VERIFY( res == WIDEN(" -20s") ); + + res = std::format(WIDEN("{:%Q}"), asLong); + VERIFY( res == WIDEN("20") ); + res = std::format(WIDEN("{:+<7%Q}"), asLong); + VERIFY( res == WIDEN("20+++++") ); + + // +asRep returns Rep<>, so formatted as Rep<> + const duration<Rep<>> asRep(10); + verify( asRep, WIDEN("10[via <<]s") ); + res = std::format(WIDEN("{:=^15}"), asRep); + VERIFY( res == WIDEN("==10[via <<]s==") ); + + verify( -asRep, WIDEN("-10[via <<]s") ); + res = std::format(WIDEN("{:=^15}"), -asRep); + VERIFY( res == WIDEN("=-10[via <<]s==") ); + + res = std::format(WIDEN("{:%Q}"), asRep); + VERIFY( res == WIDEN("10[via format]") ); + res = std::format(WIDEN("{:=^18%Q}"), asRep); + VERIFY( res == WIDEN("==10[via format]==") ); + + const duration<Rep<>, std::milli> milliRep(10); + verify( milliRep, WIDEN("10[via <<]ms") ); + res = std::format(WIDEN("{:=^15}"), milliRep); + VERIFY( res == WIDEN("=10[via <<]ms==") ); + + verify( -milliRep, WIDEN("-10[via <<]ms") ); + res = std::format(WIDEN("{:=^15}"), -milliRep); + VERIFY( res == WIDEN("=-10[via <<]ms=") ); + + res = std::format(WIDEN("{:%Q}"), milliRep); + VERIFY( res == WIDEN("10[via format]") ); + res = std::format(WIDEN("{:=^18%Q}"), milliRep); + VERIFY( res == WIDEN("==10[via format]==") ); +} + +template<typename Ratio, typename Rep, typename Period> +constexpr auto +hms(const duration<Rep, Period>& d) +{ + using Dur = duration<Rep, typename Ratio::period>; + return hh_mm_ss<Dur>(duration_cast<Dur>(d)); +} + +template<typename _CharT> +void +test_hh_mm_ss() +{ + auto dt = 22h + 24min + 54s + 111222333ns; + verify( hms<nanoseconds>(dt), + WIDEN("22:24:54.111222333") ); + verify( hms<microseconds>(dt), + WIDEN("22:24:54.111222") ); + verify( hms<milliseconds>(dt), + WIDEN("22:24:54.111") ); + verify( hms<deciseconds>(dt), + WIDEN("22:24:54.1") ); + verify( hms<seconds>(dt), + WIDEN("22:24:54") ); + verify( hms<minutes>(dt), + WIDEN("22:24:00") ); + verify( hms<hours>(dt), + WIDEN("22:00:00") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-22:24:54.111222333") ); + verify( hms<microseconds>(-dt), + WIDEN("-22:24:54.111222") ); + verify( hms<milliseconds>(-dt), + WIDEN("-22:24:54.111") ); + verify( hms<deciseconds>(-dt), + WIDEN("-22:24:54.1") ); + verify( hms<seconds>(-dt), + WIDEN("-22:24:54") ); + verify( hms<minutes>(-dt), + WIDEN("-22:24:00") ); + verify( hms<hours>(-dt), + WIDEN("-22:00:00") ); + + verify( hms<nanoseconds>(-dt), + WIDEN("-22:24:54.111222333") ); + + dt += 300h; + verify( hms<nanoseconds>(dt), + WIDEN("322:24:54.111222333") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-322:24:54.111222333") ); + + dt += 14000h; + verify( hms<nanoseconds>(dt), + WIDEN("14322:24:54.111222333") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-14322:24:54.111222333") ); +} + +template<typename _CharT> +void +test_hh_mm_ss_cust() +{ + const duration<char, deciseconds::period> charRep(123); + verify( hms<deciseconds>(charRep), + WIDEN("00:00:12.3") ); + verify( hms<seconds>(charRep), + WIDEN("00:00:12") ); + + auto dt = 22h + 24min + 54s + 123ms; + // +plus returns long, so formatted as long + const duration<Rep<long>, std::milli> asLong(dt.count()); + verify( hms<milliseconds>(asLong), + WIDEN("22:24:54.123[via format]") ); + verify( hms<deciseconds>(asLong), + WIDEN("22:24:54.1[via format]") ); + verify( hms<seconds>(asLong), + WIDEN("22:24:54") ); + verify( hms<milliseconds>(-asLong), + WIDEN("-22:24:54.123[via format]") ); + verify( hms<deciseconds>(-asLong), + WIDEN("-22:24:54.1[via format]") ); + verify( hms<seconds>(-asLong), + WIDEN("-22:24:54") ); + + // +asRep returns Rep<>, so formatted as Rep<> + const duration<Rep<>, std::milli> asRep(dt.count()); + verify( hms<milliseconds>(asRep), + WIDEN("22:24:54.123[via format]") ); + verify( hms<deciseconds>(asRep), + WIDEN("22:24:54.1[via format]") ); + verify( hms<seconds>(asLong), + WIDEN("22:24:54") ); + verify( hms<milliseconds>(-asLong), + WIDEN("-22:24:54.123[via format]") ); + verify( hms<deciseconds>(-asLong), + WIDEN("-22:24:54.1[via format]") ); + verify( hms<seconds>(-asLong), + WIDEN("-22:24:54") ); +} + +template<typename CharT> +void +test_durations() +{ + test_duration<CharT>(); + test_duration_cust<CharT>(); + + test_hh_mm_ss<CharT>(); + test_hh_mm_ss_cust<CharT>(); +} + template<typename _CharT> void test_day() @@ -288,6 +558,7 @@ void test_all() { test_padding<CharT>(); + test_durations<CharT>(); test_calendar<CharT>(); }