如何证明SPARK.Text_IO过程前置条件将成立

时间:2017-11-17 03:48:33

标签: ada proof spark-2014

我在SPARK Discovery 2017的spark_io示例中使用了SPARK.Text_IO。

我的问题是许多SPARK.Text_IO程序都有一个前提条件,我不知道如何开始尝试证明标准输入是可读的,而我们不在文件末尾。我在下面的代码中显示的尝试是将SPARK.Text_IO过程(在本例中为Get_Immediate)的前提条件添加到调用过程的前提条件中,认为这可能会保证证明该前置条件为真。它没有用。以下是我所谈论的一个例子:

测试规范:

with SPARK.Ada.Text_IO; use SPARK.Ada.Text_IO;

package test with SPARK_Mode
is

   continue_messages_key : Character := ' ';

   procedure User_Wait_For_Continue_Messages_Key
   with Global => (In_Out => Standard_Input,
                   Input => continue_messages_key),
        Pre => Is_Readable (Standard_Input) and then
                    not End_Of_File; 

end test;

测试机构:

pragma SPARK_Mode(On);

package body test is

   procedure User_Wait_For_Continue_Messages_Key
   is
      IR : Immediate_Result;
      Avail : Boolean;
   begin
      loop
         Get_Immediate(IR, Avail);
         if Avail then
            if IR.Status = Success then
               if IR.Available = True then
                  if IR.Item = continue_messages_key then
                     return;
                  end if;
               end if;
            end if;
         end if;
      end loop;
   end User_Wait_For_Continue_Messages_Key;

end test;

证明者给出的错误在Get_Immediate行" medium:precondition可能会失败" Get_Immediate过程的原型和合同如下:

procedure Get_Immediate (Item      :    out Character_Result)
     with Global => (In_Out => Standard_Input),
          Pre    => Is_Readable (Standard_Input) and then
                    not End_Of_File,
          Post   => Is_Readable (Standard_Input) and
                    Name (Standard_Input) = Name (Standard_Input)'Old and
                    Form (Standard_Input) = Form (Standard_Input)'Old and
                    Is_Standard_Input (Standard_Input);

您如何向SPARK证明Standard_Input是可读的并且它不是文件的结尾?

1 个答案:

答案 0 :(得分:2)

首先让我说自从特别评论的日子以来我没有使用过SPARK,所以我的回答可能无法反映当前的使用情况。

查看SPARK的一种方法是让您考虑您的程序可能遇到的每件事。如果前提条件为假,您的程序应该怎么做?你必须证明你已经考虑过这种可能性。

假设Standard_Input上的所有SPARK.Ada.Text_IO操作都有一个包含Is_Readable (Standard_Input)的后置条件作为Get_Immediate,那么它应该足够像

那样
pragma Assert (Is_Readable (Standard_Input) );

在程序开头并将其添加到过程的后置条件(以及您读取Standard_Input的任何其他子程序)。然后你应该确保整个程序中前提条件的那一部分都存在。 (如果SPARK最初保证Standard_Input是可读的,则可能不需要断言。)

not End_Of_File有点复杂。至少在某些平台上,它可能是假的。例如,Linux作为第一行输入时将Ctrl-D视为EOF。还有从管道或输入重定向读取的情况。如果用户在循环期间输入EOF,则当End_Of_File为True时,您的程序可能会调用Get_Immediate,因此SPARK无法证明这一点并不奇怪。可能你需要在你的程序的前提条件下取消这部分,并将你的身体改为

All_Chars : loop
   if not End_Of_File then
      Get_Immediate (IR, Avail);

      exit All_Chars when Avail               and then
                          IR.Status = Success and then
                          IR.Available        and then
                          IR.Item = Continue_Messages_Key;
   end if;
end loop All_Chars;

然后你会确定Get_Immediate的前提条件是否满足并且如果End_Of_File变为True则具有明显所需的行为(无限循环)(假设在某些情况下IR的某个字段失败了)。

您的代码有一些令人费解的事情。首先是全局变量。全局变量是万恶之源,或者至少保证不可读的代码。然后是程序的特殊性。不会更通用,比如

procedure Wait_For_Key (Key : in Character);

是否易于编写和证明,更有用?然后是嵌套if语句的字符串,我发现它比与and then相关的等效条件更难阅读。最后,将布尔值与True进行比较。既然“=”返回布尔值,那是不是表示需要的是布尔值,这是“=”左侧的布尔值?

也许这意味着“=”的结果也必须与True进行比较。那么“=”的结果也必须与True进行比较。这可能会更好,因为它可以确保写这篇文章的人永远不会完成。