-
Notifications
You must be signed in to change notification settings - Fork 0
/
Rules.v
2080 lines (1616 loc) · 73.3 KB
/
Rules.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(** * Rules: Reasoning Rules *)
Set Implicit Arguments.
(** This file imports [LibSepDirect.v] instead of [Hprop.v] and [Himpl.v].
The file [LibSepDirect.v] contains definitions that are essentially similar
to those from [Hprop.v] and [Himpl.v], yet with one main difference:
[LibSepDirect] makes the definition of Separation Logic operators opaque.
As a result, one cannot unfold the definition of [hstar], [hpure], etc.
To carry out reasoning, one must use the introduction and elimination
lemmas (e.g. [hstar_intro], [hstar_elim]). These lemmas enforce
abstraction: they ensure that the proofs do not depend on the particular
choice of the definitions used for constructing Separation Logic. *)
From SLF Require Export LibSepReference.
From SLF Require Basic.
(* ################################################################# *)
(** * First Pass *)
(** In the previous chapters, we have:
- introduced the key heap predicate operators,
- introduced the notion of Separation Logic triple,
- introduced the entailment relation,
- introduced the structural rules of Separation Logic.
We are now ready to present the other reasoning rules,
which enable establishing properties of concrete programs.
These reasoning rules are proved correct with respect to the
semantics of the programming language in which the programs
are expressed. Thus, a necessary preliminary step is to present
the syntax and the semantics of a (toy) programming language,
for which we aim to provide Separation Logic reasoning rules.
The present chapter is thus organized as follows:
- definition of the syntax of the language,
- definition of the semantics of the language,
- statements of the reasoning rules associated with each of
the term constructions from the language,
- specification of the primitive operations of the language,
in particular those associated with memory operations,
- review of the 4 structural rules introduced in prior chapters,
- examples of practical verification proofs.
The bonus section (optional) also includes:
- proofs of the reasoning rules associated with each term construct,
- proofs of the specification of the primitive operations. *)
(* ================================================================= *)
(** ** Semantic of Terms *)
Module SyntaxAndSemantics.
(* ----------------------------------------------------------------- *)
(** *** Syntax *)
(** The syntax described next captures the "abstract syntax tree"
of a programming language. It follows a presentation that
distiguishes between closed values and terms. This presentation
is intended to simplify the definition and evaluation of the
substitution function: because values are always closed (i.e.,
no free variables in them), the substitution function never
needs to traverse through values.
The grammar for values includes unit, boolean, integers,
locations, functions, recursive functions, and primitive operations.
For example, [val_int 3] denotes the integer value [3]. The value
[val_fun x t] denotes the function [fun x => t], and the value
[val_fix f x t] denotes the function [fix f x => t], which is
written [let rec f x = t in f] in OCaml syntax.
For conciseness, we include just a few primitive operations:
[ref], [get], [set] and [free] for manipulating the mutable state,
the operation [add] to illustrate a simple arithmetic operation,
and the operation [div] to illustrate a partial operation. *)
Inductive val : Type :=
| val_unit : val
| val_bool : bool -> val
| val_int : int -> val
| val_loc : loc -> val
| val_fun : var -> trm -> val
| val_fix : var -> var -> trm -> val
| val_ref : val
| val_get : val
| val_set : val
| val_free : val
| val_add : val
| val_div : val
(** The grammar for terms includes values, variables, function definitions,
recursive function definitions, function applications, sequences,
let-bindings, and conditionals. *)
with trm : Type :=
| trm_val : val -> trm
| trm_var : var -> trm
| trm_fun : var -> trm -> trm
| trm_fix : var -> var -> trm -> trm
| trm_app : trm -> trm -> trm
| trm_seq : trm -> trm -> trm
| trm_let : var -> trm -> trm -> trm
| trm_if : trm -> trm -> trm -> trm.
(** Note that [trm_fun] and [trm_fix] denote functions that may feature free
variables, unlike [val_fun] and [val_fix] which denote closed values.
The intention is that the evaluation of a [trm_fun] in the empty context
produces a [val_fun] value. Likewise, a [trm_fix] eventually evaluates to
a [val_fix]. *)
(** Several value constructors are declared as coercions, to enable more
concise statements. For example, [val_loc] is declared as a coercion,
so that a location [p] of type [loc] can be viewed as the value
[val_loc p] where an expression of type [val] is expected. Likewise,
a boolean [b] may be viewed as the value [val_bool b], and an integer
[n] may be viewed as the value [val_int n]. *)
Coercion val_loc : loc >-> val.
Coercion val_bool : bool >-> val.
Coercion val_int : Z >-> val.
(* ----------------------------------------------------------------- *)
(** *** State *)
(** The language we consider is an imperative language, with primitive
functions for manipulating the state. Thus, the statement of the
evaluation rules involve a memory state.
Recall from [Hprop] that a state is represented as a finite map
from location to values. Finite maps are presented using the type
[fmap]. Details of the construction of finite maps are beyond the
scope of this course; details may be found in the the file
[LibSepFmap.v]. *)
Definition state : Type := fmap loc val.
(** For technical reasons related to the internal representation of finite
maps, to enable reading in a state, we need to justify that the grammar
of values is inhabited. This property is captured by the following
command, whose details are not relevant for understanding the rest of
the chapter. *)
Instance Inhab_val : Inhab val.
Proof using. apply (Inhab_of_val val_unit). Qed.
(* ----------------------------------------------------------------- *)
(** *** Substitution *)
(** The semantics of the evaluation of function is described by means
of a substitution function. The substitution function, written
[subst y w t], replaces all occurrences of a variable [y] with a
value [w] inside a term [t].
The substitution function is always the identity function on values,
because our language only considers closed values. In other words,
we define [subst y w (trm_val v) = (trm_val v)].
The substitution function, when reaching a variable, performs a
comparison between two variables. To that end, it exploits the
comparison function [var_eq x y], which produces a boolean value
indicating whether [x] and [y] denote the same variable. *)
(** "Optional contents": the remaining of this section describes further
details about the substitution function that may be safely skipped
over in first reading. *)
(** The substitution operation traverses all other language constructs
in a structural manner. It takes care of avoiding "variable capture"
when traversing binders: [subst y w t] does not recurse below the
scope of binders whose name is equal to [y]. For example, the result
of [subst y w (trm_let x t1 t2)] is defined as
[trm_let x (subst y w t1) (if var_eq x y then t2 else (subst y w t2))].
The auxiliary function [if_y_eq], which appears in the definition of
[subst] shown below, helps performing the factorizing the relevant
checks that prevent variable capture. *)
Fixpoint subst (y:var) (w:val) (t:trm) : trm :=
let aux t := subst y w t in
let if_y_eq x t1 t2 := if var_eq x y then t1 else t2 in
match t with
| trm_val v => trm_val v
| trm_var x => if_y_eq x (trm_val w) t
| trm_fun x t1 => trm_fun x (if_y_eq x t1 (aux t1))
| trm_fix f x t1 => trm_fix f x (if_y_eq f t1 (if_y_eq x t1 (aux t1)))
| trm_app t1 t2 => trm_app (aux t1) (aux t2)
| trm_seq t1 t2 => trm_seq (aux t1) (aux t2)
| trm_let x t1 t2 => trm_let x (aux t1) (if_y_eq x t2 (aux t2))
| trm_if t0 t1 t2 => trm_if (aux t0) (aux t1) (aux t2)
end.
(* ----------------------------------------------------------------- *)
(** *** Implicit Types and Coercions *)
(** To improve the readability of the evaluation rules stated further,
we take advantage of both implicit types and coercions.
The implicit types are defined as shown below. For example, the
first command indicates that variables whose name begins with the
letter 'b' are, by default, variables of type [bool]. *)
Implicit Types b : bool.
Implicit Types v r : val.
Implicit Types t : trm.
Implicit Types s : state.
(** We next introduce two key coercions. First, we declare
[trm_val] as a coercion, so that, instead of writing [trm_val v],
we may write simply [v] wherever a term is expected. *)
Coercion trm_val : val >-> trm.
(** Second, we declare [trm_app] as a "Funclass" coercion. This piece
of magic enables us to write [t1 t2] as a shorthand for [trm_app t1 t2].
The idea of associating [trm_app] as the "Funclass" coercion for the
type [trm] is that if a term [t1] of type [trm] is applied like a
function to an argument, then [t1] should be interpreted as [trm_app t1]. *)
Coercion trm_app : trm >-> Funclass.
(** Interestingly, the "Funclass" coercion for [trm_app] can be iterated.
The expression [t1 t2 t3] is parsed by Coq as [(t1 t2) t3]. The first
application [t1 t2] is interpreted as [trm_app t1 t2]. This expression,
which itself has type [trm], is applied to [t3]. Hence, it is interpreted
as [trm_app (trm_app t1 t2) t3], which indeed corresponds to a function
applied to two arguments. *)
(* ----------------------------------------------------------------- *)
(** *** Big-Step Semantics *)
(** The semantics is presented in big-step style. This presentation makes
it slightly easier to establish reasoning rules than with small-step
reduction rules, because both the big-step judgment and a triple
judgment describe complete execution, relating a term with the value
that it produces.
The big-step evaluation judgment, written [eval s t s' v], asserts that,
starting from state [s], the evaluation of the term [t] terminates in
a state [s'], producing an output value [v].
For simplicity, we assume terms to be in "A-normal form": the arguments
of applications and of conditionals are restricted to variables and value.
Such a requirement does not limit expressiveness, yet it simplifies the
statement of the evaluation rules.
For example, if a source program includes a conditional [trm_if t0 t1 t2],
then it is required that [t0] be either a variable or a value.
This is not a real restriction, because [trm_if t0 t1 t2] can always be
encoded as [let x = t0 in if x then t1 else t2].
The big-step judgment is inductively defined as follows. *)
Inductive eval : state -> trm -> state -> val -> Prop :=
(** 1. [eval] for values and function definitions.
A value evaluates to itself.
A term function evaluates to a value function.
Likewise for a recursive function. *)
| eval_val : forall s v,
eval s (trm_val v) s v
| eval_fun : forall s x t1,
eval s (trm_fun x t1) s (val_fun x t1)
| eval_fix : forall s f x t1,
eval s (trm_fix f x t1) s (val_fix f x t1)
(** 2. [eval] for function applications.
The beta reduction rule asserts that [(val_fun x t1) v2]
evaluates to the same result as [subst x v2 t1].
In the recursive case, [(val_fix f x t1) v2] evaluates to
[subst x v2 (subst f v1 t1)], where [v1] denotes the recursive
function itself, that is, [val_fix f x t1]. *)
| eval_app_fun : forall s1 s2 v1 v2 x t1 v,
v1 = val_fun x t1 ->
eval s1 (subst x v2 t1) s2 v ->
eval s1 (trm_app v1 v2) s2 v
| eval_app_fix : forall s1 s2 v1 v2 f x t1 v,
v1 = val_fix f x t1 ->
eval s1 (subst x v2 (subst f v1 t1)) s2 v ->
eval s1 (trm_app v1 v2) s2 v
(** 3. [eval] for structural constructs.
A sequence [trm_seq t1 t2] first evaluates [t1], taking the
state from [s1] to [s2], drops the result of [t1], then evaluates
[t2], taking the state from [s2] to [s3].
The let-binding [trm_let x t1 t2] is similar, except that the
variable [x] gets substituted for the result of [t1] inside [t2]. *)
| eval_seq : forall s1 s2 s3 t1 t2 v1 v,
eval s1 t1 s2 v1 ->
eval s2 t2 s3 v ->
eval s1 (trm_seq t1 t2) s3 v
| eval_let : forall s1 s2 s3 x t1 t2 v1 r,
eval s1 t1 s2 v1 ->
eval s2 (subst x v1 t2) s3 r ->
eval s1 (trm_let x t1 t2) s3 r
(** 4. [eval] for conditionals.
A conditional in a source program is assumed to be of the form
[if t0 then t1 else t2], where [t0] is either a variable or a
value. If it is a variable, then by the time it reaches an
evaluation position, the variable must have been substituted
by a value. Thus, the evaluation rule only considers the form
[if v0 then t1 else t2]. The value [v0] must be a boolean value,
otherwise evaluation gets stuck.
The term [trm_if (val_bool true) t1 t2] behaves like [t1], whereas
the term [trm_if (val_bool false) t1 t2] behaves like [t2].
This behavior is described by a single rule, leveraging Coq's "if"
constructor to factor out the two cases. *)
| eval_if : forall s1 s2 b v t1 t2,
eval s1 (if b then t1 else t2) s2 v ->
eval s1 (trm_if (val_bool b) t1 t2) s2 v
(** 5. [eval] for primitive stateless operations.
For similar reasons as explained above, the behavior of applied
primitive functions only need to be described for the case of value
arguments.
An arithmetic operation expects integer arguments. The addition
of [val_int n1] and [val_int n2] produces [val_int (n1 + n2)].
The division operation, on the same arguments, produces the
quotient [n1 / n2], under the assumption that the dividor [n2]
is non-zero. In other words, if a program performs a division
by zero, then it cannot satisfy the [eval] judgment. *)
| eval_add : forall s n1 n2,
eval s (val_add (val_int n1) (val_int n2)) s (val_int (n1 + n2))
| eval_div : forall s n1 n2,
n2 <> 0 ->
eval s (val_div (val_int n1) (val_int n2)) s (val_int (Z.quot n1 n2))
(** 6. [eval] for primitive operations on memory.
There remains to describe operations that act on the mutable store.
[val_ref v] allocates a fresh cell with contents [v]. The operation
returns the location, written [p], of the new cell. This location
must not be previously in the domain of the store [s].
[val_get (val_loc p)] reads the value in the store [s] at location [p].
The location must be bound to a value in the store, else evaluation
is stuck.
[val_set (val_loc p) v] updates the store at a location [p] assumed to
be bound in the store [s]. The operation modifies the store and returns
the unit value.
[val_free (val_loc p)] deallocates the cell at location [p]. *)
| eval_ref : forall s v p,
~ Fmap.indom s p ->
eval s (val_ref v) (Fmap.update s p v) (val_loc p)
| eval_get : forall s p,
Fmap.indom s p ->
eval s (val_get (val_loc p)) s (Fmap.read s p)
| eval_set : forall s p v,
Fmap.indom s p ->
eval s (val_set (val_loc p) v) (Fmap.update s p v) val_unit
| eval_free : forall s p,
Fmap.indom s p ->
eval s (val_free (val_loc p)) (Fmap.remove s p) val_unit.
End SyntaxAndSemantics.
(* ----------------------------------------------------------------- *)
(** *** Loading of Definitions from [Direcŧ] *)
(** Throughout the rest of this file, we rely not on the definitions
shown above, but on the definitions from [LibSepDirect.v]. The latter
are slightly more general, yet completely equivalent to the ones
presented above for the purpose of establishing the reasoning rules
that we are interested in. *)
(** To reduce the clutter in the statement of lemmas, we associate default
types to a number of common meta-variables. *)
Implicit Types x f : var.
Implicit Types b : bool.
Implicit Types p : loc.
Implicit Types n : int.
Implicit Types v w r : val.
Implicit Types t : trm.
Implicit Types h : heap.
Implicit Types s : state.
Implicit Types H : hprop.
Implicit Types Q : val->hprop.
(* ================================================================= *)
(** ** Rules for Terms *)
(** We next present reasoning rule for terms. Most of these Separation Logic
rules have a statement essentially identical to the statement of the
corresponding Hoare Logic rule. The main difference lies in their
interpretation: whereas Hoare Logic pre- and post-conditions describe
the full state, a Separation Logic rule describes only a fraction of
the mutable state. *)
(* ----------------------------------------------------------------- *)
(** *** Reasoning Rule for Sequences *)
(** Let us begin with the reasoning rule for sequences.
The Separation Logic reasoning rule for a sequence [t1;t2] is
essentially the same as that from Hoare logic. The rule is:
{H} t1 {fun v => H1} {H1} t2 {Q}
------------------------------------
{H} (t1;t2) {Q}
The Coq statement corresponding to the above rule is: *)
Parameter triple_seq : forall t1 t2 H Q H1,
triple t1 H (fun v => H1) ->
triple t2 H1 Q ->
triple (trm_seq t1 t2) H Q.
(** The variable [v] denotes the result of the evaluation of [t1]. For
well-typed programs, this result would always be [val_unit]. Yet,
because we here consider an untyped language, we do not bother
adding the constraint [v = val_unit]. Instead, we simply treat the
result of [t1] as a value irrelevant to the remaining of the
evaluation. *)
(* ----------------------------------------------------------------- *)
(** *** Reasoning Rule for Let-Bindings *)
(** Next, we present the reasoning rule for let-bindings. Here again,
there is nothing specific to Separation Logic, the rule would be
exactly the same in Hoare Logic.
The reasoning rule for a let binding [let x = t1 in t2] could
be stated, in informal writing, in the form:
{H} t1 {Q1} (forall x, {Q1 x} t2 {Q})
-----------------------------------------
{H} (let x = t1 in t2) {Q}
Yet, such a presentation makes a confusion between the [x] that
denotes a program variable in [let x = t1 in t2], and the [x]
that denotes a value when quantified as [forall x].
The correct statement involves a substitution from the variable
[x] to a value quantified as [forall (v:val)].
{H} t1 {Q1} (forall v, {Q1 v} (subst x v t2) {Q})
-----------------------------------------------------
{H} (let x = t1 in t2) {Q}
The corresponding Coq statement is thus as follows. *)
Parameter triple_let : forall x t1 t2 Q1 H Q,
triple t1 H Q1 ->
(forall v1, triple (subst x v1 t2) (Q1 v1) Q) ->
triple (trm_let x t1 t2) H Q.
(* ----------------------------------------------------------------- *)
(** *** Reasoning Rule for Conditionals *)
(** The rule for a conditional is, again, exactly like in Hoare logic.
b = true -> {H} t1 {Q} b = false -> {H} t2 {Q}
--------------------------------------------------
{H} (if b then t1 in t2) {Q}
The corresponding Coq statement appears next.
*)
Parameter triple_if_case : forall b t1 t2 H Q,
(b = true -> triple t1 H Q) ->
(b = false -> triple t2 H Q) ->
triple (trm_if (val_bool b) t1 t2) H Q.
(** Note that the two premises may be factorized into a single one
using Coq's conditional construct. Such an alternative
statement is discussed further in this chapter. *)
(* ----------------------------------------------------------------- *)
(** *** Reasoning Rule for Values *)
(** The rule for a value [v] can be written as a triple with an
empty precondition and a postcondition asserting that the
result value [r] is equal to [v], in the empty heap. Formally:
----------------------------
{\[]} v {fun r => \[r = v]}
It is however more convenient in practice to work with a judgment
whose conclusion is of the form [{H} v {Q}], for an arbitrary
[H] and [Q]. For this reason, we prever the following rule for
values.
H ==> Q v
---------
{H} v {Q}
It may not be completely obvious at first sight why this alternative
rule is equivalent to the former. We prove the equivalence further
in this chapter.
The Coq statement of the rule for values is thus as follows. *)
Parameter triple_val : forall v H Q,
H ==> Q v ->
triple (trm_val v) H Q.
(** Let us prove that the "minimalistic" rule [{\[]} v {fun r => \[r = v]}]
is equivalent to [triple_val]. *)
(** **** Exercise: 1 star, standard, especially useful (triple_val_minimal)
Prove that the alternative rule for values derivable from
[triple_val]. Hint: use the tactic [xsimpl] to conclude the proof. *)
Lemma triple_val_minimal : forall v,
triple (trm_val v) \[] (fun r => \[r = v]).
Proof using.
intros. apply triple_val. xsimpl. reflexivity.
Qed.
(** [] *)
(** **** Exercise: 2 stars, standard, especially useful (triple_val')
More interestingly, prove that [triple_val] is derivable
from [triple_val_minimal]. Hint: you will need to exploit
the appropriate structural rule(s). *)
Lemma triple_val' : forall v H Q,
H ==> Q v ->
triple (trm_val v) H Q.
Proof using.
intros. eapply triple_conseq. eapply triple_frame. apply triple_val_minimal.
xsimpl. xsimpl. intros. subst. assumption.
Qed.
(** [] *)
(** **** Exercise: 4 stars, standard, especially useful (triple_let_val)
Consider a term of the form [let x = v1 in t2], that is, where the
argument of the let-binding is already a value. State and prove a
reasoning rule of the form:
Lemma triple_let_val : forall x v1 t2 H Q,
... ->
triple (trm_let x v1 t2) H Q.
Hint: you'll need to guess the intermediate postcondition [Q1] associated
with the let-binding rule, and exploit the appropriate structural rules. *)
Lemma triple_let_val : forall x v1 t2 H Q,
triple (subst x v1 t2) H Q ->
triple (trm_let x v1 t2) H Q.
Proof using.
unfold triple. unfold hoare. intros. lets M: H0 H' h H1.
destruct M as [h' [v [M1 M2]]]. exists h' v. split.
eapply eval_let. apply eval_val. assumption. assumption.
Qed.
(** [] *)
(* ----------------------------------------------------------------- *)
(** *** Reasoning Rule for Functions *)
(** In addition to the reasoning rule for values, we need reasoning
rules for functions and recursive functions that appear as terms
in the source program (as opposed to appearing as values).
A function definition [trm_fun x t1], expressed as a subterm in a
program, evaluates to a value, more precisely to [val_fun x t1].
Again, we could consider a rule with an empty precondition:
------------------------------------------------------
{\[]} (trm_fun x t1) {fun r => \[r = val_fun x t1]}
However, we prefer a conclusion of the form [{H} (trm_fun x t1) {Q}].
We thus consider the following rule, very similar to [triple_val]. *)
Parameter triple_fun : forall x t1 H Q,
H ==> Q (val_fun x t1) ->
triple (trm_fun x t1) H Q.
(** The rule for recursive functions is similar. It is presented
further in the file. *)
(** Last but not least, we need a reasoning rule to reason about a
function application. Consider an application [trm_app v1 v2].
Assume [v1] to be a function, that is, to be of the form
(* [val_fun x t1]. Then, according to the beta-reduction rule, the *)
semantics of [trm_app v1 v2] is the same as that of [subst x v2 t1].
This reasoning rule is thus:
v1 = val_fun x t1 {H} (subst x v2 t1) {Q}
---------------------------------------------
{H} (trm_app v1 v2) {Q}
The corresponding Coq statement is as shown below. *)
Parameter triple_app_fun : forall x v1 v2 t1 H Q,
v1 = val_fun x t1 ->
triple (subst x v2 t1) H Q ->
triple (trm_app v1 v2) H Q.
(** The generalization to the application of recursive functions is
straightforward. It is discussed further in this chapter. *)
(* ================================================================= *)
(** ** Specification of Primitive Operations *)
(** Before we can tackle verification of actual programs, there remains
to present the specifications for the primitive operations.
Let us begin with basic arithmetic operations: addition and division. *)
(* ----------------------------------------------------------------- *)
(** *** Specification of Arithmetic Primitive Operations *)
(** Consider a term of the form [val_add n1 n2], which is short for
[trm_app (trm_app (trm_val val_add) (val_int n1)) (val_int n2)].
Indeed, recall that [val_int] is declared as a coercion.
The addition operation may execute in an empty state. It does not
modify the state, and returns the value [val_int (n1+n2)].
In the specification shown below, the precondition is written [\[]]
and the postcondition binds a return value [r] of type [val]
specified to be equal to [val_int (n1+n2)]. *)
Parameter triple_add : forall n1 n2,
triple (val_add n1 n2)
\[]
(fun r => \[r = val_int (n1 + n2)]).
(** The specification of the division operation [val_div n1 n2] is similar,
yet with the extra requirement that the argument [n2] must be nonzero.
This requirement [n2 <> 0] is a pure fact, which can be asserted as
part of the precondition, as follows. *)
Parameter triple_div : forall n1 n2,
triple (val_div n1 n2)
\[n2 <> 0]
(fun r => \[r = val_int (Z.quot n1 n2)]).
(** Equivalently, the requirement [n2 <> 0] may be asserted as an
hypothesis to the front of the triple judgment, in the form of
a standard Coq hypothesis, as shown below. *)
Parameter triple_div' : forall n1 n2,
n2 <> 0 ->
triple (val_div n1 n2)
\[]
(fun r => \[r = val_int (Z.quot n1 n2)]).
(** This latter presentation with pure facts such as [n2 <> 0] placed
to the front of the triple turns out to be more practical to exploit
in proofs. Hence, we always follow this style of presentation, and
reserve the precondition for describing pieces of mutable state. *)
(* ----------------------------------------------------------------- *)
(** *** Specification of Primitive Operations Acting on Memory *)
(** There remains to describe the specification of operations on the heap. *)
(** Recall that [val_get] denotes the operation for reading a memory cell.
A call of the form [val_get v'] executes safely if [v'] is of the
form [val_loc p] for some location [p], in a state that features
a memory cell at location [p], storing some contents [v]. Such a state
is described as [p ~~> v]. The read operation returns a value [r]
such that [r = v], and the memory state of the operation remains
unchanged. The specification of [val_get] is thus expressed as follows. *)
Parameter triple_get : forall v p,
triple (val_get (val_loc p))
(p ~~> v)
(fun r => \[r = v] \* (p ~~> v)).
(** Remark: [val_loc] is registered as a coercion, so [val_get (val_loc p)]
could be written simply as [val_get p], where [p] has type [loc].
We here chose to write [val_loc] explicitly for clarity. *)
(** Recall that [val_set] denotes the operation for writing a memory cell.
A call of the form [val_set v' w] executes safely if [v'] is of the
form [val_loc p] for some location [p], in a state [p ~~> v].
The write operation updates this state to [p ~~> w], and returns
the unit value, which can be ignored. Hence, [val_set] is specified
as follows. *)
Parameter triple_set : forall w p v,
triple (val_set (val_loc p) w)
(p ~~> v)
(fun _ => p ~~> w).
(** Recall that [val_ref] denotes the operation for allocating a cell
with a given contents. A call to [val_ref v] does not depend on
the contents of the existing state. It extends the state with a fresh
singleton cell, at some location [p], assigning it [v] as contents.
The fresh cell is then described by the heap predicate [p ~~> v].
The evaluation of [val_ref v] produces the value [val_loc p]. Thus,
if [r] denotes the result value, we have [r = val_loc p] for some [p].
In the corresponding specification shown below, observe how the
location [p] is existentially quantified in the postcondition. *)
Parameter triple_ref : forall v,
triple (val_ref v)
\[]
(fun (r:val) => \exists (p:loc), \[r = val_loc p] \* p ~~> v).
(** Using the notation [funloc p => H] as a shorthand for
[fun (r:val) => \exists (p:loc), \[r = val_loc p] \* H],
the specification for [val_ref] becomes more concise. *)
Parameter triple_ref' : forall v,
triple (val_ref v)
\[]
(funloc p => p ~~> v).
(** Recall that [val_free] denotes the operation for deallocating a cell
at a given address. A call of the form [val_free p] executes safely
in a state [p ~~> v]. The operation leaves an empty state, and
asserts that the return value, named [r], is equal to unit. *)
Parameter triple_free : forall p v,
triple (val_free (val_loc p))
(p ~~> v)
(fun _ => \[]).
(* ================================================================= *)
(** ** Review of the Structural Rules *)
(** Let us review the essential structural rules, which were introduced
in the previous chapters. These structural rules are involved in
the practical verification proofs carried out further in this chapter. *)
(** The frame rule asserts that the precondition and the postcondition
can be extended together by an arbitrary heap predicate.
Recall that the definition of [triple] was set up precisely to
validate this frame rule, so in a sense in holds "by construction". *)
Parameter triple_frame : forall t H Q H',
triple t H Q ->
triple t (H \* H') (Q \*+ H').
(** The consequence rule allows to strengthen the precondition and
weaken the postcondition. *)
Parameter triple_conseq : forall t H' Q' H Q,
triple t H' Q' ->
H ==> H' ->
Q' ===> Q ->
triple t H Q.
(** In practice, it is most convenient to exploit a rule that combines
both frame and consequence into a single rule, as stated next.
(Note that this "combined structural rule" was proved as an exercise
in chapter [Himpl].) *)
Parameter triple_conseq_frame : forall H2 H1 Q1 t H Q,
triple t H1 Q1 ->
H ==> H1 \* H2 ->
Q1 \*+ H2 ===> Q ->
triple t H Q.
(** The two extraction rules enable to extract pure facts and existentially
quantified variables, from the precondition into the Coq context. *)
Parameter triple_hpure : forall t (P:Prop) H Q,
(P -> triple t H Q) ->
triple t (\[P] \* H) Q.
Parameter triple_hexists : forall t (A:Type) (J:A->hprop) Q,
(forall (x:A), triple t (J x) Q) ->
triple t (\exists (x:A), J x) Q.
(** **** Exercise: 1 star, standard, optional (triple_hpure')
The extraction rule [triple_hpure] assumes a precondition of the form
[\[P] \* H]. The variant rule [triple_hpure'], stated below, assumes
instead a precondition with only the pure part, i.e., of the form [\[P]].
Prove that [triple_hpure'] is indeed a corollary of [triple_hpure]. *)
Lemma triple_hpure' : forall t (P:Prop) Q,
(P -> triple t \[] Q) ->
triple t \[P] Q.
Proof using.
intros. eapply triple_conseq_frame. apply triple_hpure. apply H.
xsimpl. auto. xsimpl.
Qed.
(** [] *)
(* ================================================================= *)
(** ** Verification Proof in Separation Logic *)
(** We have at hand all the necessary rules for carrying out actual
verification proofs in Separation Logic. Let's do it! *)
Module ExamplePrograms.
Export ProgramSyntax.
(* ----------------------------------------------------------------- *)
(** *** Proof of [incr] *)
(** First, we consider the verification of the increment function,
which is written in OCaml syntax as:
OCaml:
let incr p =
p := !p + 1
Recall that, for simplicity, we assume programs to be written in
"A-normal form", that is, with all intermediate expressions named
by a let-binding. Thereafter, we thus consider the following
definition for the [incr].
OCaml:
let incr p =
let n = !p in
let m = n+1 in
p := m
Using the construct from our toy programming language, the definition
of [incr] is written as shown below. *)
Definition incr : val :=
val_fun "p" (
trm_let "n" (trm_app val_get (trm_var "p")) (
trm_let "m" (trm_app (trm_app val_add
(trm_var "n")) (val_int 1)) (
trm_app (trm_app val_set (trm_var "p")) (trm_var "m")))).
(** Alternatively, using notation and coercions, the same program can be
written as shown below. *)
Definition incr' : val :=
<{ fun 'p =>
let 'n = ! 'p in
let 'm = 'n + 1 in
'p := 'm }>.
(** Let us check that the two definitions are indeed the same. *)
Lemma incr_eq_incr' :
incr = incr'.
Proof using. reflexivity. Qed.
(** Recall from the first chapter the specification of the increment function.
Its precondition requires a singleton state of the form [p ~~> n].
Its postcondition describes a state of the form [p ~~> (n+1)]. *)
Lemma triple_incr : forall (p:loc) (n:int),
triple (trm_app incr p)
(p ~~> n)
(fun _ => p ~~> (n+1)).
(** We next show a detailed proof for this specification. It exploits:
- the structural reasoning rules,
- the reasoning rules for terms,
- the specification of the primitive functions,
- the [xsimpl] tactic for simplifying entailments.
*)
Proof using.
(* initialize the proof *)
intros. applys triple_app_fun. { reflexivity. } simpl.
(* reason about [let n = ..] *)
applys triple_let.
(* reason about [!p] *)
{ apply triple_get. }
(* name [n'] the result of [!p] *)
intros n'. simpl.
(* substitute away the equality [n' = n] *)
apply triple_hpure. intros ->.
(* reason about [let m = ..] *)
applys triple_let.
(* apply the frame rule to put aside [p ~~> n] *)
{ applys triple_conseq_frame.
(* reason about [n+1] in the empty state *)
{ applys triple_add. }
{ xsimpl. }
{ xsimpl. } }
(* name [m'] the result of [n+1] *)
intros m'. simpl.
(* substitute away the equality [m' = m] *)
apply triple_hpure. intros ->.
(* reason about [p := m] *)
{ applys triple_set. }
Qed.
(* ----------------------------------------------------------------- *)
(** *** Proof of [succ_using_incr] *)
(** Recall from [Basic] the function [succ_using_incr]. *)
Definition succ_using_incr : val :=
<{ fun 'n =>
let 'r = val_ref 'n in
incr 'r;
let 'x = ! 'r in
val_free 'r;
'x }>.
(** Recall the specification of [succ_using_incr]. *)
Lemma triple_succ_using_incr : forall (n:int),
triple (trm_app succ_using_incr n)
\[]
(fun v => \[v = val_int (n+1)]).
(** **** Exercise: 4 stars, standard, especially useful (triple_succ_using_incr)
Verify the function [triple_succ_using_incr].
Hint: follow the pattern of [triple_incr].
Hint: use [applys triple_seq] for reasoning about a sequence.
Hint: use [applys triple_val] for reasoning about the final
return value, namely [x]. *)
Proof using.
intros. applys triple_app_fun. reflexivity. simpl.
applys triple_let. apply triple_ref. simpl. intros.
applys triple_hexists. intros. applys triple_hpure.
intros. applys triple_seq. subst. applys triple_incr.
subst. applys triple_let. applys triple_get. intros.
simpl. applys triple_hpure. intros. subst. applys triple_seq.
applys triple_free. applys triple_val. xsimpl. reflexivity.
Qed.
(** [] *)
(* ----------------------------------------------------------------- *)
(** *** Proof of [factorec] *)
Import Basic.Facto.
(** Recall from [Basic] the function [repeat_incr].
OCaml:
let rec factorec n =
if n <= 1 then 1 else n * factorec (n-1)
*)
Definition factorec : val :=
<{ fix 'f 'n =>
let 'b = ('n <= 1) in
if 'b
then 1
else let 'x = 'n - 1 in
let 'y = 'f 'x in
'n * 'y }>.
(** **** Exercise: 4 stars, standard, especially useful (triple_factorec)
Verify the function [factorec].
Hint: exploit [triple_app_fix] for reasoning about the recursive function.
Hint: [triple_hpure'], the corollary of [triple_hpure], is helpful.
Hint: exploit [triple_le] and [triple_sub] and [triple_mul] to reason about
the behavior of the primitive operations involved.
Hint: exploit [applys triple_if. case_if as C.] to reason about the
conditional; alternatively, if using [triple_if_case], you'll need to use
the tactic [rew_bool_eq in *] to simplify, e.g., the expression
[isTrue (m <= 1)) = true]. *)
Lemma triple_factorec : forall n,
n >= 0 ->
triple (factorec n)
\[]
(fun r => \[r = facto n]).
Proof using.
intros n. induction_wf IH: (downto 1) n. unfold downto in IH.
intros. applys triple_app_fix. reflexivity. simpl.
applys triple_let. applys triple_le. intros. simpl.
applys triple_hpure'. intros. subst. applys triple_if. case_if as C.
applys triple_val. xsimpl. symmetry. rewrite facto_init. reflexivity.
split; assumption.
applys triple_let. applys triple_sub. intros. simpl.
applys triple_hpure'. intros. applys triple_let. subst. applys IH; math.
intros. simpl. applys triple_hpure'. intros. subst. applys triple_conseq.
applys triple_mul. xsimpl. xsimpl. intros. rewrite facto_step. assumption. math.
Qed.
(** [] *)
End ExamplePrograms.