From: Eric Botcazou <ebotca...@adacore.com>

The 3 units Ada.Calendar, GNAT.Calendar and GNAT.Sockets contain conversion
functions from the Duration fixed-point type that implement the same idiom
but with some inconsistencies:

  * GNAT.Sockets only handles Timeval_Duration, i.e. positive Duration, and
    is satisfactory, although a simpler implementation can be written,

  * GNAT.Calendar mishandles negative Duration values, as well as integral
    Duration values,

  * Ada.Calendar mishandles negative Duration values, and rounds nanoseconds
    instead of truncating them.

gcc/ada/ChangeLog:

        * libgnat/a-calend.adb (To_Struct_Timespec_64): Deal with negative
        Duration values and truncate the nanoseconds too.
        * libgnat/g-calend.adb (timeval_to_duration): Unsuppress overflow
        checks.
        (duration_to_timeval): Likewise.  Deal with negative Duration values
        as well as integral Duration values.
        * libgnat/g-socket.adb (To_Timeval): Simplify the implementation.

Tested on x86_64-pc-linux-gnu, committed on master.

---
 gcc/ada/libgnat/a-calend.adb | 27 ++++++++++++++++++---------
 gcc/ada/libgnat/g-calend.adb | 21 +++++++++++++++++++--
 gcc/ada/libgnat/g-socket.adb |  9 ++++-----
 3 files changed, 41 insertions(+), 16 deletions(-)

diff --git a/gcc/ada/libgnat/a-calend.adb b/gcc/ada/libgnat/a-calend.adb
index 4f89a401a9c..0be56734ab2 100644
--- a/gcc/ada/libgnat/a-calend.adb
+++ b/gcc/ada/libgnat/a-calend.adb
@@ -1068,19 +1068,28 @@ is
          tv_nsec : out Long_Integer)
       is
          pragma Unsuppress (Overflow_Check);
-         Secs      : Duration;
-         Nano_Secs : Duration;
 
       begin
-         --  Seconds extraction, avoid potential rounding errors
+         if D = 0.0 then
+            tv_sec  := 0;
+            tv_nsec := 0;
 
-         Secs   := D - 0.5;
-         tv_sec := Long_Long_Integer (Secs);
+         elsif D < 0.0 then
+            tv_sec := Long_Long_Integer (D + 0.5);
+            if D = Duration (tv_sec) then
+               tv_nsec := 0;
+            else
+               tv_nsec := Long_Integer ((D - Duration (tv_sec)) * Nano + 0.5);
+            end if;
 
-         --  Nanoseconds extraction
-
-         Nano_Secs := D - Duration (tv_sec);
-         tv_nsec := Long_Integer (Nano_Secs * Nano);
+         else
+            tv_sec := Long_Long_Integer (D - 0.5);
+            if D = Duration (tv_sec) then
+               tv_nsec := 0;
+            else
+               tv_nsec := Long_Integer ((D - Duration (tv_sec)) * Nano - 0.5);
+            end if;
+         end if;
       end To_Struct_Timespec_64;
 
       ------------------
diff --git a/gcc/ada/libgnat/g-calend.adb b/gcc/ada/libgnat/g-calend.adb
index e410c3e9408..a2bc77c1cc7 100644
--- a/gcc/ada/libgnat/g-calend.adb
+++ b/gcc/ada/libgnat/g-calend.adb
@@ -344,6 +344,8 @@ package body GNAT.Calendar is
       sec   : aliased C.Extensions.long_long;
       usec  : aliased C.long;
 
+      pragma Unsuppress (Overflow_Check);
+
    begin
       timeval_to_duration (T, sec'Access, usec'Access);
       pragma Annotate (CodePeer, Modified, sec);
@@ -369,13 +371,28 @@ package body GNAT.Calendar is
       sec    : C.Extensions.long_long;
       usec   : C.long;
 
+      pragma Unsuppress (Overflow_Check);
+
    begin
       if D = 0.0 then
          sec  := 0;
          usec := 0;
+
+      elsif D < 0.0 then
+         sec := C.Extensions.long_long (D + 0.5);
+         if D = Duration (sec) then
+            usec := 0;
+         else
+            usec := C.long ((D - Duration (sec)) * Micro + 0.5);
+         end if;
+
       else
-         sec  := C.Extensions.long_long (D - 0.5);
-         usec := C.long ((D - Duration (sec)) * Micro - 0.5);
+         sec := C.Extensions.long_long (D - 0.5);
+         if D = Duration (sec) then
+            usec := 0;
+         else
+            usec := C.long ((D - Duration (sec)) * Micro - 0.5);
+         end if;
       end if;
 
       duration_to_timeval (sec, usec, Result'Access);
diff --git a/gcc/ada/libgnat/g-socket.adb b/gcc/ada/libgnat/g-socket.adb
index 5042dacc166..0fed7917754 100644
--- a/gcc/ada/libgnat/g-socket.adb
+++ b/gcc/ada/libgnat/g-socket.adb
@@ -3059,12 +3059,11 @@ package body GNAT.Sockets is
       --  Normal case where we do round down
 
       else
-         S  := time_t (Val - 0.5);
-         uS := suseconds_t (1_000_000 * (Val - Selector_Duration (S)) - 0.5);
-
-         if uS = -1 then
-            --  It happen on integer duration
+         S := time_t (Val - 0.5);
+         if Val = Timeval_Duration (S) then
             uS := 0;
+         else
+            uS := suseconds_t ((Val - Timeval_Duration (S)) * 1_000_000 - 0.5);
          end if;
       end if;
 
-- 
2.43.0

Reply via email to