SPARK整数溢出检查

时间:2017-11-10 17:08:31

标签: ada ada2012 spark-2014

我有以下程序:

procedure Main with SPARK_Mode is
   F : array (0 .. 10) of Integer := (0, 1, others => 0);
begin
   for I in 2 .. F'Last loop
      F (I) := F (I - 1) + F (I - 2);
   end loop;
end Main;

如果我运行gnatprove,我会得到以下结果,指向+符号:

  

medium:溢出检查可能会失败

这是否意味着F (I - 1)可能等于Integer'Last,并且添加任何内容会溢出?如果是这样,那么从程序流程中不清楚这是不可能的吗?或者我需要在合同中指定这个吗?如果没有,那么它是什么意思?

一个反例表明,在这种情况下gnatprove确实担心Integer的边缘:

  

medium:溢出检查可能会失败(例如F = (1 => -1, others => -2147483648)I = 2

3 个答案:

答案 0 :(得分:2)

考虑为代码添加循环不变量。以下是“使用Spark构建高完整性应用程序”一书中的一个示例。

procedure Copy_Into(Buffer : out Buffer_Type;
                    Source : in String) is
   Characters_To_Copy : Buffer.Count_Type := Maximum_Buffer_Size;
begin
   Buffer := (Others => ' '); -- Initialize to all blanks
   if Source'Length < Characters_To_Copy then
      Characters_To_Copy := Source'Length;
   end if;
   for Index in Buffer.Count_Type range 1..Characters_To_Copy loop
      pragma Loop_Invariant
        (Characters_To_Copy <= Source'Length and
         Characters_To_Copy = Characters_To_Copy'Loop_Entry);
      Buffer (Index) := Source(Source'First + (Index - 1));
   end loop;
end Copy_Into;

答案 1 :(得分:0)

这个循环不变量应该起作用 - 因为2 ^(n-1)+ 2 ^(n-2)&lt; 2 ^ n - 但我无法说服投标人:

procedure Fibonacci with SPARK_Mode is
   F : array (0 .. 10) of Natural := (0      => 0,
                                      1      => 1,
                                      others => 0);
begin
   for I in 2 .. F'Last loop
      pragma Loop_Invariant
        (for all J in F'Range => F (J) < 2 ** J);

      F (I) := F (I - 1) + F (I - 2);
   end loop;
end Fibonacci;

您可以通过一些手动帮助来说服证明者(显示2 ^(n-1)+ 2 ^(n-2)= 2 ^(n-2)*(2 + 1)= 3 / 4 * 2 ^ n <2 ^ n)。

答案 2 :(得分:0)

这已经是一个老问题了,但是我还是想添加一个答案(仅供将来参考)。

随着证明者的进步,问题中所述的示例现在已在GNAT CE 2019中证明是开箱即用的(即无需循环不变性)。还可以证明一个更高级的示例:

main.adb

procedure Main with SPARK_Mode is

   --  NOTE: The theoretical upper bound for N is 46 as
   --
   --              Fib (46)    <    2**31 - 1    <    Fib (47)
   --           1_836_311_903  <  2_147_483_647  <  2_971_215_073

   --  NOTE: Proved with Z3 only. Z3 is pretty good in arithmetic. Additional
   --        options for gnatprove:
   --
   --           --prover=Z3 --steps=0 --timeout=10 --report=all

   type Seq is array (Natural range <>) of Natural;

   function Fibonacci (N : Natural) return Seq with
     Pre  =>  (N in 2 .. 46),
     Post =>  (Fibonacci'Result (0) = 0)
     and then (Fibonacci'Result (1) = 1)
     and then (for all I in 2 .. N =>
                Fibonacci'Result (I) = Fibonacci'Result (I - 1) + Fibonacci'Result (I - 2));

   ---------------
   -- Fibonacci --
   ---------------

   function Fibonacci (N : Natural) return Seq is
      F : Seq (0 .. N) := (0, 1, others => 0);
   begin

      for I in 2 .. N loop

         F (I) := F (I - 1) + F (I - 2);

         pragma Loop_Invariant
           (for all J in 2 .. I =>
              F (J) = F (J - 1) + F (J - 2));

         --  NOTE: The loop invariant below helps the prover to proof the
         --        absence of overflow. It "reminds" the prover that all values
         --        from iteration 3 onwards are strictly monotonically increasing.
         --        Hence, if absence of overflow is proven in this iteration,
         --        then absence is proven for all previous iterations.

         pragma Loop_Invariant
           (for all J in 3 .. I =>
              F (J) > F (J - 1));

      end loop;

      return F;

   end Fibonacci;

begin
   null;
end Main;