Skip to content

Commit e89e39a

Browse files
ntson-awsthanhnguyen-awszhassan-aws
authored
add README.md, RustString library, Iterator, general functions (#1)
README.md: documentation (installation, description) of the library RustString.lean: the RustString library which includes all the equivalent Lean versions of Rust String functions in Rust standard library, proof of correctness and supporting theorems/lemma Basic.lean: Generic lemmas for logic, Nat, List Iterator: modeling Rust Iterator in Lean --------- Co-authored-by: THANH NGUYEN <[email protected]> Co-authored-by: Zyad Hassan <[email protected]>
1 parent 2a6f787 commit e89e39a

10 files changed

+6566
-2
lines changed

README.md

+170
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,177 @@
1+
<!---
2+
-- Copyright Kani Contributors
3+
-- SPDX-License-Identifier: Apache-2.0 OR MIT
4+
--->
5+
16
## rust-lean-models
27

38
Lean models of various Rust libraries to facilitate Lean-based verification of Rust programs.
49

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+
5175
## Security
6176

7177
See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.

RustLeanModels.lean

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
-- Copyright Kani Contributors
2+
-- SPDX-License-Identifier: Apache-2.0 OR MIT
13
-- This module serves as the root of the `RustLeanModels` library.
24
-- Import modules here that should be built as part of the library.
3-
import «RustLeanModels».Basic
5+
import «RustLeanModels».Basic

0 commit comments

Comments
 (0)