drnim.rst 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. ===================================
  2. DrNim User Guide
  3. ===================================
  4. :Author: Andreas Rumpf
  5. :Version: |nimversion|
  6. .. default-role:: code
  7. .. include:: rstcommon.rst
  8. .. contents::
  9. Introduction
  10. ============
  11. This document describes the usage of the *DrNim* tool. DrNim combines
  12. the Nim frontend with the `Z3 <https://github.com/Z3Prover/z3>`_ proof
  13. engine, in order to allow verify/validate software written in Nim.
  14. DrNim's command-line options are the same as the Nim compiler's.
  15. DrNim currently only checks the sections of your code that are marked
  16. via `staticBoundChecks: on`:
  17. .. code-block:: nim
  18. {.push staticBoundChecks: on.}
  19. # <--- code section here ---->
  20. {.pop.}
  21. DrNim currently only tries to prove array indexing or subrange checks,
  22. overflow errors are *not* prevented. Overflows will be checked for in
  23. the future.
  24. Later versions of the **Nim compiler** will **assume** that the checks inside
  25. the `staticBoundChecks: on` environment have been proven correct and so
  26. it will **omit** the runtime checks. If you do not want this behavior, use
  27. instead `{.push staticBoundChecks: defined(nimDrNim).}`. This way the
  28. Nim compiler remains unaware of the performed proofs but DrNim will prove
  29. your code.
  30. Installation
  31. ============
  32. Run `koch drnim`:cmd:, the executable will afterwards be
  33. in ``$nim/bin/drnim``.
  34. Motivating Example
  35. ==================
  36. The follow example highlights what DrNim can easily do, even
  37. without additional annotations:
  38. .. code-block:: nim
  39. {.push staticBoundChecks: on.}
  40. proc sum(a: openArray[int]): int =
  41. for i in 0..a.len:
  42. result += a[i]
  43. {.pop.}
  44. echo sum([1, 2, 3])
  45. This program contains a famous "index out of bounds" bug. DrNim
  46. detects it and produces the following error message::
  47. cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck]
  48. In other words for `i == 0` and `a.len == 0` (for example!) there would be
  49. an index out of bounds error.
  50. Pre-, postconditions and invariants
  51. ===================================
  52. DrNim adds 4 additional annotations (pragmas) to Nim:
  53. - `requires`:idx:
  54. - `ensures`:idx:
  55. - `invariant`:idx:
  56. - `assume`:idx:
  57. These pragmas are ignored by the Nim compiler so that they don't have to
  58. be disabled via `when defined(nimDrNim)`.
  59. Invariant
  60. ---------
  61. An `invariant` is a proposition that must be true after every loop
  62. iteration, it's tied to the loop body it's part of.
  63. Requires
  64. --------
  65. A `requires` annotation describes what the function expects to be true
  66. before it's called so that it can perform its operation. A `requires`
  67. annotation is also called a `precondition`:idx:.
  68. Ensures
  69. -------
  70. An `ensures` annotation describes what will be true after the function
  71. call. An `ensures` annotation is also called a `postcondition`:idx:.
  72. Assume
  73. ------
  74. An `assume` annotation describes what DrNim should **assume** to be true
  75. in this section of the program. It is an unsafe escape mechanism comparable
  76. to Nim's `cast` statement. Use it only when you really know better
  77. than DrNim. You should add a comment to a paper that proves the proposition
  78. you assume.
  79. Example: insertionSort
  80. ======================
  81. **Note**: This example does not yet work with DrNim.
  82. .. code-block:: nim
  83. import std / logic
  84. proc insertionSort(a: var openArray[int]) {.
  85. ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
  86. for k in 1 ..< a.len:
  87. {.invariant: 1 <= k and k <= a.len.}
  88. {.invariant: forall(j in 1..<k, i in 0..<j, a[i] <= a[j]).}
  89. var t = k
  90. while t > 0 and a[t-1] > a[t]:
  91. {.invariant: k < a.len.}
  92. {.invariant: 0 <= t and t <= k.}
  93. {.invariant: forall(j in 1..k, i in 0..<j, j == t or a[i] <= a[j]).}
  94. swap a[t], a[t-1]
  95. dec t
  96. Unfortunately, the invariants required to prove that this code is correct take more
  97. code than the imperative instructions. However, this effort can be compensated
  98. by the fact that the result needs very little testing. Be aware though that
  99. DrNim only proves that after `insertionSort` this condition holds::
  100. forall(i in 1..<a.len, a[i-1] <= a[i])
  101. This is required, but not sufficient to describe that a `sort` operation
  102. was performed. For example, the same postcondition is true for this proc
  103. which doesn't sort at all:
  104. .. code-block:: nim
  105. import std / logic
  106. proc insertionSort(a: var openArray[int]) {.
  107. ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
  108. # does not sort, overwrites `a`'s contents!
  109. for i in 0..<a.len: a[i] = i
  110. Syntax of propositions
  111. ======================
  112. The basic syntax is `ensures|requires|invariant: <prop>`.
  113. A `prop` is either a comparison or a compound::
  114. prop = nim_bool_expression
  115. | prop 'and' prop
  116. | prop 'or' prop
  117. | prop '->' prop # implication
  118. | prop '<->' prop
  119. | 'not' prop
  120. | '(' prop ')' # you can group props via ()
  121. | forallProp
  122. | existsProp
  123. forallProp = 'forall' '(' quantifierList ',' prop ')'
  124. existsProp = 'exists' '(' quantifierList ',' prop ')'
  125. quantifierList = quantifier (',' quantifier)*
  126. quantifier = <new identifier> 'in' nim_iteration_expression
  127. `nim_iteration_expression` here is an ordinary expression of Nim code
  128. that describes an iteration space, for example `1..4` or `1..<a.len`.
  129. `nim_bool_expression` here is an ordinary expression of Nim code of
  130. type `bool` like `a == 3` or `23 > a.len`.
  131. The supported subset of Nim code that can be used in these expressions
  132. is currently underspecified but `let` variables, function parameters
  133. and `result` (which represents the function's final result) are amenable
  134. for verification. The expressions must not have any side-effects and must
  135. terminate.
  136. The operators `forall`, `exists`, `->`, `<->` have to imported
  137. from `std / logic`.