drnim.rst 5.6 KB

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