]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Fix formatting of most negative chrono::duration [PR116755]
authorJonathan Wakely <jwakely@redhat.com>
Wed, 18 Sep 2024 16:20:29 +0000 (17:20 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Sun, 22 Sep 2024 16:45:06 +0000 (17:45 +0100)
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.

libstdc++-v3/include/bits/chrono_io.h
libstdc++-v3/testsuite/20_util/duration/io.cc

index 0e4d23c9bb77a8fe98900f99ead1f1433e075cd2..c7d2c9862fcf7917ff7452715f4699a1d6a8991b 100644 (file)
@@ -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);
        }
 
index 6b00689672c85254f2db75809cc280a836a9a845..57020f4f953767bf1fad7eca88d3caf9bb464834 100644 (file)
@@ -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