Skip to content

A potential bug in how Silicon handles loop invariant #957

@funemy

Description

@funemy

Hi!

The example below fails to verify in Silicon, but verifies in Carbon (using the most recent release of the viper vscode plugin).

The places that seem relevant are commented with // FIXME:.
If I switch the order of the last two invariants in the inner loop, then the code gets verified in Silicon as well.

Seems like a potential bug in handling loop invariant?

domain Matrix {
  function select(m: Matrix, i: Int, j: Int) : Int
  function store(m:Matrix, i: Int, j:Int, value: Int) : Matrix
  function size(m:Matrix) : Int // only one size function needed for a square matrix (number of rows and number of columns)
  axiom select_store_same {
    forall m: Matrix, i: Int, j:Int, v:Int :: {select(store(m,i,j,v),i,j)} inRange(m,i,j) ==> select(store(m,i,j,v),i,j) == v
  }
  axiom store_preserve_size {
    forall m: Matrix, i: Int, j: Int, v: Int :: {store(m,i,j,v)} inRange(m,i,j) ==> size(store(m,i,j,v)) == size(m)
  }
  axiom select_store_diff {
    forall m: Matrix, i1: Int, j1:Int, i2: Int, j2:Int, v : Int :: {select(store(m,i1,j1,v),i2,j2)} {select(m,i2,j2),store(m,i1,j1,v)}
      inRange(m,i1,j1) && inRange(m,i2,j2) && (i1 != i2 || j1 != j2) ==> select(store(m,i1,j1,v),i2,j2) == select(m,i2,j2)
  }
}

define inRange(m,i,j) 0 <= i && 0 <= j && i < size(m) && j < size(m)

method add(m1: Matrix, m2: Matrix) returns (m3: Matrix)
  requires size(m1) == size(m2)
  ensures forall x:Int, y:Int :: {select(m3,x,y)} inRange(m1,x,y) ==> select(m3,x,y) == select(m1,x,y) + select(m2,x,y)
{
  assume size(m3) == size(m1) // "initialise" m3 to *some* matrix of the right size
  var i : Int := 0
  var j : Int
  while(i < size(m1))
    invariant i >= 0
    invariant size(m3) == size(m1)
    // FIXME: fails to be reestablished (with Silicon)
    invariant forall x: Int, y: Int :: {select(m3,x,y)} 0 <= x < i && 0 <= y < size(m1) ==> select(m3,x,y) == select(m1,x,y) + select(m2,x,y)
  {
    j := 0
    while(j < size(m1))
        invariant i >= 0
        invariant j >= 0
        invariant size(m3) == size(m1)
        // FIXME: if we switch the order of the two invariants below, the program verifies in both Silicon and Carbon
        invariant forall y: Int :: {select(m3,i,y)} 0 <= y < j ==> select(m3,i,y) == select(m1,i,y) + select(m2, i, y)
        invariant forall x: Int, y: Int :: {select(m3,x,y)} 0 <= x < i && 0 <= y < size(m3) ==> select(m3,x,y) == select(m1,x,y) + select(m2, x, y)
    {
      m3 := store(m3,i,j,(select(m1,i,j) + select(m2,i,j)))
      // assert select(m3,i,j) == select(m1,i,j) + select(m2,i,j)
      j := j + 1
    }
    i := i + 1
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions