|
| 1 | +<!--- |
| 2 | +-- Copyright Kani Contributors |
| 3 | +-- SPDX-License-Identifier: Apache-2.0 OR MIT |
| 4 | +---> |
| 5 | + |
1 | 6 | ## rust-lean-models
|
2 | 7 |
|
3 | 8 | Lean models of various Rust libraries to facilitate Lean-based verification of Rust programs.
|
4 | 9 |
|
| 10 | + |
| 11 | +## Description |
| 12 | +The library defines equivalent Lean versions of functions from the Rust standard library. |
| 13 | +This library is intended to be used for verifying Rust programs via Lean. |
| 14 | + |
| 15 | +The Lean implementation is based on the description of the Rust functions, which are published at https://doc.rust-lang.org/std. |
| 16 | +The library includes: |
| 17 | +- Definitions of functions equivalent to those from the Rust standard library |
| 18 | +- Proofs that these definitions are consistent with the description of the Rust functions |
| 19 | +- Supporting lemmas and theorems for proof construction |
| 20 | + |
| 21 | +## Installation |
| 22 | + |
| 23 | +- Add the following lines to your `lakefile.lean`: |
| 24 | + |
| 25 | + ` require «rust-lean-models» from git "https://github.com/model-checking/rust-lean-models" ` |
| 26 | + |
| 27 | +- Change the lean version in your `lean-toolchain` to the one in |
| 28 | +[lean-toolchain](https://github.com/model-checking/rust-lean-models/blob/main/lean-toolchain) |
| 29 | + |
| 30 | +- Run `lake update` in the terminal. |
| 31 | + |
| 32 | +- Import the packages and open the namespaces in your Lean files |
| 33 | +(see [ProofTutorial.lean](https://github.com/model-checking/rust-lean-models/tree/main/RustLeanModels/ProofTutorial.lean)) |
| 34 | + |
| 35 | + ```lean |
| 36 | + import RustLeanModels.Basic |
| 37 | + import RustLeanModels.RustString |
| 38 | + open RustString |
| 39 | + open Iterator |
| 40 | + ``` |
| 41 | + |
| 42 | +## Usage |
| 43 | +### Translating a Rust program |
| 44 | + |
| 45 | +For any Rust built-in functions which are used in user code, map it with |
| 46 | +the corresponding function name in the library (see the Tables below). |
| 47 | + |
| 48 | +### Proof Tutorial |
| 49 | +We demonstrate the applications of the library and some proof techniques |
| 50 | +for String programs in |
| 51 | +[ProofTutorial.lean](https://github.com/model-checking/rust-lean-models/tree/main/RustLeanModels/ProofTutorial.lean) |
| 52 | +through two simple programs that compute the longest common prefix |
| 53 | +and longest common substring of the two input strings. |
| 54 | +More examples can be found in |
| 55 | +[ProofExample.lean](https://github.com/model-checking/rust-lean-models/tree/main/RustLeanModels/ProofExample.lean). |
| 56 | + |
| 57 | +## Implementation |
| 58 | + |
| 59 | + |
| 60 | +### Recursive function definition |
| 61 | +For each Rust function, we provide a recursive Lean function. Implementing |
| 62 | +the equivalent functions in Lean recursively enables user to construct |
| 63 | +induction proofs conveniently. The Lean function has the same name as the Rust original, |
| 64 | +except when the Rust name clashes with a Lean keyword. In case of a clash, a Rust function 'func_name' |
| 65 | +is renamed to `rust_func_name` in Lean. |
| 66 | + |
| 67 | +Implementing a function recursively may requires some extra parameters. |
| 68 | +In those cases, first, we implement an auxiliary function (name: `func_name_aux`) which is defined |
| 69 | +recursively with the parameters, then we define the function `func_name` based on the auxiliary function |
| 70 | +with initial values for the parameters. |
| 71 | +For example, `char_indices` is defined based on `char_indices_aux` as |
| 72 | +`def char_indices (s: Str) := char_indices_aux s 0`. |
| 73 | + |
| 74 | +For Rust functions involving the `Pattern` type, we implement two recursive sub-functions |
| 75 | +(name: `func_name_char_filter` and `func_name_substring`) which replace the `Pattern` type |
| 76 | +in the input by either a char filter (or type`Char → Bool`) or a `List Char`. Then we define |
| 77 | +the function `func_name` based on these two sub-functions by matching the input of `Pattern` type. |
| 78 | +For example, `split` is defined by two sub-functions `split_char_filter` and `split_substring` as: |
| 79 | + |
| 80 | +```lean |
| 81 | +def split (s: Str) (p: Pattern) := match p with |
| 82 | +| Pattern.SingleChar c => split_char_filter s (fun x => x = c) |
| 83 | +| Pattern.ListChar l => split_char_filter s (fun x => x ∈ l) |
| 84 | +| Pattern.FilterFunction f => split_char_filter s f |
| 85 | +| Pattern.WholeString s1 => split_substring s s1 |
| 86 | +``` |
| 87 | +All recursive implementations are proven to be "correct" in the sense that |
| 88 | +they are consistent with the descriptions of the Rust versions (see below for more details). |
| 89 | + |
| 90 | +## Correctness Proofs |
| 91 | + |
| 92 | +### For functions that return `Bool` or can be defined based on other functions that have already been proven correct |
| 93 | + |
| 94 | +- First, we provide a variant Lean definition of the Rust function that we call the definitional |
| 95 | +version (with name `func_name_def`). This version is intended to match the documented description |
| 96 | +of the Rust function. Ideally, the definitional version should not contain recursion, except in some trivial cases, |
| 97 | +but it can make use of the recursive versions of other functions that have been previously proven correct. |
| 98 | + |
| 99 | +- Then, we state and prove a theorem that shows that the recursive and definitional versions of Rust |
| 100 | +function `func_name` are equivalent. This equivalence theorem is called `func_name_EQ` and |
| 101 | +it has type `func_name = func_name_def`. |
| 102 | +The theorem ensures that the function is implemented correctly |
| 103 | +and allows the two versions to be used interchangeably. |
| 104 | +In some cases, constructing a non-induction proof using the definitional version is more convenient. |
| 105 | + |
| 106 | +- For example, the function `is_char_boundary` has a definitional version: |
| 107 | + |
| 108 | + ```lean |
| 109 | + def is_char_boundary_def (s: Str) (p: Nat) := p ∈ (ListCharPos s ++ [byteSize s]) |
| 110 | + ``` |
| 111 | +
|
| 112 | + and an equivalence theorem |
| 113 | + |
| 114 | + ```lean |
| 115 | + theorem is_char_boundary_EQ : is_char_boundary s p = is_char_boundary_def s p |
| 116 | + ``` |
| 117 | +
|
| 118 | +When the description of a Rust function cannot be efficiently expressed in Lean (requires recursions, or is unintuitive), |
| 119 | +we can: |
| 120 | +- Define the definitional version (similar to Case 1) based on a recursive trivial function, then prove the equivalence theorem. |
| 121 | +For example, the `byteSize_def` function is defined on the simple function `sum_list_Nat` |
| 122 | +that computes the sum of a list of natural numbers: |
| 123 | + |
| 124 | + ```lean |
| 125 | + def sum_list_Nat (l : List Nat) := List.foldl Nat.add 0 l` |
| 126 | + def byteSize_def (s : List Char) : Nat := sum_list_Nat (List.map (fun x: Char => Char.utf8Size x) s) |
| 127 | + ``` |
| 128 | +
|
| 129 | +- Define and prove the correctness of one/some subordinate function(s), |
| 130 | +then define the definitional version based on them. |
| 131 | + For example, to define `split_inclusive_char_filter_def`, firstly we define and prove the coreectness of two functions: |
| 132 | + - `list_char_filter_charIndex (s: Str) (f: Char → Bool)`: returns the list of positions of characters in `s` satisfying the filter `f` |
| 133 | +
|
| 134 | + - `split_at_charIndex_list (s: Str) (l: List Nat)`: split the strings `s` at positions in `l` |
| 135 | +
|
| 136 | + then define `split_inclusive_char_filter_def` based on them: |
| 137 | +
|
| 138 | + ```lean |
| 139 | + def split_inclusive_char_filter_def (s: Str) (f: Char → Bool):= split_at_charIndex_list s (List.map (fun x => x+1) (list_char_filter_charIndex s f)) |
| 140 | + ``` |
| 141 | +
|
| 142 | +### When the Rust documentation describes properties of the return value |
| 143 | +We state and prove a soundness theorem for the function with |
| 144 | +name: `func_name_sound` and type: `x = func_name input1 input2 ... ↔ properties of x`. |
| 145 | +For example, the soundness theorem for the function `floor_char_boundary` is |
| 146 | +
|
| 147 | +```lean |
| 148 | +theorem floor_char_boundary_sound: flp = floor_char_boundary s p |
| 149 | + ↔ (is_char_boundary s flp) ∧ (flp ≤ p) ∧ (∀ k, ((is_char_boundary s k) ∧ (k ≤ p)) → k ≤ flp ) |
| 150 | +``` |
| 151 | + |
| 152 | +- The soundness theorem should cover all the properties in the Rust documentation. |
| 153 | +- We make some minor/trivial adjustments when needed to express the properties in Lean. |
| 154 | +- The modus ponens (→) direction of the theorem is enough to ensure the correctness of the recursive version |
| 155 | +if the properties in the right-hand-side ensure that the function in the left-hand-side is deterministic. |
| 156 | +- The modus ponens reverse (←) direction ensures that we stated enough properties in right-hand-side such that |
| 157 | +it can be satisfied by only one function. |
| 158 | +- If the function returns an option, we separately state and prove two soundness theorems for the two cases |
| 159 | +of the return value: `func_name_none_sound` and `func_name_some_sound`. For example: |
| 160 | + |
| 161 | + ```lean |
| 162 | + theorem split_at_none_sound : split_at s p = none ↔ ¬ ∃ s1, List.IsPrefix s1 s ∧ byteSize s1 = p |
| 163 | + theorem split_at_some_sound : split_at s p = some (s1, s2) ↔ byteSize s1 = p ∧ s = s1 ++ s2 |
| 164 | + ``` |
| 165 | + |
| 166 | +- For functions involving the `Pattern` type, we separately state and prove two equivalent/soundness |
| 167 | +theorems for the two sub-functions discussed previously (`func_name_char_filter_EQ` and `func_name_substring_EQ`) |
| 168 | +or (`func_name_char_filter_sound` and `func_name_substring_sound`). For example: |
| 169 | + |
| 170 | + ```lean |
| 171 | + theorem contains_char_filter_EQ : contains_char_filter s f = contains_char_filter_def s f |
| 172 | + theorem contains_substring_EQ : contains_substring s p = contains_substring_def s p |
| 173 | + ``` |
| 174 | + |
5 | 175 | ## Security
|
6 | 176 |
|
7 | 177 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
|
|
0 commit comments