=================================== DrNim User Guide =================================== :Author: Andreas Rumpf :Version: |nimversion| .. default-role:: code .. include:: rstcommon.rst .. contents:: Introduction ============ This document describes the usage of the *DrNim* tool. DrNim combines the Nim frontend with the `Z3 `_ proof engine, in order to allow verify/validate software written in Nim. DrNim's command-line options are the same as the Nim compiler's. DrNim currently only checks the sections of your code that are marked via `staticBoundChecks: on`: .. code-block:: nim {.push staticBoundChecks: on.} # <--- code section here ----> {.pop.} DrNim currently only tries to prove array indexing or subrange checks, overflow errors are *not* prevented. Overflows will be checked for in the future. Later versions of the **Nim compiler** will **assume** that the checks inside the `staticBoundChecks: on` environment have been proven correct and so it will **omit** the runtime checks. If you do not want this behavior, use instead `{.push staticBoundChecks: defined(nimDrNim).}`. This way the Nim compiler remains unaware of the performed proofs but DrNim will prove your code. Installation ============ Run `koch drnim`:cmd:, the executable will afterwards be in ``$nim/bin/drnim``. Motivating Example ================== The follow example highlights what DrNim can easily do, even without additional annotations: .. code-block:: nim {.push staticBoundChecks: on.} proc sum(a: openArray[int]): int = for i in 0..a.len: result += a[i] {.pop.} echo sum([1, 2, 3]) This program contains a famous "index out of bounds" bug. DrNim detects it and produces the following error message:: cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck] In other words for `i == 0` and `a.len == 0` (for example!) there would be an index out of bounds error. Pre-, postconditions and invariants =================================== DrNim adds 4 additional annotations (pragmas) to Nim: - `requires`:idx: - `ensures`:idx: - `invariant`:idx: - `assume`:idx: These pragmas are ignored by the Nim compiler so that they don't have to be disabled via `when defined(nimDrNim)`. Invariant --------- An `invariant` is a proposition that must be true after every loop iteration, it's tied to the loop body it's part of. Requires -------- A `requires` annotation describes what the function expects to be true before it's called so that it can perform its operation. A `requires` annotation is also called a `precondition`:idx:. Ensures ------- An `ensures` annotation describes what will be true after the function call. An `ensures` annotation is also called a `postcondition`:idx:. Assume ------ An `assume` annotation describes what DrNim should **assume** to be true in this section of the program. It is an unsafe escape mechanism comparable to Nim's `cast` statement. Use it only when you really know better than DrNim. You should add a comment to a paper that proves the proposition you assume. Example: insertionSort ====================== **Note**: This example does not yet work with DrNim. .. code-block:: nim import std / logic proc insertionSort(a: var openArray[int]) {. ensures: forall(i in 1.. 0 and a[t-1] > a[t]: {.invariant: k < a.len.} {.invariant: 0 <= t and t <= k.} {.invariant: forall(j in 1..k, i in 0..`. A `prop` is either a comparison or a compound:: prop = nim_bool_expression | prop 'and' prop | prop 'or' prop | prop '->' prop # implication | prop '<->' prop | 'not' prop | '(' prop ')' # you can group props via () | forallProp | existsProp forallProp = 'forall' '(' quantifierList ',' prop ')' existsProp = 'exists' '(' quantifierList ',' prop ')' quantifierList = quantifier (',' quantifier)* quantifier = 'in' nim_iteration_expression `nim_iteration_expression` here is an ordinary expression of Nim code that describes an iteration space, for example `1..4` or `1.. a.len`. The supported subset of Nim code that can be used in these expressions is currently underspecified but `let` variables, function parameters and `result` (which represents the function's final result) are amenable for verification. The expressions must not have any side-effects and must terminate. The operators `forall`, `exists`, `->`, `<->` have to imported from `std / logic`.