Skip to content

Commit 9f66be5

Browse files
committed
Enforce safety of proof principles for higher-order store
1 parent 21c7fd5 commit 9f66be5

File tree

7 files changed

+126
-13
lines changed

7 files changed

+126
-13
lines changed

AppendOnlyLinkedList.v

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ Require Import Coq.Program.Equality.
1616

1717
Inductive appList : Set :=
1818
| rcons : nat -> ref{option appList|any}[optset appList,optset appList] -> appList.
19+
Global Instance pure_applist : pure_type appList.
20+
Global Instance pure_option {A:Set}`{pure_type A} : pure_type (@option A).
1921

2022
Definition alist := ref{option appList|any}[optset appList, optset appList].
23+
Global Instance pure_alist : pure_type alist.
2124

2225
(** A remarkable number of the generated proof goals can be solved by
2326
firstorder reasoning or basic proof search. *)

LinkedList.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Inductive list_imm : hrel rgrList' :=
6161
| imm_cons : forall h h' P R G n (tl:ref{rgrList'|P}[R,G]),
6262
list_imm (h[tl]) (h'[tl]) h h' ->
6363
list_imm (rgrl_cons' rgrList' P R G n tl) (rgrl_cons' rgrList' P R G n tl) h h'.
64+
Instance pure_rgrlist : pure_type rgrList'.
6465
(** Ideally we'd like to enforce correct instantiation of
6566
the recursive components via a predicate like this:
6667
<<
@@ -197,7 +198,7 @@ Next Obligation. firstorder. eapply plumb_precise; eauto. firstorder. Qed. (** D
197198
Program Definition cons { Γ P Q}`{precise_pred P} n (tl meta_tl:@list P Q)
198199
: rgref Γ (list P _ (* (fun l h=> locally_const list_imm->l=rgrl_cons' rgrList' P list_imm list_imm n (convert_P _ _ _ tl))*)) Γ :=
199200
(*Alloc! (rgrl_cons' rgrList' P list_imm list_imm n (convert_P _ _ _ tl)).*)
200-
alloc' _ _ _ (rgrl_cons' rgrList' P list_imm list_imm n (convert_P _ _ _ tl)) (rgrl_cons' rgrList' P list_imm list_imm n (convert_P _ _ _ meta_tl)) _ _ _ _ _.
201+
alloc' _ _ _ (rgrl_cons' rgrList' P list_imm list_imm n (convert_P _ _ _ tl)) (rgrl_cons' rgrList' P list_imm list_imm n (convert_P _ _ _ meta_tl)) _ _ _ _ _ _.
201202
Next Obligation. firstorder. eapply plumb_stable; eauto. firstorder. eapply stable_P_hack; eauto. Qed.
202203
(** Again, previous goal is solved by things the obligation tactic should be doing... *)
203204
Next Obligation. constructor.
@@ -209,6 +210,7 @@ Check cons.
209210
Notation "'RGCons' n tl ::" := (@cons _ _ _ _ n tl ({{{tl}}})) (at level 100).
210211

211212
Record list_container {P Q} := mkList {head : @list P Q}.
213+
Instance pure_container {P Q}: pure_type (@list_container P Q).
212214

