https://gcc.gnu.org/g:93aa395bc32d0e0c7254eb9ab1eee546809d4338
commit r16-2495-g93aa395bc32d0e0c7254eb9ab1eee546809d4338 Author: Eric Botcazou <ebotca...@adacore.com> Date: Tue Jul 15 00:37:19 2025 +0200 ada: Fix inconsistencies in conversion functions from Duration 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. Diff: --- gcc/ada/libgnat/a-calend.adb | 29 +++++++++++++++++++---------- gcc/ada/libgnat/g-calend.adb | 21 +++++++++++++++++++-- gcc/ada/libgnat/g-socket.adb | 9 ++++----- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/gcc/ada/libgnat/a-calend.adb b/gcc/ada/libgnat/a-calend.adb index 4f89a401a9c9..0be56734ab2a 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 - - Secs := D - 0.5; - tv_sec := Long_Long_Integer (Secs); - - -- Nanoseconds extraction + if D = 0.0 then + tv_sec := 0; + tv_nsec := 0; + + 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; - 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 e410c3e9408f..a2bc77c1cc7b 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 5042dacc166f..0fed79177544 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;