Viper cheat sheet

This page briefly summarizes the Viper features that we have seen in the first lecture. If you have not installed Viper yet, please first follow the installation steps.

Further details about Viper are found in the official Viper tutorial. However, do not expect to understand parts about features that we have not covered yet in the course.

Viper programs are stored in .vpr files. They can be verified with two backends: silicon and carbon. silicon is the default and carbon is close to the approach that we will follow throughout the lecture. Ideally, your programs should always verify with both backends. However, for complex examples, one backend might be significantly slower than the other.

Methods

Most Viper code must be placed in methods of the form

method <name>(<inputs>) 
    returns <outputs>
    requires <precondition>
    ensures <postcondition>
{
    <statements>
}

Here, <name> is a name that is unique for the whole Viper program.

<inputs> is a comma-separated list of variable names and their types (so far: Int or Bool). For example, a list of inputs could be x: Int, y: Bool, z: Bool. All input variables are read-only.

Analogously, <outputs> is a comma-separated list of variable names and their types of outputs. Outputs are read-write, but initially have an unknown value. Viper does not have a return statement. The last value assigned to each output variable is returned implicitly when the method terminates.

The variable names used in inputs and outputs are not allowed to overlap.

A method can have zero or more preconditions (predicates such as x > 0 && x < n) indicated by requires. Preconditions may contain input variables but no output variables.

A method can have zero or more postconditions (predicates such as x > 0 && x < n) indicated by ensures. Preconditions may contain both input and output variables.

A method is called abstract if it has no implementation { <statements> }.

Statements

Implementations of Viper methods consist of:

  • local variable declarations var <name>:<type>, for example var x: Int; all variable names within a Viper method (including input and output parameters) must be unique;
  • assignments <var> := <expression>, where <var> is either an output variable or a previously declared local variable and <expression> is a standard arithmetic expression;
  • assertions assert <predicate>, for example assert x > 0, which causes an error if we can reach the assertion in a state that does not satisfy the predicate (otherwise nothing happens);
  • assumptions assume <predicate>, which let the program proceed only with states in which the predicate holds;
  • standard conditionals if (<boolean expression>) { <statement> } else { <statement> }; and
  • standard sequential composition ; between statements; for statements in different lines, the semicolon can be omitted.

Example

For example, the three methods below cover all Viper features that we have encountered in the first lecture. The comments (//) indicate the role of each line.

// every method must have a unique name
method first(x: Int, y: Int, z: Bool) // method three read-only input parameters
    returns (a: Int, b: Bool) // uninitialized output parameters
    requires x < y // precondition, can only refer to input parameters
    requires y > 0 // another precondition
    requires z == true // another precondition
    ensures !b // postcondition, can refer to input and output parameters
    ensures a == x + 2 * y // another precondition
{ // begin of method body
    var u: Int // declaration of local integer variable
               // every variable name can appear at most once in every method
    u := x + y // assignment to local variable
    a := u + y // assignment to output variable
               // the final value of each output is returned
    if (z) { // conditional
        b := false // assignment to output variable of type Bool
    } else {
        b := true
    }
} // end of method body

// An abstract method is a method without a body.
// Such methods are trusted and their verification never fails.
// By default, the input parameters are empty.
// By default, pre- and postconditions are true.
method second() returns (x: Int) 
    ensures x > 0

// the "returns" can be omitted if a method has no outputs
method third()
{
    var a: Int 
    a := second() // a method call that assigns the output to a
    assert a > 0 // assertion; verification fails if we cannot guarantee the condition

    var b: Int 
    assume b > 2 // an assumption; from now on, the verifier proceeds as if b > 2
    var c: Bool

    // all method outputs need to be assigned during a call
    // here, the first output is assigned to b and the second to c.
    b, c := first(2, b, true)

}