213215
Inductive reach_list_head : forall {Q Q'}{T:Set}{P R G} (p:ref{T|P}[R,G]) (a:@list_container Q Q'), Prop :=
214216
| reach_container_head : forall Q Q' T P R G p lst, @reach_list_head Q Q' T P R G p (mkList Q Q' lst).

MemoTable.v

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Require Import RGref.DSL.DSL.
2+
Require Import Coq.Arith.Arith.
3+
4+
Section MemoTable.
5+
6+
(* Apparently the instances below stick around after the section ends... *)
7+
Variable B : nat -> Set.
8+
Variable f : forall (x:nat), B x.
9+
Parameter safe_f : Safe f.
10+
Existing Instance safe_f.
11+
Parameter safe_B : Safe B.
12+
Existing Instance safe_B.
13+
14+
Definition prefernat {P:nat->Set}(g:forall x:nat, P x)(a:nat) : forall x:nat, P x.
15+
refine(let v := g a in
16+
(fun x => if eq_nat_dec x a then _ else g x)).
17+
subst. exact v.
18+
Defined.
19+
Print prefernat.
20+
21+
Definition obs_equiv {A:Set}{P:A->Set}(f g:forall x, P x)(_ _:heap) :=
22+
forall x, f x = g x.
23+
Lemma obs_eq_refl : forall A P, hreflexive (@obs_equiv A P).
24+
Proof. intros; red. compute. eauto. Qed.
25+
Hint Resolve obs_eq_refl.
26+
Lemma precise_obs_equiv : precise_rel (@obs_equiv nat (fun _ => nat)).
27+
Proof. compute. intuition. Qed.
28+
Hint Resolve precise_obs_equiv.
29+
30+
Instance safe_prefernat : Safe (@prefernat).
31+
(* Instance prior_safe (r:ref{forall x:nat,B x|any}[obs_equiv,obs_equiv]) (n:nat)
32+
: Safe (@prefernat B (@deref _ _ _ _ _ _ (obs_eq_refl _ _) eq_refl r) n). *)
33+
Instance prior_safe (r:ref{forall x:nat,B x|any}[obs_equiv,obs_equiv]) (n:nat)
34+
: ESafe 0 (@prefernat B f n).
35+
repeat solve_applications.
36+
Defined.
37+
38+
Program Definition prioritize {Γ} (r:ref{forall x:nat,B x|any}[obs_equiv,obs_equiv]) (n:nat) : rgref Γ unit Γ :=
39+
[r]:= prefernat (!r) n.
40+
Next Obligation.
41+
repeat solve_applications.
42+
Qed.
43+
Next Obligation.
44+
red. unfold prefernat. intros. induction (eq_nat_dec x n); intuition; eauto.
45+
subst. compute. eauto.
46+
Qed.
47+
(* Doesn't (and shouldn't!) typecheck *)
48+
Program Example should_not_typecheck {Γ} (r:ref{nat|any}[havoc,havoc]) : rgref Γ (ref{nat->nat|any}[havoc,havoc]) Γ :=
49+
Alloc (fun x => !r). (*;
50+
prioritize _ r 0.
51+
(*Print should_not_typecheck.*)
52+
*)
53+
Next Obligation. compute; eauto. Defined.
54+
Next Obligation.
55+
(* Cannot typecheck because (λ x:N. !r) is in fact not safe! *)
56+
Admitted.
57+
58+
End MemoTable.

