-
Notifications
You must be signed in to change notification settings - Fork 26
PVL Syntax Reference
On this page you can find a description of the specification language of VerCors. In general, the specification language extends the language that is verified, like Java. That means that all types, (pure) operators, etc. from the source language can also be used in the specifications, together with the specification-only constructs described below.
Additionally, this page describes PVL, the Prototypal Verification Language. It can be verified like Java or C, so the same specification constructs are available. However, it is developed specifically for VerCors, so we also provide the information that is usually found in the language documentation, like the C standard.
- Identifiers can consist of the characters a-z, A-Z, 0-9 and _. However, they must start with a letter (a-z, A-Z). Note that the following words are reserved and can therefore not be used as identifiers: create, action, destroy, send, recv, use, open, close, atomic, from, merge, split, process, apply, label, \result, write, read, none, empty and current_thread.
- Specification-only elements are usually embedded in JML-like comments:
/*@ spec @*/
or//@ spec line
. Exception: In PVL, specifications are not embedded in comments, instead they are written into the code directly. - Most specification constructs are self-contained statements, like preconditions, loop invariants or ghost assignments. Those end with a semicolon
;
. However, a few elements. likewith
side conditions, are parts of another statement, and do not have their own semicolon.
- At the top level of a program, there can be methods directly (like in C). But more commonly, those are defined within a
class
:
class ClassName {
// Here you can add any constructors, class variables and method declarations
}
- The keyword
this
is used to refer to the current object. - You can write single-line or multi-line comments as follows:
// This is a single-line comment
/*
This is a
multi-line comment
*/
- Like in C or Java, statements in PVL should end with a semi-colon
;
.
Type | Description |
---|---|
int |
Integer |
boolean |
true or false
|
void |
Used when a method does not return anything |
resource |
Boolean-like type that also allows reasoning about permissions |
frac |
Fraction |
zfrac |
Fraction that can also be zero. |
process |
Type of actions and defined processes in histories. For more information on histories & futures, have a look at the section on Histories & Futures. |
T[] |
Array which contains elements of type T . T should be replaced by a type. Note that when you initialize a new array, you should always define the length of the array, e.g. new int[3] instead of new int[] . |
seq<T> var |
Defines an immutable ordered list (sequence) named var . T should be replaced by a type. |
set<T> var |
Defines an immutable orderless collection (set) that does not allow duplicates. T should be replaced by a type. |
bag<T> var |
Defines an immutable orderless collection (bag) that does allow duplicates. T should be replaced by a type. |
option<T> |
Extends type T with an extra element None . Each element is then either of the type None or of the type Some(e) where e is of type T . T should be replaced by a type. Options cannot be unpacked at the moment. |
For more information on sequences, sets, bags and options, have a look at the wiki page on Axiomatic Data Types.
Code | Description |
---|---|
== , !=
|
Equals and not equals for reasoning about the equality of expressions |
&& , || , !
|
And, or, and negation respectively for reasoning with boolean variables. |
< , <= , > , >=
|
Smaller than, smaller than equals, greater than and greater than equals respectively. They are used to compare integers. |
+ , - , * , / , \
|
Addition, subtraction, multiplication, integer division and fractional division respectively. |
++ , --
|
Increase by 1 and decrease by 1 respectively. Unlike the other operators, this only requires an variable on the left-hand side. |
==> |
Implication. This statement evaluates to true unless the statement before the arrow evaluates to true and the statement after the arrow evaluates to false. |
** |
Separating conjunction. a ** b denotes that a and b point to different variables on the heap and that both expressions mus tevaluate to true. This is used to reason about multiple resources. |
-* |
Magic wand or separating implication. This is used to reason about resources. //TODO: investigate what is supported. |
new T() |
Creates a new object of type T . |
new T[length] |
Creates a new array which contains objects of type T with length number of items. |
boolExpr ? exprA : exprB; |
Evaluates boolExpr , if this is true it will return exprA and otherwise exprB . |
(\let T t = exprA; exprB) |
Evaluates exprB , replacing every occurence of t with the result of evaluatingexprA . The surrounding parens ( ) are required. |
Code | Description |
---|---|
(\forall vars; range; boolExpr) |
Construct that allows you to repeat a certain expression for several variables. For example, (\forall int j; j >= 0 && j < n; array[j] >= 0) denotes that all elements in array nonnegative. It is equal to the following statement: array[0] >= 0 && array[1] >= 0 && ... && array[n-1] >= 0 . vars should declare the variables over which we will reason. range can be any boolean expression, often used to describe the range of vars . boolExpr is some expression that should evaluate to a boolean for all variables in the given range. |
(\forall* vars; range; expr) |
Similar construct to the \forall except that the expressions are separated by ** instead of && . One can for example write (\forall* int j; j >= 0 && j < array.length; Perm(array[j], write) which denotes that the thread has writing access to all elements in array . |
(\exists Type id; range; expr) |
Evaluates to true if there exists an element, called id , such that the final expression evaluates to true. |
array[*] |
This is a simplified \forall* expression that ranges over all elements in the array array . Instead of the example mentioned above for \forall* , you can then write Perm(array[*], write) . This cannot be used within nested \forall* expressions. |
Code | Description |
---|---|
\result |
Keyword that refers to the object that the method returns |
\old(expr) |
Refers to the value of the specified expression in the pre-state. This can be used in a postcondition (ensures) or loop invariant in which case the pre-state is when the method was called. |
held(x) |
Check whether you are holding a non-reentrant lock. x should refer to the lock invariant. See also: Queue.pvl. |
idle(t) |
Returns true if thread t is idle (before calling t.fork() or after calling t.join() ). Overall a thread starts as idle, if the thread is forked, it goes to a 'runnning' state. If join is called on a running thread, then it goes back to an 'idle' state. |
running(t) |
Returns true if thread t is running. Overall a thread can go through the following states: idle --[t.fork()]--> running --[t.join()]--> idle |
Code | Description |
---|---|
Perm(var, p) |
Defines permissions for variable var . If p is 1 or write then it denotes write permission, anything between 0 and 1 or read denotes read permission. Be aware that you cannot use arithmetic when using read such as read/2 or dividing read among multiple threads! Perm() is of the type Resource. |
PointsTo(var, p, val) |
Denotes permissions p for variable var similar to Perm() . Moreover, variable var points to the value val . PointsTo() is of the type Resource. |
Value(var) |
Defines a read-only permission on the given variable. This read-only permission cannot become a write permission and it can duplicate as often as necessary. |
Control flow construct | Example |
---|---|
Function |
contract returnType functionName(paramType paramName, ...) { ... } . contract should describe the specification of the method. returnType should be replaced by a specific type or void depending on what (if anything) the method returns. functionName should be replaced by the name of the function. paramType paramName, ... is a comma-separated list of parameters, for every parameter you should define its type and its name. It is also possible to have no parameters. For example: ensures \result == a + b; int sum(int a, int b) { return a + b; } is a function which returns an integer that is the sum of the parameters given.Pure and Abstract Functions: Pure functions are declared by prepending modifiers static and pure in that order. They should be side-effect free and their postconditions must not contain resource assertions such as accessibility predicates. All accessibility constraints for the body of the function and for the postcondition should be ensured by the preconditions. In fact, since pure functions applications are side-effect-free, pre- and post-states of a function application are the same. Example:requires a != null; requires 0 <= n && n <= a.length; requires (\forall* int j; 0 <= j && j < a.length; Perm(a[j],1\2)); ensures (n==a.length)? \result == 0 : \result == a[n] + sumAll(a,n+1); static pure int sumAll(int []a, int n); Notice that in the example the function has no body. By doing so we declare an abstract function. When calling this function, its pre-conditions will be checked and its postconditions assumed. No correspondence should be assumed between pure and abstract functions. |
Return |
return can be used to exit the current method. return expr can be used within a method to return a specific object as a result. |
If-statement |
|
If-then-else | if (boolExpr) { ... } else { ... } |
For-loop | loop_invariant p; for (...) { ... } |
While-loop | loop_invariant p; while (boolExpr) { ... } |
Parallel block |
par contract { ... } OR par identifier(iters) contract { ... } . The identifier is optional and iters can be empty. iters specifies what variables are iterated over. Note that you can also extend a parallel block with another parallel block as follows: par contract { ... } and contract { ... } OR par identifier(iters) contract { ... } and identifier(iters) contract { ... } . |
Vector block |
vec (iters) { ... } is a variant of the parallel block where every step is executed in lock step. You do not need to specify a pre- and postcondition. iters should define what variables are iterated over, e.g., int i = 0..10 . |
Atomic block |
atomic(inv) { ... } performs the actions within the block atomically. As a result, other threads will not be able to see any intermediate results, only the result before or after executing the atomic block. inv refers to an invariant which stores permissions that are necessary when executing the atomic block. |
Barrier |
barrier(identifier) contract { } waits for all threads to reach this point in the program, then permissions can be redistributed amongst all threads, as specified in the contract, after which the threads are allowed to continue. The barrier needs to know how many threads should reach it before everyone is allowed to continue, this is done by specifying identifier which refers to a parallel block before the barrier. |
Fork a thread |
fork expr starts a new thread. |
Join a thread |
join expr waits for the specified thread to complete. |
Wait |
wait expr will pause the thread until it is notified to continue. |
Notify |
notify expr will notify another thread that it may continue if it is waiting. |
Acquire a lock | lock expr |
Release a lock | unlock expr |
Label |
label l indicates a location in the program that can be jumped to with goto l . |
Goto |
goto l indicates control flow must be transferred to the location of the label l . |
Note that for-loops and while-loops should be preceded with a contract consisting of one or more loop invariants. Parallel blocks require a contract in the form of requires/ensures clauses.
Code | Description |
---|---|
assert expr |
Defines an assert statement expr which describes a condition that should hold at the program point where this statement is defined. |
assume expr |
Defines a statement that assumes that expr holds. It can be put anywhere within the code. |
requires expr |
Defines the precondition as expr , i.e., expr must hold when calling this method. The precondition should be specified before the method declaration. |
ensures expr |
Defines the postcondition as expr , i.e., expr must hold when completing this method. The postcondition should be specified before the method declaration. |
context expr |
This is an abbreviation that combines the statements requires expr and ensures expr . This statement should be specified before the method declaration. |
loop_invariant expr |
Defines a loop invariant expr which is a condition that must hold when entering the loop and after each loop iteration. A loop invariant should be specified before the corresponding loop. |
context_everywhere expr |
This is an abbreviation that combines the statement requires expr and ensures expr . Moreover, it also adds loop_invariant expr to all loops within the method. This statement should be specified before the method declaration. |
given T p |
Defines that a ghost input parameter (specification-only parameter) of type T with the name p is passed when this method is called. This statement should be specified before the method declaration. |
yields x |
Returns a ghost output parameter (specification-only parameter) to the callee of this method. This statement should be specified before the method declaration. |
with ... then ... |
with is used to pass a parameter to a method (which has a given statement) and then can be used to store a returned value from a yields statement. This statement is specified after the corresponding method call (on the same line) where you want to pass the parameter. All the ... should be replaced by an assignment. |
unfolding ... in expr |
Temporarily unfold definitions in (pure) expressions. The ... should be replaced by a predicate. |
refute expr |
Disproves expr at the given program point. This statement can be put anywhere in the code. Internally it is translated to assert !(expr) . |
inhale p |
Take in the specified permissions and properties. |
exhale p |
Discard the specified permissions |
fold x |
Wrap permissions inside the definition |
unfold x |
Unwrap a bundle of permissions |
witness x |
Declares witness names. This feature is deprecated and should no longer be used. |
Code | Description |
---|---|
process |
Type of actions and defined processes in histories. |
process1 + process 2 |
Do either process1 or process2
|
process1 * process 2 |
Sequential composition, do process1 followed by process2
|
process1 || process 2 |
Parallel composition, do process1 and process2 at the same time (interleaved) |
Code | Description |
---|---|
History hist |
Declare a History object called hist
|
HPerm(var, p) |
History-specific permissions where var refers to a variable in the history and p is the amount of permissions (a value between 0 and 1) |
Hist(var, p, val) |
Similar to PointsTo , it denotes permissions p for variable var (which is in a history). Moreover, variable var points to the value val . |
AbstractState(h, boolExpr) |
This can be used to check that the given boolean expression holds in the history (or future) h
|
action(h, perm, preState, postState) { ... } |
The action describes how the state of the history (or future) is changed by the code within { ... } . The pre- and post-state describe the state of the history (or future) in terms of processes. perm describes the permissions we have on the history (or future) and h is the name of the history (or future) |
create h |
Create a history. Note that the History class should be instantiated beforehand. |
destroy h, val |
Destroy a history h which has the value val
|
split h, p1, val1, p2, val2 |
Split a history (or future) into two such that the first part has permissions p1 and value val1 and the second part has permissions p2 and value val2
|
merge h, p1, val1, p2, val2 |
Merge two histories (or futures) such that resulting history h has permissions p1+p2 and consists of combination of the actions described in val1 and val2
|
modifies vars |
List of locations that might be modified |
accessible vars |
List of locations that might be accessed |
Code | Description |
---|---|
Future f |
Declare a Future object called f
|
Future(f, p, val) |
It denotes that we have permissions p on future f of which the state is val
|
choose f, p1, pre, post |
//TODO check this support and definition |
create f, val |
Create a future f with the initial state val
|
destroy f |
Destroy the future f
|
- APerm ??
-
Ls: send p to Lr, d
| Release permissionsp
to the statement labelledLr
withd
being the distance of dependence. This statement is labelledLs
. -
Lr: recv p from Ls, d
| Acquire permissionp
from the statement labelledLs
with the distance of dependenced
. This statement is labelledLr
. - Iteration contract | An iteration contract specifies the iteration's resources. The precondition of an iteration contract specifies the resources that a particular iteration needs, and the postcondition specifies the resources that become available after the execution of the iteration.
- \pointer(var, length, perm), \pointer_index(...)?
Tutorial
- Introduction
- Installing and Running VerCors
- Prototypical Verification Language
- Specification Syntax
- Permissions
- GPGPU Verification
- Axiomatic Data Types
- Arrays and Pointers
- Parallel Blocks
- Atomics and Locks
- Process Algebra Models
- Predicates
- Inheritance
- Exceptions & Goto
- VerCors by Error
- VeyMont
- Advanced Concepts
- Annex
- Case Studies
Developing for VerCors