Формальная проверка ключа Java не может доказать цикл сброса массива

В настоящее время я пытаюсь понять немного формальной проверки с помощью инструмента Key для программ Java.
Вот мой аннотированный Java-код:

public class Test{
    public int[] a;

    /*@ public normal_behavior
    @ ensures (\forall int x; 0<=x && x<a.length; a[x]==1);
    @*/
    public void fillArray() {
        int i = 0;
        while(i < a.length) {
            a[i] = 1;
            i++;
        }
    }

}

К моему удивлению, Key не может доказать, что текущая программа действительна в соответствии с ее спецификацией. Ключ не достигает цели 54. Текущее окно цели показывает:

 self.a.<created> = TRUE,
 wellFormed(heap),
 self.<created> = TRUE,
 Test::exactInstance(self) = TRUE,
 measuredByEmpty
==>
 self.a = null,
 self = null,
 {exc:=null || i:=0}
   \<{try {method-frame(source=fillArray()@Test,this=self)
       : {
           while ( i<this.a.length ) {
             this.a[i]=1;
             i++;
           }
         }
       }   catch (java.lang.Throwable e) {
         exc=e;
       }
     }\> (\forall int x; (x <= -1 | x >= self.a.length | self.a[x] = 1) & self.<inv> & exc = null)

Я действительно не понимаю, что является основной причиной спецификации отказа проверки?

0 ответов

Основная причина сбоя заключалась в том, что, если проверяющий обнаружил неограниченный цикл в методе, то он не мог следовать спецификации метода без спецификации инварианта цикла.

Таким образом, для каждого неограниченного цикла мы должны указать инвариант цикла. Инвариант цикла - это некое правило, которое выполняется для каждой итерации цикла. Каждый цикл может иметь свое собственное инвариантное правило. Поэтому код Java со спецификацией должен быть зафиксирован на:

public class Test{

  public int[] a;

  /*@ public normal_behavior
  @ ensures (\forall int x; 0<=x && x<a.length; a[x]==1); // method post-condition
  @ diverges true; // not necessary terminates
  @*/
  public void fillArray() {
    int i = 0;

    /*@ loop_invariant
    @ 0 <= i && i <= a.length &&  // i ∈ [0, a.length]
    @ (\forall int x; 0<=x && x<i; a[x]==1); // x ∈ [0, i) | a[x] = 1
    @ assignable a[*]; // Valid array location
    @*/
    while(i < a.length) {
      a[i] = 1;
      i++;
    }
  }

}

Самое сложное в размышлении о том, как указать метод, - это найти инвариант цикла. Но в то же время - это самое интересное. Ради причины я повторю инвариант этого цикла:

i ∈ [0, длина]
x ∈ [0, i) | a [x] = 1

И это условие никогда не меняется в цикле в ЛЮБОЙ итерации. Вот почему это инвариант.

Кстати, если формальная спецификация сделана правильно - мы можем выбросить TDD и модульное тестирование из окна. Кого волнуют результаты во время выполнения, если математически доказано, что программа верна в соответствии с ее спецификацией?

Если спецификация хорошая и семантика кода доказана - тогда при выполнении программы ничего не может пойти не так - это точно. Из-за этого формальная валидация является очень многообещающей областью.

Другие вопросы по тегам