GNATprove: "постусловие может потерпеть неудачу" в простой функции

Я хочу написать простую функцию, которая находит наибольшее число в данном массиве Integer. Вот спецификация:

package Maximum with SPARK_Mode is

   type Vector is array(Integer range <>) of Integer;

   function Maximum (A : in Vector) return Integer
     with
       SPARK_Mode,
       Pre => A'Length > 0,
         Post =>
         (for all i in A'Range => A(i) <= Maximum'Result)
         and then
           (for some i in A'Range => A(i) = Maximum'Result);

end Maximum;

А вот и тело функции:

package body Maximum with SPARK_Mode is

   function Maximum (A : in Vector) return Integer
   is
   Max : Integer := A (A'First);
   begin
      if (A'Length = 1) then
         return Max;
      end if;

      for I in A'First + 1 .. A'Last loop
         pragma Loop_Invariant
           (for all Index in A'First .. I - 1 => Max >= A(Index));

         if A (I) > Max then
            Max := A (I);
         end if;
      end loop;

      return Max;
   end Maximum;

end Maximum;

И когда я пытаюсь доказать эту функцию с помощью SPARK, он говорит, что постусловие может потерпеть неудачу. Я пытаюсь понять это около 5 часов, и я понятия не имею, почему он так говорит. Это действительно раздражает, эта функция ДОЛЖНА работать. Ты хоть представляешь, почему SPARK ведет себя так странно? Каков пример данных для этой функции, чтобы не выполнить ее постусловие? Он всегда возвращает значение, взятое непосредственно из данного массива, и оно всегда максимально.

1 ответ

Решение

Ваша ошибка - сделать цикл инвариантом, который слабее, чем постусловие:

Спецификация:

package Maximum
  with SPARK_Mode
is

   type Vector is array (Integer range <>) of Integer;

   function Maximum (A : in Vector) return Integer
     with
       Pre  => A'Length > 0,
       Post => (for all i in A'Range => A(i) <= Maximum'Result)
               and
               (for some i in A'Range => A(i) = Maximum'Result);

end Maximum;

Реализация:

package body Maximum with SPARK_Mode is

   function Maximum (A : in Vector) return Integer
   is
   Max : Integer := A (A'First);
   begin
      if (A'Length = 1) then
         return Max;
      end if;

      for K in A'First + 1 .. A'Last loop
         pragma Loop_Invariant
           ((for all  I in A'First .. K - 1 => A (I) <= Max)
            and
            (for some I in A'First .. K - 1 => A (I) = Max));

         if A (K) > Max then
            Max := A (K);
         end if;
      end loop;

      return Max;
   end Maximum;

end Maximum;

Файл проекта:

project Maximum is
   for Main use ("maximum");
end Maximum;
Другие вопросы по тегам