RCC.v

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ Module Type RCC.
3535
{pres:(forall h, P meta_x h -> P meta_e (hwrite x meta_e h))}
3636
, rgref Γ unit Γ.
3737
*)
38-
Parameter rcc_write : forall {Γ Γ'}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}{l:lock}{w:var}{pf:tymember w (ref{lockwitness l | locked}[empty,locked-->unlocked]) Γ}(x:rccref A P R G l)(e:rgref Γ A Γ')
38+
Parameter rcc_write : forall {Γ Γ'}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}{l:lock}{w:var}{pf:tymember w (ref{lockwitness l | locked}[empty,locked-->unlocked]) Γ}(x:rccref A P R G l)(e:rgref Γ A Γ')`{ESafe 0 _ e}
3939
(meta_x_deref:rgref Γ A Γ') (meta_e_fold:rgref Γ A Γ')
4040
{guar:forall h env, G (valueOf _ _ _ env h meta_x_deref) (valueOf _ _ _ env h e) h (hwrite x (valueOf _ _ _ env h e) h)}
4141
{pres:(forall h env, P (valueOf _ _ _ env h meta_x_deref) h -> P (valueOf _ _ _ env h meta_e_fold) (hwrite x (valueOf _ _ _ env h meta_e_fold) h))}
4242
, rgref Γ unit Γ'.
4343

4444
Parameter rcc_alloc : forall {Γ}{T:Set}{RT:ImmediateReachability T}{CT:Containment T}{FT:rel_fold T} P R G (l:lock) (e:T),
45+
`(ESafe 0 e) ->
4546
stable P R -> (* predicate is stable *)
4647
(forall h, P e h) -> (* predicate is true *)
4748
precise_pred P -> (* P precise *)
@@ -76,21 +77,22 @@ Module RCCImpl : RCC.
7677
{pres:(forall h, P meta_x h -> P meta_e (heap_write x meta_e h))}
7778
: rgref Γ unit Γ :=
7879
@write' Γ A _ P R G _ x e meta_x meta_e _ _.*)
79-
Program Definition rcc_write {Γ Γ'}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}{l:lock}{w:var}{pf:tymember w (ref{lockwitness l | locked}[empty,locked-->unlocked]) Γ}(x:rccref A P R G l)(e:rgref Γ A Γ')
80+
Program Definition rcc_write {Γ Γ'}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}{l:lock}{w:var}{pf:tymember w (ref{lockwitness l | locked}[empty,locked-->unlocked]) Γ}(x:rccref A P R G l)(e:rgref Γ A Γ')`{ESafe 0 _ e}
8081
(meta_x_deref:rgref Γ A Γ') (meta_e_fold:rgref Γ A Γ')
8182
{guar:forall h env, G (valueOf _ _ _ env h meta_x_deref) (valueOf _ _ _ env h e) h (hwrite l _ _ _ _ x (valueOf _ _ _ env h e) h)}
8283
{pres:(forall h env, P (valueOf _ _ _ env h meta_x_deref) h -> P (valueOf _ _ _ env h meta_e_fold) (hwrite _ _ _ _ _ x (valueOf _ _ _ env h meta_e_fold) h))}
8384
: rgref Γ unit Γ' :=
84-
@write_imp_exp Γ Γ' A _ P R G _ x e meta_x_deref meta_e_fold guar pres.
85+
@write_imp_exp Γ Γ' A _ P R G _ x e _ meta_x_deref meta_e_fold guar pres.
8586

8687
Program Definition rcc_alloc {Γ}{T:Set}{RT:ImmediateReachability T}{CT:Containment T}{FT:rel_fold T} P R G (l:lock) (e:T)
88+
`(ESafe 0 _ e)
8789
`(stable P R) (* predicate is stable *)
8890
`((forall h, P e h)) (* predicate is true *)
8991
`(precise_pred P) (* P precise *)
9092
`(precise_rel R) (* R precise *)
9193
`(precise_rel G) (* G precise *)
9294
: rgref Γ (rccref T P R G l) Γ :=
93-
alloc _ _ _ e _ _ _ _ _.
95+
alloc _ _ _ e _ _ _ _ _ _.
9496
Parameter acquire : forall {Γ}(w:var)(l:lock), rgref Γ unit (w:ref{lockwitness l | locked}[empty,locked-->unlocked],Γ).
9597
Parameter release : forall {Γ}{l:lock}(w:var){pf:(tymember w (ref{lockwitness l | locked}[empty,locked-->unlocked]) Γ)}, rgref Γ unit (tyrem pf).
9698
End RCCImpl.
@@ -113,13 +115,13 @@ Section RaceFreeMonotonicCounter.
113115
Program Definition read_counter {Γ}{l:lock}{w:var}{pf:tymember w (ref{lockwitness l | locked}[empty,locked-->unlocked]) Γ}
114116
(c:rf_monotonic_counter l) : rgref Γ nat Γ :=
115117
@rcc_read _ _ _ _ _ _ _ l w _ c _ _.
116-
Check @rcc_write.
117118
Local Obligation Tactic := intros; compute [increasing hreflexive]; eauto; try apply natsp.
118119
Program Definition inc_monotonic {Γ l}{w:var}{pf:tymember w (ref{lockwitness l|locked}[empty,locked-->unlocked]) Γ} (p:rf_monotonic_counter l) : rgref Γ unit Γ :=
119120
(*rccwrite p <:= ((rcc_read p _ _) + 1).*)
120121
(* We can't directly add to the read result because that result is monadic. Instead we have to use pureApp. *)
121122
@rcc_write Γ Γ nat _ any increasing increasing _ l _ _ p
122123
(@pureApp _ _ _ _ _ (plus 1) (@rcc_read Γ _ _ _ _ _ _ l w pf p _ _))
124+
_
123125
({{{@rcc_read Γ _ _ _ _ _ _ l w pf p _ _}}})
124126
({{{pureApp _ _ _ _ _ (plus 1) (@rcc_read Γ _ _ _ _ _ _ l w pf p _ _)}}}) _ _.
125127
Next Obligation.
@@ -136,7 +138,7 @@ Section RaceFreeMonotonicCounter.
136138
Local Obligation Tactic := intros; eauto with arith; compute; auto with arith; repeat constructor.
137139
Program Definition mkRFCounter {Γ} (l:lock) : rgref Γ (rf_monotonic_counter l) Γ :=
138140
(*RCCAlloc l 0.*)
139-
rcc_alloc _ _ _ l 0 _ _ _ _ _.
141+
rcc_alloc _ _ _ l 0 _ _ _ _ _ _.
140142
(** Again, remember that the lock witness should really be in a monadic context *)
141143
Parameter w : var.
142144
Program Example test_counter {Γ} (l:lock) : rgref Γ unit Γ :=

RGref/DSL/Core.v

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ Global Instance ref_fold `{A:Set,P:hpred A,R:hrel A,G:hrel A} : rel_fold (ref{A|
246246
(* We'll admit the runtime fold for references; the semantics for proofs will need an extensional treatment
247247
as an axiom. *)
248248
Admitted.
249+
Global Instance fn_fold {T : Set}{B : T -> Set} : rel_fold (forall x : T, B x) := {
250+
rgfold := fun _ _ => (forall x : T, B x);
251+
fold := fun _ _ x => x
252+
}.
253+
249254

250255
(** TODO: polymorphic lists *)
251256

RGref/DSL/Monad.v

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,45 @@ Axiom rgret : forall {Γ:tyenv}{A:Set}(a:A), rgref Γ A Γ .
3535

3636
Axiom dropvar : forall {Γ} (v:var) (t:Set) (tm:tymember v t Γ), rgref Γ unit (tyrem tm).
3737

38+
(** * Higher-Order Safety *)
39+
(** The following typeclass network prohibits latent dereference expressions in the heap, alleviating the need for
40+
extra caution in proof principles, and removing the need for the type-sensitive semantics in the PLDI'13 paper.
41+
It is the subject of a submission to POPL'14. *)
42+
(* Some background stuff... *)
43+
Instance n2n : ImmediateReachability (nat -> nat) := { imm_reachable_from_in := fun _ _ _ _ _ _ => False }.
44+
Print Containment.
45+
Instance n2nc : Containment (nat -> nat) := {contains := fun _ => False}.
46+
47+
Instance n2n' : ImmediateReachability (forall x : nat, (fun _ : nat => nat) x) := { imm_reachable_from_in := fun _ _ _ _ _ _ => False }.
48+
49+
50+
Class Library {T:Type}(o:T) : Prop := {}.
51+
Class Safe {T:Type}(t:T) : Prop := {}.
52+
Class SafeType (T:Type) : Prop := {}.
53+
Instance safe_lib {T:Type}{o:T}`{Library T o} : Safe o.
54+
Instance val_of_safe_type {T:Type}`{SafeType T}(t:T) : Safe t.
55+
Instance imp_of_safe_type {T:Set}`{SafeType T}{Γ}(t : rgref Γ T Γ) : Safe t.
56+
Instance puretype_safe {T:Set}`{pure_type T} : SafeType T.
57+
58+
Class ESafe (n:nat){T:Type}(t:T) : Prop := { force_proof : False }.
59+
Instance safe_esafe {T:Type}(t:T){n:nat}`{Safe _ t} : ESafe n t. Admitted.
60+
Instance esafe_ind {A:Type}{B:A->Type}{n:nat}(f:forall x:A, B x)`{forall a:A, Safe a -> ESafe n (f a)} : ESafe (S n) f.
61+
Admitted.
62+
Instance deref_esafe {A B:Set}{P R G}`{rel_fold A}(r:ref{A|P}[R,G]){a b} : ESafe 0 (@deref A B _ P R G a b r).
63+
Admitted.
64+
Instance esafe_app {A:Type}{B:A->Type}(f:forall x:A, B x)(a:A){n:nat}`{ESafe (S n) _ f}`{ESafe 0 _ a} : ESafe n (f a).
65+
Admitted.
66+
67+
Ltac solve_applications :=
68+
match goal with
69+
| [ |- @ESafe _ _ (?g _ _ _) ] => eapply @esafe_app with (f := g _ _)
70+
| [ |- @ESafe _ _ (?g _ _) ] => eapply @esafe_app with (f := g _)
71+
| [ |- @ESafe _ _ (?g _) ] => eapply @esafe_app with (f := g)
72+
end; eauto with typeclass_instances.
73+
74+
75+
76+
3877
(** The core problem with folding here is that we want the user (and automated provers!) to see the same dereference on either side of
3978
G's state. But G applies to elements of A, not [R,G]>>A. We could introduce an internal "cheat_deref" that skipped
4079
folding, but then the two sides aren't equal, and many automated tactics that treat !e as essentially an uninterpreted
@@ -63,15 +102,15 @@ Axiom dropvar : forall {Γ} (v:var) (t:Set) (tm:tymember v t Γ), rgref Γ unit
63102
G (!l) e h (heap_write l e h)}
64103
{pres:(forall h (l:ref{A|P}[R,G]), P (!l) h -> P e (heap_write l e h))}
65104
, rgref Γ unit Γ.*)
66-
Program Axiom write' : forall {Γ:tyenv}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}(x:ref{A|P}[R,G])(e:A)
105+
Program Axiom write' : forall {Γ:tyenv}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}(x:ref{A|P}[R,G])(e:A)`{ESafe 0 A e}
67106
(meta_x_deref:A) (meta_e_fold:A)
68107
(** These meta args are notationally expanded x and e using the identity relation folding *)
69108
(*{guar:forall h, G (!x) e h (heap_write x e h)} *)
70109
{guar:forall h, (forall A (fa:rel_fold A), fa = meta_fold) -> G (meta_x_deref) e h (heap_write x e h)}
71110
(** temporarily not using meta_e_fold... the cases where I needed the "nop" behavior are once where the types are actually equal *)
72111
{pres:(forall h, P meta_x_deref h -> P meta_e_fold (heap_write x meta_e_fold h))}
73112
, rgref Γ unit Γ.
74-
Notation "[ x ]:= e" := (@write' _ _ _ _ _ _ _ x e ({{{!x}}}) ({{{e}}}) _ _) (at level 70).
113+
Notation "[ x ]:= e" := (@write' _ _ _ _ _ _ _ x e _ ({{{!x}}}) ({{{e}}}) _ _) (at level 70).
75114
(** TODO: heap writes that update the predicate. Because of the monadic style, we'll actually
76115
need a new axiom and syntax support for this, to rebind the variable at the strengthened type *)
77116

@@ -99,6 +138,7 @@ Program Axiom read_imp : forall {Γ}{A B:Set}`{rel_fold A}{P R G}`{hreflexive G}
99138

100139
(* Writing with an impure source expression (and direct ref value) *)
101140
Program Axiom write_imp_exp : forall {Γ Γ'}{A:Set}`{rel_fold A}{P R G}`{hreflexive G}(x:ref{A|P}[R,G])(e:rgref Γ A Γ')
141+
`{ESafe 0 _ e}
102142
(meta_x_deref:rgref Γ A Γ') (meta_e_fold:rgref Γ A Γ')
103143
{guar:forall h env, G (valueOf _ _ _ env h meta_x_deref) (valueOf _ _ _ env h e) h (heap_write x (valueOf _ _ _ env h e) h)}
104144
{pres:(forall h env, P (valueOf _ _ _ env h meta_x_deref) h -> P (valueOf _ _ _ env h meta_e_fold) (heap_write x (valueOf _ _ _ env h meta_e_fold) h))}
@@ -109,36 +149,39 @@ Definition locally_const {A:Set} (R:hrel A) := forall a a' h h', R a a' h h' ->
109149

110150

111151
Axiom alloc : forall {Γ}{T:Set}{RT:ImmediateReachability T}{CT:Containment T}{FT:rel_fold T} P R G (e:T),
152+
ESafe 0 e ->
112153
stable P R -> (* predicate is stable *)
113154
(forall h, P e h) -> (* predicate is true *)
114155
precise_pred P -> (* P precise *)
115156
precise_rel R -> (* R precise *)
116157
precise_rel G -> (* G precise *)
117158
rgref Γ (ref{T|P}[R,G]) Γ.
118-
Notation "'Alloc' e" := (alloc _ _ _ e _ _ _ _ _) (at level 70).
159+
Notation "'Alloc' e" := (alloc _ _ _ e _ _ _ _ _ _) (at level 70).
119160
(** Sometimes it is useful to refine P to give equality with the allocated value, which
120161
propagates assumptions and equalities across "statements." *)
121162
Axiom alloc' : forall {Γ}{T:Set}{RT:ImmediateReachability T}{CT:Containment T}{FT:rel_fold T} P R G (e:T) (meta_e:T),
163+
ESafe 0 e ->
122164
stable P R -> (* predicate is stable *)
123165
(forall h, P e h) -> (* predicate is true *)
124166
precise_pred P -> (* P precise *)
125167
precise_rel R -> (* R precise *)
126168
precise_rel G -> (* G precise *)
127169
rgref Γ (ref{T|P ⊓ (fun t=>fun h=> (locally_const R -> t=meta_e))}[R,G]) Γ.
128-
Notation "Alloc! e" := (alloc' _ _ _ e ({{{e}}}) _ _ _ _ _) (at level 70).
170+
Notation "Alloc! e" := (alloc' _ _ _ e ({{{e}}}) _ _ _ _ _ _) (at level 70).
129171

130172

131173

132174
Notation "x <- M ; N" := (rgref_bind M (fun x => N)) (at level 49, right associativity).
133175

134176
Axiom varalloc' : forall {Γ}{T:Set}{RT:ImmediateReachability T}{CT:Containment T}{FT:rel_fold T} P R G (v:var) (e:T) (meta_e:T),
177+
ESafe 0 e ->
135178
stable P R -> (* predicate is stable *)
136179
(forall h, P e h) -> (* predicate is true *)
137180
precise_pred P -> (* P precise *)
138181
precise_rel R -> (* R precise *)
139182
precise_rel G -> (* G precise *)
140183
rgref Γ unit (v:ref{T|P ⊓ (fun t=>fun h=> (locally_const R -> t=meta_e))}[R,G],Γ).
141-
Notation "VarAlloc! v e" := (varalloc' _ _ _ v e ({{{e}}}) _ _ _ _ _) (at level 70).
184+
Notation "VarAlloc! v e" := (varalloc' _ _ _ v e ({{{e}}}) _ _ _ _ _ _) (at level 70).
142185

143186

144187

compile.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
export CORE="RGref/DSL/LinearEnv.v RGref/DSL/DSL.v RGref/DSL/Theories.v RGref/DSL/Core.v RGref/DSL/Monad.v"
3-
export EXAMPLES="PosMonotonicCounter.v AppendOnlyLinkedList.v MonotonicCounter.v PrependOnlyPureList.v CounterModule.v RCC.v ReferenceImmutability.v LinkedList.v"
3+
export EXAMPLES="PosMonotonicCounter.v AppendOnlyLinkedList.v MonotonicCounter.v PrependOnlyPureList.v CounterModule.v RCC.v ReferenceImmutability.v LinkedList.v MemoTable.v"
44
export BUGS="KnownUnsoundnessExamples.v"
55
coq_makefile -arg -impredicative-set -R RGref RGref $CORE $EXAMPLES $BUGS > Makefile
66
touch .depend

0 commit comments

Comments
 (0)