diff mbox series

[COMMITTED,29/30] ada: Update contracts on Strings libraries

Message ID 20240801151738.400796-29-poulhies@adacore.com
State New
Headers show
Series [COMMITTED,01/30] ada: Remove obsolete workaround | expand

Commit Message

Marc Poulhiès Aug. 1, 2024, 3:17 p.m. UTC
From: Yannick Moy <moy@adacore.com>

The contracts of Ada.Strings.Bounded.To_String and
Ada.Strings.Fixed.Delete are updated to reflect the standard spec and to
allow proof of callers.

gcc/ada/

	* libgnat/a-strbou.ads (To_String): Add a postcondition to state
	the value of bounds of the returned string, which helps with proof
	of callers.
	* libgnat/a-strfix.adb (Delete): Fix implementation to produce
	correct result in all cases. For example, returned string should
	always have a lower bound of 1, which was not respected in one
	case. This was not detected by proof, since this code was dead
	according to the too strict precondition.
	* libgnat/a-strfix.ads (Delete): State the correct precondition
	from standard which allows a value of Through beyond the last
	valid index, and also restricts values of From from below. Update
	the Contract_Cases accordingly to allow new values of parameters.

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

---
 gcc/ada/libgnat/a-strbou.ads |  3 +++
 gcc/ada/libgnat/a-strfix.adb | 33 ++++++++++++---------------------
 gcc/ada/libgnat/a-strfix.ads | 20 ++++++++++++--------
 3 files changed, 27 insertions(+), 29 deletions(-)
diff mbox series

Patch

diff --git a/gcc/ada/libgnat/a-strbou.ads b/gcc/ada/libgnat/a-strbou.ads
index 827c0dc7448..a4830e56b78 100644
--- a/gcc/ada/libgnat/a-strbou.ads
+++ b/gcc/ada/libgnat/a-strbou.ads
@@ -127,6 +127,9 @@  is
       --  * If Drop=Error, then Strings.Length_Error is propagated.
 
       function To_String (Source : Bounded_String) return String with
+        Post   =>
+          To_String'Result'First = 1
+            and then To_String'Result'Length = Length (Source),
         Global => null;
       --  To_String returns the String value with lower bound 1
       --  represented by Source. If B is a Bounded_String, then
diff --git a/gcc/ada/libgnat/a-strfix.adb b/gcc/ada/libgnat/a-strfix.adb
index 901fd60284b..2da5367985b 100644
--- a/gcc/ada/libgnat/a-strfix.adb
+++ b/gcc/ada/libgnat/a-strfix.adb
@@ -266,36 +266,27 @@  package body Ada.Strings.Fixed with SPARK_Mode is
             return Result_Type (Source);
          end;
 
-      elsif From not in Source'Range
-        or else Through > Source'Last
-      then
-         pragma Annotate
-           (CodePeer, False_Positive,
-            "test always false", "self fullfilling prophecy");
-
-         --  In most cases this raises an exception, but the case of deleting
-         --  a null string at the end of the current one is a special-case, and
-         --  reflects the equivalence with Replace_String (RM A.4.3 (86/3)).
-
-         if From = Source'Last + 1 and then From = Through then
-            return Source;
-         else
-            raise Index_Error;
-         end if;
-
       else
          declare
-            Front : constant Integer := From - Source'First;
+            Front_Len     : constant Integer :=
+              Integer'Max (0, From - Source'First);
+            --  Length of prefix of Source copied to result
 
+            Back_Len      : constant Integer :=
+              Integer'Max (0, Source'Last - Through);
+            --  Length of suffix of Source copied to result
+
+            Result_Length : constant Integer := Front_Len + Back_Len;
+            --  Length of result
          begin
-            return Result : String (1 .. Source'Length - (Through - From + 1))
+            return Result : String (1 .. Result_Length)
               with Relaxed_Initialization
             do
-               Result (1 .. Front) :=
+               Result (1 .. Front_Len) :=
                  Source (Source'First .. From - 1);
 
                if Through < Source'Last then
-                  Result (Front + 1 .. Result'Last) :=
+                  Result (Front_Len + 1 .. Result'Last) :=
                     Source (Through + 1 .. Source'Last);
                end if;
             end return;
diff --git a/gcc/ada/libgnat/a-strfix.ads b/gcc/ada/libgnat/a-strfix.ads
index 9d5e9d92341..aed0851493b 100644
--- a/gcc/ada/libgnat/a-strfix.ads
+++ b/gcc/ada/libgnat/a-strfix.ads
@@ -1061,9 +1061,9 @@  is
       From    : Positive;
       Through : Natural) return String
    with
-     Pre            => (if From <= Through
-                        then (From in Source'Range
-                                and then Through <= Source'Last)),
+     Pre            => From > Through
+       or else (From - 1 <= Source'Last
+                 and then Through >= Source'First - 1),
 
      --  Lower bound of the returned string is 1
 
@@ -1079,12 +1079,14 @@  is
 
           --  Length of the returned string
 
-          Delete'Result'Length = Source'Length - (Through - From + 1)
+          Delete'Result'Length =
+            Integer'Max (0, From - Source'First)
+            + Integer'Max (Source'Last - Through, 0)
 
             --  Elements before From are preserved
 
             and then
-              Delete'Result (1 .. From - Source'First)
+              Delete'Result (1 .. Integer'Max (0, From - Source'First))
               = Source (Source'First .. From - 1)
 
             --  If there are remaining characters after Through, they are
@@ -1092,9 +1094,11 @@  is
 
             and then
               (if Through < Source'Last
-               then Delete'Result
-                      (From - Source'First + 1 .. Delete'Result'Last)
-                    = Source (Through + 1 .. Source'Last)),
+               then
+                 Delete'Result
+                   (Integer'Max (0, From - Source'First) + 1
+                    .. Delete'Result'Last)
+                 = Source (Through + 1 .. Source'Last)),
 
         --  Otherwise, the returned string is Source with lower bound 1