-
Notifications
You must be signed in to change notification settings - Fork 14
/
adequacy.v
113 lines (91 loc) · 4.13 KB
/
adequacy.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
From iris.algebra Require Import excl.
From iris.base_logic.lib Require Export token.
From iris.heap_lang Require Import adequacy lang proofmode notation par.
From exercises Require Import spin_lock.
(* ################################################################# *)
(** * Adequacy *)
(**
Thus far, we have assumed that the weakest precondition is correct.
But what does that even mean? This is what is called _adequacy_, and
its definition depends on the properties we want it to satisfy. In
Iris, we have decided to focus on two properties:
- The program won't get stuck.
- The program satisfies the postcondition.
In this chapter, we go over how to define these properties and show
that the weakest precondition satisfies them.
HeapLang is defined using a step relation. This relation defines how
an expression can be reduced in a single step.
[prim_step e1 σ1 κs e2 σ2 efs]
Here, [e1] is the expression we want to reduce, and [σ1] is the
current state of the machine. [e2] and [σ2] are then the new
expression and new state. [efs] is a list of expressions representing
new threads spawned. Finally, [κs] represents a list of something
called observations. We won't use these, so [κs] will remain empty.
This relation isn't very nice to work with by itself. To fix this, we
package the state of the program into what we call a configuration.
Notice that this configuration takes a list of expressions. These
represent all the threads that are currently running.
*)
Definition cfg : Type := list expr * state.
(**
Now, let us define steps in terms of configurations. To model the
non-sequential behaviour of threads, we define steps to be a step in
any thread.
*)
Inductive step (ρ1 ρ2 : cfg) : Prop :=
step_atomic e1 σ1 e2 σ2 efs t1 t2 :
ρ1 = (t1 ++ e1 :: t2, σ1) →
ρ2 = (t1 ++ e2 :: t2 ++ efs, σ2) →
prim_step e1 σ1 [] e2 σ2 efs →
step ρ1 ρ2.
(**
With this, we can define multiple steps as the reflexive transitive
closure ([rtc]) of [step].
Now, a program is stuck if any thread cannot take a step and is not a
value. Conversely, a thread is not stuck if it is either a value or
can take a step.
*)
Definition not_stuck e σ :=
is_Some (to_val e) ∨ ∃ e' σ' efs, prim_step e σ [] e' σ' efs.
(**
With this, we can now express the first adequacy criterion: a program
will not get stuck if all possible future configurations are not
stuck.
[∀ t σ' e', rtc step ([e], σ) (t, σ') → e' ∈ t → not_stuck e' σ']
Likewise, for the second criterion, we say that a program satisfies a
postcondition when the main thread terminates to a value, and that
value satisfies the postcondition.
[∀ t σ' v, rtc step ([e], σ) (of_val v :: t, σ') → φ v σ']
Together, these two properties define [adequate]. However, [adequate]
takes an extra parameter of type [stuckness]. This parameter specifies
whether adequacy includes the first criterion.
The proof that the weakest precondition implies adequacy is:
[[
heap_adequacy Σ `{!heapGpreS Σ} s e σ φ : ∀ _ : heapGS Σ,
inv_heap_inv -∗ WP e @ s; ⊤ {{ v, ⌜φ v⌝}} → adequate s e σ (λ v σ,
φ v)
]]
To use this statement, we have to actually instantiate [Σ]. It needs
to contain all the kinds of resources we needed to prove the weakest
precondition. Let us use [prog] from the Spin Lock chapter as an
example. To prove [prog_spec], we used the resource of heaps as well
as tokens. The resources needed by the resource of heaps are all
contained in [heapΣ]. Likewise, the resources needed by token
([exclR unitO]) are contained in [tokenΣ].
*)
Definition progΣ : gFunctors := #[
heapΣ; (* The resources required by HeapLang *)
tokenΣ (* The resources we used for spin-lock *)
].
(** Finally, we can combine the pieces to prove adequacy. *)
Lemma prog_adequacy σ :
adequate NotStuck prog σ (λ v _, v = #0 ∨ v = #1).
Proof.
apply (heap_adequacy progΣ).
iIntros "%_ _".
iApply prog_spec.
Qed.
(**
With this, we now have a _closed_ proof that [prog] does not get
stuck, and if it terminates at some value [v], then [v = #0 ∨ v = #1].
*)