1 Introduction
The formal verification of computer programs is a very challenging task. If the program operates
on an unbounded domain of values, the only general verification technique is to generate, from
the text of the program and its specification, verification conditions, i.e., logical formulas whose
validity ensures the correctness of the program with respect to its specification. The genera-
tion of such conditions generally requires from the human additional program annotations that
express meta-knowledge about the program such as loop invariants and termination measures.
Since for more complex programs proofs of these formulas by fully automatic reasoners rarely
succeed, typically interactive proving assistants are applied where the human helps the software
to construct successful proofs.
This may become a frustrating task, because usually the first verification attempts are doomed
to fail: the verification conditions are often not valid due to (also subtle) deficiencies in the
program, its specification, or annotations. The main problem is then to find out whether a proof
fails because of an inadequate proof strategy or because the condition is not valid and, if the
later is the case, which errors make the formula invalid. Typically, therefore most time and effort
in verification is actually spent in attempts to prove invalid verification conditions, often due to
inadequate annotations, in particular due to loop invariants that are too strong or too weak.
For this reason, programs are often restricted to make fully automatic verification feasible that
copes without extra program annotations and without manual proving efforts. One possibility
is to restrict the domain of a program such that it only operates on a finite number of values,
which allows to apply model checkers that investigate all possible executions of a program.
Another possibility is to limit the length of executions being considered; then bounded model
checkers (typically based on SMT solvers, i.e., automatic decision procedures for combinations
of decidable logical theories) are able to check the correctness of a program for a subset of
the executions. However, while being fully automatic, (bounded) model checking is time- and
memory-consuming, and ultimately does not help to verify the general correctness of a program
operating on unbounded domains with executions of unbounded length.
Based on prior expertise with computer-supported program verification, also in educational
scenarios [27, 22], we have started the development of RISCAL (RISC Algorithm Language) as
an attempt to make program verification less painful. The term “algorithm language” indicates
that RISCAL is intended to model, rather than low-level code, high-level algorithms as can be
found in textbooks on discrete mathematics [25]. RISCAL thus provides a rich collection of data
types (e.g., sets and maps) and operations (e.g., quantified formulas as Boolean conditions and
implicit definitions of values by formulas) but only cares about the correctness of execution, not
its efficiency. The core idea behind RISCAL is to use automatic techniques to find problems in
a program, its specification, or its annotations, that may prevent a successful verification before
actually attempting to prove verification conditions; we thus aim to start a proof of verification
conditions only when we are reasonably confident that they are indeed valid.
As a first step towards this goal, RISCAL restricts in a program P all program variables and
mathematical variables to finite types where the number of values of a type T, however, may
depend on an unspecified constant n ∈ N. If we set n to some concrete value c, we get an
instance P
c
of P where all specifications and annotations can be effectively evaluated during the
execution of the program (runtime assertion checking). Furthermore, we can execute a program
4