aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-09-18 17:20:29 +0100
committerJonathan Wakely <redi@gcc.gnu.org>2024-09-22 17:45:06 +0100
commit482e651f5750e4648ade90e32ed45b094538e7f8 (patch)
treeaa1734619b465e73ef54ad98f40bd862fd00bc8f
parentb6463161c3cd0b1f764697290d9569c7153b8a5b (diff)
downloadgcc-482e651f5750e4648ade90e32ed45b094538e7f8.zip
gcc-482e651f5750e4648ade90e32ed45b094538e7f8.tar.gz
gcc-482e651f5750e4648ade90e32ed45b094538e7f8.tar.bz2
libstdc++: Fix formatting of most negative chrono::duration [PR116755]
When formatting chrono::duration<signed-integer-type, P>::min() we were causing undefined behaviour by trying to form the negative of the most negative value. If we convert negative durations with integer rep to the corresponding unsigned integer rep then we can safely represent all values. libstdc++-v3/ChangeLog: PR libstdc++/116755 * include/bits/chrono_io.h (formatter<duration<R,P>>::format): Cast negative integral durations to unsigned rep. * testsuite/20_util/duration/io.cc: Test the most negative integer durations.
-rw-r--r--libstdc++-v3/include/bits/chrono_io.h16
-rw-r--r--libstdc++-v3/testsuite/20_util/duration/io.cc8
2 files changed, 22 insertions, 2 deletions
diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index 0e4d23c..c7d2c98 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -1720,8 +1720,20 @@ namespace __format
basic_format_context<_Out, _CharT>& __fc) const
{
if constexpr (numeric_limits<_Rep>::is_signed)
- if (__d < __d.zero())
- return _M_f._M_format(-__d, __fc, true);
+ if (__d < __d.zero()) [[unlikely]]
+ {
+ if constexpr (is_integral_v<_Rep>)
+ {
+ // -d is undefined for the most negative integer.
+ // Convert duration to corresponding unsigned rep.
+ using _URep = make_unsigned_t<_Rep>;
+ auto __ucnt = -static_cast<_URep>(__d.count());
+ auto __ud = chrono::duration<_URep, _Period>(__ucnt);
+ return _M_f._M_format(__ud, __fc, true);
+ }
+ else
+ return _M_f._M_format(-__d, __fc, true);
+ }
return _M_f._M_format(__d, __fc, false);
}
diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc
index 6b00689..57020f4 100644
--- a/libstdc++-v3/testsuite/20_util/duration/io.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/io.cc
@@ -106,6 +106,14 @@ test_format()
VERIFY( s == "500ms" );
s = std::format("{:%Q %q}", u);
VERIFY( s == "500 ms" );
+
+ // PR libstdc++/116755 extra minus sign for most negative value
+ auto minsec = std::chrono::seconds::min();
+ s = std::format("{}", minsec);
+ auto expected = std::format("{}s", minsec.count());
+ VERIFY( s == expected );
+ s = std::format("{:%Q%q}", minsec);
+ VERIFY( s == expected );
}
void