-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathLinkedList.v
1159 lines (1004 loc) · 48.1 KB
/
LinkedList.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
(******************************************************************************)
(* Copyright (c) 2021 Steven Keuchel *)
(* All rights reserved. *)
(* *)
(* Redistribution and use in source and binary forms, with or without *)
(* modification, are permitted provided that the following conditions are *)
(* met: *)
(* *)
(* 1. Redistributions of source code must retain the above copyright notice, *)
(* this list of conditions and the following disclaimer. *)
(* *)
(* 2. Redistributions in binary form must reproduce the above copyright *)
(* notice, this list of conditions and the following disclaimer in the *)
(* documentation and/or other materials provided with the distribution. *)
(* *)
(* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *)
(* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR *)
(* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR *)
(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *)
(* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *)
(* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *)
(* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *)
(* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *)
(* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *)
(* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *)
(******************************************************************************)
(* This file contains a toy example verifying the correctness of simple
functions operating on singly-linked lists. It is essentially based on the
definitions of linked list first defined in
John C. Reynolds. 2000.
Intuitionistic reasoning about shared mutable data structure.
We instantiate the framework to get a symbolic verification condition
generator and also instantiate the Iris model with a suitable definition of
memory.
*)
From Coq Require Import
Lists.List
Logic.FinFun
Program.Tactics
Strings.String
ZArith.ZArith
micromega.Lia.
From Equations Require Import
Equations.
From Katamaran Require Import
Iris.Instance
Iris.Base
Notations
Program
Semantics
Semantics.Registers
Sep.Hoare
Signature
Specification
Symbolic.Solver
MicroSail.ShallowExecutor
MicroSail.ShallowSoundness
MicroSail.SymbolicExecutor
MicroSail.RefineExecutor
MicroSail.Soundness.
From stdpp Require decidable finite list fin_maps infinite.
From iris.proofmode Require string_ident tactics.
Set Implicit Arguments.
Import ctx.notations.
Import ctx.resolution.
Import env.notations.
Local Open Scope string_scope.
(* We use this notation in every place an int represents a memory address.
Unfortunately, this Notation is also used for integers that are not
addreses. *)
Notation ptr := ty.int.
Notation llist := (ty.option ptr).
Module Import ExampleBase <: Base.
Section MemoryModel.
(* The type of memory we use for this example. A mapping from addresses
represented as integers to pairs. *)
Definition Memory : Set := gmap.gmap Z (Z * (Z + unit)).
End MemoryModel.
(* We use default instances because this example does not use any types other
than the standard ones already available. We also don't make any use of
registers (global variables). *)
#[export] Instance typedeclkit : TypeDeclKit := DefaultTypeDeclKit.
#[export] Instance typedenotekit : TypeDenoteKit typedeclkit := DefaultTypeDenoteKit.
#[export] Instance typedefkit : TypeDefKit typedenotekit := DefaultTypeDefKit.
#[export] Instance varkit : VarKit := DefaultVarKit.
Include DefaultRegDeclKit.
Include BaseMixin.
End ExampleBase.
(* The [Program] module contains the declaration and definition of the functions
that make up the program. *)
Module Import ExampleProgram <: Program ExampleBase.
Section FunDeclKit.
(* We define the signatures of μSail functions. Their bodies are defined
later.*)
Inductive Fun : PCtx -> Ty -> Set :=
| append : Fun [ "p" ∷ llist; "q" ∷ llist ] llist
| appendloop : Fun [ "p" ∷ ptr; "q" ∷ llist ] ty.unit
| length : Fun [ "p" ∷ llist ] ty.int
| copy : Fun [ "p" ∷ llist ] llist
| reverse : Fun [ "p" ∷ llist ] llist
| reverseloop : Fun [ "p" ∷ llist; "q" ∷ llist ] llist
.
(* These are foreign functions that will be implemented in Coq. They should
be considered to be primitives provided by a runtime system. *)
Inductive FunX : PCtx -> Ty -> Set :=
| mkcons : FunX [ "x" ∷ ty.int; "xs" ∷ llist ] ptr
| fst : FunX [ "p" ∷ ptr ] ty.int
| snd : FunX [ "p" ∷ ptr ] llist
(* | setfst : FunX [ "p" ∷ ptr; "x" ∷ ty.int ] ty.unit *)
| setsnd : FunX [ "p" ∷ ptr; "xs" ∷ llist ] ty.unit
.
Definition 𝑭 : PCtx -> Ty -> Set := Fun.
Definition 𝑭𝑿 : PCtx -> Ty -> Set := FunX.
(* The ghost lemmas for opening and closing the recursive linked list
predicate ptstolist. This predicate is defined later in this file, but
since the ghost lemmas are used in statements, their signatures are
already defined here. *)
Inductive Lem : PCtx -> Set :=
| open_nil : Lem [ ]
| open_cons : Lem [ "p" ∷ ptr ]
| close_nil : Lem [ "p" ∷ ty.unit ]
| close_cons : Lem [ "p" ∷ ptr ].
Definition 𝑳 : PCtx -> Set := Lem.
End FunDeclKit.
(* A mixin provided by the library pulling in definitions of statements etc.
which rely on the declared functions and ghost lemmas. *)
Include FunDeclMixin ExampleBase.
Section FunDefKit.
Local Coercion stm_exp : Exp >-> Stm.
Local Notation "'x'" := (@exp_var _ "x" _ _) : exp_scope.
Local Notation "'y'" := (@exp_var _ "y" _ _) : exp_scope.
Local Notation "'z'" := (@exp_var _ "z" _ _) : exp_scope.
Notation "'lemma' f args" := (stm_lemma f args%env) (at level 10, f at next level) : exp_scope.
(* The append function, taking two linked lists [p],[q] and appending [q] to
[p] by destructively updating the last heap-allocated pair. Both lists
are allowed to be empty. *)
Definition fun_append : Stm [ "p" ∷ llist; "q" ∷ llist ] llist :=
match: exp_var "p" with
| inl "x" =>
call appendloop (exp_var "x") (exp_var "q") ;;
exp_var "p"
| inr "tt" =>
lemma close_nil [exp_var "tt"] ;;
exp_var "q"
end.
(* The loop that recurses to the last pair. The first argument needs to be a
valid address pointing to a pair, i.e. it represents a non-empty linked
list. *)
Definition fun_appendloop : Stm [ "p" ∷ ptr; "q" ∷ llist ] ty.unit :=
lemma open_cons [exp_var "p"] ;;
let: "mbn" := foreign snd (exp_var "p") in
match: (exp_var "mbn") with
| inl "x" => call appendloop (exp_var "x") (exp_var "q")
| inr "tt" =>
lemma close_nil [exp_var "tt"] ;;
(* Do the destructive update. *)
foreign setsnd (exp_var "p") (exp_var "q")
end;;
lemma close_cons [exp_var "p"].
(* An example with the first lemma call removed. The intention is to demonstrate
the error reporting capabilities of the library. *)
Definition fun_appendloop_broken : Stm [ "p" ∷ ptr; "q" ∷ llist ] ty.unit :=
(* lemma open_cons [exp_var "p"] ;; *)
let: "mbn" := foreign snd (exp_var "p") in
match: (exp_var "mbn") with
| inl "x" => call appendloop (exp_var "x") (exp_var "q")
| inr "tt" =>
lemma close_nil [exp_var "tt"] ;;
foreign setsnd (exp_var "p") (exp_var "q")
end;;
lemma close_cons [exp_var "p"].
(* A recursive function calculating the length of a heap-allocated list. *)
Definition fun_length : Stm [ "p" ∷ llist ] ty.int :=
match: exp_var "p" with
| inl "x" =>
lemma open_cons [exp_var "x"] ;;
let: "t" := foreign snd (exp_var "x") in
let: "r" := call length (exp_var "t") in
lemma close_cons [exp_var "x"] ;;
exp_binop bop.plus (exp_val ty.int 1%Z) (exp_var "r")
| inr "tt" =>
lemma close_nil [exp_var "tt"] ;;
lemma open_nil [] ;;
stm_val ty.int 0%Z
end.
(* Make a deep copy of a linked list, i.e. allocate new pairs that hold
the same elements in the same order as the given list. *)
Definition fun_copy : Stm [ "p" ∷ llist ] llist :=
match: exp_var "p" with
| inl "x" =>
lemma open_cons [exp_var "x"] ;;
let: "h" := foreign fst (exp_var "x") in
let: "t" := foreign snd (exp_var "x") in
let: "t'" := call copy (exp_var "t") in
let: "x'" := foreign mkcons (exp_var "h") (exp_var "t'") in
lemma close_cons [exp_var "x"] ;;
lemma close_cons [exp_var "x'"] ;;
exp_inl (exp_var "x'")
| inr "tt" =>
lemma close_nil [exp_var "tt"] ;;
lemma open_nil [] ;;
lemma open_nil [] ;;
exp_val llist (inr tt)
end.
(* In-place reversing of a linked list. This changes the order of all
pointers in the list, destructively updating the given list. *)
Definition fun_reverse : Stm [ "p" ∷ llist ] llist :=
lemma open_nil [] ;;
call reverseloop (exp_var "p") (exp_val llist (inr tt)).
(* The loop for the reversal with an accumulator [q]. This reverses [p]
and appends [q] to that result. *)
Definition fun_reverseloop : Stm [ "p" ∷ llist; "q" ∷ llist ] llist :=
match: exp_var "p" with
| inl "x" =>
lemma open_cons [exp_var "x"] ;;
let: "n" := foreign snd (exp_var "x") in
foreign setsnd (exp_var "x") (exp_var "q");;
lemma close_cons [exp_var "x"] ;;
call reverseloop (exp_var "n") (exp_inl (exp_var "x"))
| inr "tt" =>
lemma close_nil [exp_var "tt"] ;;
exp_var "q"
end.
(* Combine all the separately-defined function bodies into a single map from
function names to their bodies. *)
Definition FunDef {Δ τ} (f : Fun Δ τ) : Stm Δ τ :=
match f in Fun Δ τ return Stm Δ τ with
| append => fun_append
| appendloop => fun_appendloop
| length => fun_length
| copy => fun_copy
| reverse => fun_reverse
| reverseloop => fun_reverseloop
end.
End FunDefKit.
(* We pull in the default implementation of a store for registers. *)
Include DefaultRegStoreKit ExampleBase.
(* In this section we define the foreign functions. *)
Section ForeignKit.
Import iris.proofmode.tactics.
(* This defines a "stepping relation" for the foreign functions. Since all
these functions are deterministic, we write them in a functional instead
of a relational style. *)
Equations(noeqns) ForeignCall {σs σ} (f : 𝑭𝑿 σs σ) (args : NamedEnv Val σs)
(res : string + Val σ) (γ γ' : RegStore) (μ μ' : Memory) : Prop :=
(* mkcons allocates a new pair and initializes it with the given values
[x] and [xs]*)
ForeignCall mkcons (env.snoc (env.snoc env.nil _ x) _ xs) res γ γ' μ μ' :=
(* Determinate the next free address. *)
let next := infinite_fresh (elements (dom μ)) in
γ' = γ /\
(* Allocate the pair at [next]. *)
μ' = (<[next := (x, xs)]> μ) /\
res = inr next;
ForeignCall fst (env.snoc env.nil _ z) res γ γ' μ μ' :=
match μ !! z with
| None => res = inl "Invalid pointer"
| Some (head,_) => γ' = γ /\ μ' = μ /\ res = inr head
end;
ForeignCall snd (env.snoc env.nil _ z) res γ γ' μ μ' :=
match μ !! z with
| None => res = inl "Invalid pointer"
| Some (_,next) => γ' = γ /\ μ' = μ /\ res = inr next
end;
ForeignCall setsnd (env.snoc (env.snoc env.nil _ z) _ xs) res γ γ' μ μ' :=
match (μ !! z) with
| None => res = inl "Invalid pointer"
| Some (elem, _) => γ' = γ /\ μ' = <[z := (elem, xs)]> μ /\ res = inr tt
end.
(* Show the progress of the foreign function stepping relation. *)
Lemma ForeignProgress {σs σ} (f : 𝑭𝑿 σs σ) (args : NamedEnv Val σs) γ μ :
exists γ' μ' res, ForeignCall f args res γ γ' μ μ'.
Proof.
destruct f; env.destroy args; cbn;
try match goal with
| |- context[match ?disc with _ => _ end] =>
lazymatch disc with
(* Destruct the lookup first before creating the evars using eexists. *)
| lookup _ _ => destruct disc as [[elem next]|] eqn:?
end
end;
repeat
lazymatch goal with
| |- _ = _ => reflexivity
| |- _ /\ _ => split
| |- exists _, _ => eexists
end; auto.
Qed.
End ForeignKit.
Include ProgramMixin ExampleBase.
End ExampleProgram.
(* These are user-defined pure predicates that we use to encode the functional
correctness of some of the linked list functions that are otherwise not
encodable in the assertion language. The assertion language has a primitive
list append, but no operators for the other functions. *)
Inductive PurePredicate : Set :=
| plength
| preverse
| preverseappend
.
(* The following datatype defines the spatial predicates that are used in the
example. *)
Inductive Predicate : Set :=
| ptstocons (* A points-to predicate for a single heap allocated pair. *)
| ptstolist (* A points-to predicate for a heap allocated linked list. *)
.
Section TransparentObligations.
Local Set Transparent Obligations.
Derive NoConfusion for PurePredicate.
Derive NoConfusion for Predicate.
End TransparentObligations.
Derive EqDec for PurePredicate.
Derive EqDec for Predicate.
(* The program logic signature contains all the necessary definitions
pertaining to user-defined pure and spatial predicates. These definitions
are enough to instantiate the assertion language for pre- and
postconditions used in function contracts. *)
Module Import ExampleSignature <: Signature ExampleBase.
Import ExampleBase.
Definition 𝑷 := PurePredicate.
(* Maps each pure predicate to a list of arguments with their types. *)
Definition 𝑷_Ty (p : 𝑷) : Ctx Ty :=
match p with
| plength => [ty.list ty.int; ty.int]
| preverse => [ty.list ty.int; ty.list ty.int]
| preverseappend => [ty.list ty.int; ty.list ty.int; ty.list ty.int]
end.
(* Interprets a pure predicate name as a Coq proposition. *)
Definition 𝑷_inst (p : 𝑷) : env.abstract Val (𝑷_Ty p) Prop :=
match p with
| plength => fun xs l => Z.of_nat (Datatypes.length xs) = l
| preverse => fun xs ys => ys = rev xs
| preverseappend => fun xs ys zs => zs = rev_append xs ys
end.
#[export] Instance 𝑷_eq_dec : EqDec 𝑷 := PurePredicate_eqdec.
Section HeapPredicateDeclKit.
Definition 𝑯 := Predicate.
(* Maps each spatial predicate to a list of arguments with their types. *)
Definition 𝑯_Ty (p : 𝑯) : Ctx Ty :=
match p with
| ptstocons => [ptr; ty.int; llist]
| ptstolist => [llist; ty.list ty.int]
end.
#[export] Instance 𝑯_eq_dec : EqDec 𝑯 := Predicate_eqdec.
(* None of the predicates is duplicable. *)
#[export] Instance 𝑯_is_dup : IsDuplicable 𝑯 :=
{| is_duplicable p := false |}.
#[local] Arguments Some {_} &.
(* Defines precieness for both predicates. The address forms the input
in both cases and the pointed-to values are the outputs. *)
Definition 𝑯_precise (p : 𝑯) : option (Precise 𝑯_Ty p) :=
match p with
| ptstocons => Some (MkPrecise [ptr] [ptr; llist] eq_refl)
| ptstolist => Some (MkPrecise [llist] [ty.list ptr] eq_refl)
end.
End HeapPredicateDeclKit.
(* A mixin that defines Formulas, Chunks and assertions to write contract and
that defines Worlds and symbolic propositions for the executor. *)
Include PredicateMixin ExampleBase.
Include WorldsMixin ExampleBase.
(* The SolverKit module is the user-defined part of the solver that is linked
with a generic part in MakeSolver. Here we can automatically simplify or
solve the user-defined predicate case of formulas. We also prove
correctness by showing that all runs of the simplifier produce unsolved
residual formulas (that are hopefully simpler) that are equivalent to the
input. *)
Section ExampleSolverKit.
#[local] Arguments Some {_} _%ctx.
#[local] Unset Implicit Arguments.
#[local] Set Equations Transparent.
(* Simplification of the [plength] predicate with arguments [xs] and [n]. *)
Equations simplify_plength {Σ} (xs : Term Σ (ty.list ty.int)) (n : Term Σ ty.int) : Option PathCondition Σ :=
| term_binop bop.cons x xs | term_binop bop.plus (term_val ?(ty.int) 1%Z) n => Some [formula_user plength [xs;n]]
| term_val ?(ty.list ty.int) nil | term_val ?(ty.int) 0%Z => Some []
| xs | n => Some [formula_user plength [xs;n]].
(* Simplification of the [preverseappend] predicate with arguments [xs], [ys],
and [zs]. *)
Equations simplify_preverseappend {Σ} (xs ys zs: Term Σ (ty.list ty.int)) : Option PathCondition Σ :=
(* | term_binop binop_cons x xs | term_binop binop_plus (term_val ?(ty.int) 1%Z) n := *)
(* Some [formula_user plength (env.nil ► (_ ↦ xs) ► (ty.int ↦ n))]%list; *)
| term_val ?(ty.list ty.int) nil | ys | zs => Some [formula_relop bop.eq ys zs]
| xs | term_val ?(ty.list ty.int) nil | zs => Some [formula_user preverse [xs;zs]]
| term_binop bop.cons x xs | ys | zs => Some [formula_user preverseappend [xs; term_binop bop.cons x ys; zs]]
| xs | ys | zs => Some [formula_user preverseappend [xs;ys;zs]].
Import Entailment.
Local Ltac lsolve := repeat Entailment.tactics.mixin; try easy; auto.
(* Prove that the simplifier of [plength] is sound and complete. *)
Goal True. idtac "Timing before: llist/simplify_plength_spec". Abort.
Lemma simplify_plength_spec {Σ} (xs : Term Σ (ty.list ty.int)) (n : Term Σ ty.int) :
simplify_plength xs n ⊣⊢ Some [formula_user plength [xs;n]].
Proof.
pattern (simplify_plength xs n).
apply_funelim (simplify_plength xs n); intros *; lsolve;
intro ι; cbn; lia.
Qed.
Goal True. idtac "Timing after: llist/simplify_plength_spec". Abort.
(* Prove that the simplifier of [preverseappend] is sound and complete. *)
Goal True. idtac "Timing before: llist/simplify_preverseappend_spec". Abort.
Lemma simplify_preverseappend_spec {Σ} (xs ys zs : Term Σ (ty.list ty.int)) :
simplify_preverseappend xs ys zs ⊣⊢ Some [formula_user preverseappend [xs;ys;zs]].
Proof.
pattern (simplify_preverseappend xs ys zs).
apply_funelim (simplify_preverseappend xs ys zs); intros *; lsolve;
intro ι; cbn.
- now rewrite rev_alt.
- now rewrite rev_append_rev.
- now rewrite rev_alt.
Qed.
Goal True. idtac "Timing after: llist/simplify_preverseappend_spec". Abort.
(* Combined the solvers to a solver for the [formula_user] case. *)
Definition solve_user : SolverUserOnly :=
fun Σ p =>
match p with
| plength => fun ts =>
let (ts,n) := env.view ts in
let (ts,xs) := env.view ts in
simplify_plength xs n
| preverse => fun ts => Some [formula_user preverse ts]
| preverseappend =>
fun ts =>
let (ts,zs) := env.view ts in
let (ts,ys) := env.view ts in
let (ts,xs) := env.view ts in
simplify_preverseappend xs ys zs
end.
(* Combine the correctness proofs. *)
Lemma solve_user_spec : SolverUserOnlySpec solve_user.
Proof.
intros Σ p ts.
destruct p; cbv in ts; env.destroy ts.
- apply simplify_plength_spec.
- reflexivity.
- apply simplify_preverseappend_spec.
Qed.
(* Lift the solver for the [formula_user] case to a solver over any set
of formulas. *)
Definition solver : Solver :=
solveruseronly_to_solver solve_user.
Lemma solver_spec : SolverSpec solver.
Proof.
apply solveruseronly_to_solver_spec, solve_user_spec.
Qed.
End ExampleSolverKit.
Include SignatureMixin ExampleBase.
End ExampleSignature.
(* The specification module contains the contracts for all μSail and foreign functions. *)
Module Import ExampleSpecification <: Specification ExampleBase ExampleSignature ExampleProgram.
Include SpecificationMixin ExampleBase ExampleSignature ExampleProgram.
Section ContractDefKit.
Import ctx.resolution.
Import asn.notations.
(* We define notations for more convenience. *)
Local Notation "p '↦l' xs" := (asn.chunk (chunk_user ptstolist (env.nil ► (llist ↦ p) ► (ty.list ty.int ↦ xs)))) (at level 70).
Local Notation "p '↦p' ( x , xs )" := (asn.chunk (chunk_user ptstocons (env.nil ► (ptr ↦ p) ► (ty.int ↦ x) ► (llist ↦ xs)))) (at level 70).
Arguments formula_prop [Σ] Σ' ζ _.
Definition asn_append {Σ : LCtx} (xs ys zs : Term Σ (ty.list ty.int)) : Assertion Σ :=
term_binop bop.append xs ys = zs.
Definition sep_contract_append : SepContract [ "p" ∷ llist; "q" ∷ llist ] llist :=
{| sep_contract_logic_variables := ["p" ∷ llist; "q" ∷ llist; "xs" ∷ ty.list ty.int; "ys" ∷ ty.list ty.int];
sep_contract_localstore := [term_var "p"; term_var "q"];
sep_contract_precondition := term_var "p" ↦l term_var "xs" ∗ term_var "q" ↦l term_var "ys";
sep_contract_result := "result";
sep_contract_postcondition :=
asn.exist "zs" (ty.list ty.int)
(term_var "result" ↦l term_var "zs" ∗
asn_append (term_var "xs") (term_var "ys") (term_var "zs"));
|}.
Definition sep_contract_appendloop : SepContract [ "p" ∷ ptr; "q" ∷ llist ] ty.unit :=
{| sep_contract_logic_variables := ["p" ∷ ptr; "q" ∷ llist; "xs" ∷ ty.list ty.int; "ys" ∷ ty.list ty.int];
sep_contract_localstore := [term_var "p"; term_var "q"];
sep_contract_precondition := term_inl (term_var "p") ↦l term_var "xs" ∗ term_var "q" ↦l term_var "ys";
sep_contract_result := "result";
sep_contract_postcondition :=
term_var "result" = term_val ty.unit tt ∗
asn.exist "zs" (ty.list ty.int)
(term_inl (term_var "p") ↦l term_var "zs" ∗
asn_append (term_var "xs") (term_var "ys") (term_var "zs"));
|}.
Definition sep_contract_length : SepContract [ "p" ∷ llist ] ty.int :=
{| sep_contract_logic_variables := ["p" ∷ llist; "xs" ∷ ty.list ty.int];
sep_contract_localstore := [term_var "p"];
sep_contract_precondition := term_var "p" ↦l term_var "xs";
sep_contract_result := "result";
sep_contract_postcondition :=
asn.formula (formula_user plength [term_var "xs"; term_var "result"]) ∗
term_var "p" ↦l term_var "xs"
|}.
Definition sep_contract_copy : SepContract [ "p" ∷ llist ] llist :=
{| sep_contract_logic_variables := ["p" ∷ llist; "xs" ∷ ty.list ty.int];
sep_contract_localstore := [term_var "p"];
sep_contract_precondition := term_var "p" ↦l term_var "xs";
sep_contract_result := "result";
sep_contract_postcondition :=
term_var "p" ↦l term_var "xs" ∗
term_var "result" ↦l term_var "xs"
|}.
Definition sep_contract_reverse : SepContract [ "p" ∷ llist ] llist :=
{| sep_contract_logic_variables := ["p" ∷ llist; "xs" ∷ ty.list ty.int];
sep_contract_localstore := [term_var "p"];
sep_contract_precondition := term_var "p" ↦l term_var "xs";
sep_contract_result := "r";
sep_contract_postcondition :=
asn.exist "zs" (ty.list ty.int)
(term_var "r" ↦l term_var "zs" ∗
asn.formula (formula_user preverse [term_var "xs"; term_var "zs"]));
|}.
Definition sep_contract_reverseloop : SepContract [ "p" ∷ llist; "q" ∷ llist ] llist :=
{| sep_contract_logic_variables := ["p" ∷ llist; "q" ∷ llist; "xs" ∷ ty.list ty.int; "ys" ∷ ty.list ty.int];
sep_contract_localstore := [term_var "p"; term_var "q"];
sep_contract_precondition := term_var "p" ↦l term_var "xs" ∗ term_var "q" ↦l term_var "ys";
sep_contract_result := "r";
sep_contract_postcondition :=
asn.exist "zs" (ty.list ty.int)
(term_var "r" ↦l term_var "zs" ∗
asn.formula (formula_user preverseappend [term_var "xs"; term_var "ys"; term_var "zs"]));
|}.
Definition sep_contract_mkcons : SepContract [ "x" ∷ ty.int; "xs" ∷ llist ] ptr :=
{| sep_contract_logic_variables := ["x" ∷ ty.int; "xs" ∷ llist];
sep_contract_localstore := [term_var "x"; term_var "xs"];
sep_contract_precondition := ⊤;
sep_contract_result := "p";
sep_contract_postcondition := term_var "p" ↦p ( term_var "x" , term_var "xs" );
|}.
Definition sep_contract_fst : SepContract [ "p" ∷ ptr ] ty.int :=
{| sep_contract_logic_variables := ["p" ∷ ty.int; "x" ∷ ty.int; "xs" ∷ llist];
sep_contract_localstore := [term_var "p"];
sep_contract_precondition := term_var "p" ↦p ( term_var "x" , term_var "xs" );
sep_contract_result := "result";
sep_contract_postcondition :=
term_var "result" = term_var "x" ∗
term_var "p" ↦p ( term_var "x" , term_var "xs" );
|}.
Definition sep_contract_snd : SepContract [ "p" ∷ ptr ] llist :=
{| sep_contract_logic_variables := ["p" ∷ ty.int; "x" ∷ ty.int; "xs" ∷ llist];
sep_contract_localstore := [term_var "p"];
sep_contract_precondition := term_var "p" ↦p ( term_var "x" , term_var "xs" );
sep_contract_result := "result";
sep_contract_postcondition :=
term_var "result" = term_var "xs" ∗
term_var "p" ↦p ( term_var "x" , term_var "xs" );
|}.
Definition sep_contract_setsnd : SepContract [ "p" ∷ ptr; "xs" ∷ llist ] ty.unit :=
{| sep_contract_logic_variables := ["p" ∷ ty.int; "x" ∷ ty.int; "xs" ∷ llist];
sep_contract_localstore := [term_var "p"; term_var "xs"];
sep_contract_precondition := asn.exist "ys" llist (term_var "p" ↦p ( term_var "x" , term_var "ys"));
sep_contract_result := "result";
sep_contract_postcondition :=
term_var "result" = term_val ty.unit tt ∗
term_var "p" ↦p ( term_var "x" , term_var "xs");
|}.
Definition sep_lemma_open_nil : Lemma [ ] :=
{| lemma_logic_variables := [];
lemma_patterns := [];
lemma_precondition := ⊤;
lemma_postcondition := term_val llist (inr tt) ↦l term_val (ty.list ty.int) nil;
|}.
Definition sep_lemma_open_cons : Lemma [ "p" ∷ ptr ] :=
{| lemma_logic_variables := ["p" ∷ ty.int; "xs" ∷ ty.list ty.int];
lemma_patterns := [term_var "p"];
lemma_precondition := term_inl (term_var "p") ↦l term_var "xs";
lemma_postcondition :=
asn.match_list (term_var "xs")
⊥
"y" "ys"
(asn.exist "n" llist
(term_var "p" ↦p (term_var "y", term_var "n") ∗
term_var "n" ↦l term_var "ys"))
|}.
Definition sep_lemma_close_cons : Lemma [ "p" ∷ ptr ] :=
{| lemma_logic_variables := ["p" ∷ ptr; "x" ∷ ty.int; "xs" ∷ ty.list ty.int; "n" ∷ llist ];
lemma_patterns := [term_var "p"];
lemma_precondition := term_var "p" ↦p (term_var "x" , term_var "n") ∗
term_var "n" ↦l term_var "xs";
lemma_postcondition := term_inl (term_var "p") ↦l term_binop bop.cons (term_var "x") (term_var "xs")
|}.
Definition sep_lemma_close_nil : Lemma [ "p" ∷ ty.unit ] :=
{| lemma_logic_variables := ["p" ∷ ty.unit; "xs" ∷ ty.list ty.int ];
lemma_patterns := [term_var "p"];
lemma_precondition := term_inr (term_var "p") ↦l term_var "xs";
lemma_postcondition :=
term_var "p" = term_val ty.unit tt ∗
term_var "xs" = term_val (ty.list ty.int) nil
|}.
(* The following maps μSail function names to their contracts. *)
Definition CEnv : SepContractEnv :=
fun Δ τ f =>
match f with
| append => Some (sep_contract_append)
| appendloop => Some (sep_contract_appendloop)
| length => Some (sep_contract_length)
| copy => Some (sep_contract_copy)
| reverse => Some (sep_contract_reverse)
| reverseloop => Some (sep_contract_reverseloop)
end.
(* And this definition maps foreign functions to their contracts. *)
Definition CEnvEx : SepContractEnvEx :=
fun Δ τ f =>
match f with
| mkcons => sep_contract_mkcons
| fst => sep_contract_fst
| snd => sep_contract_snd
| setsnd => sep_contract_setsnd
end.
(* And finally a mapping from ghost lemmas to the entailments they encode. *)
Definition LEnv : LemmaEnv :=
fun Δ l =>
match l with
| open_nil => sep_lemma_open_nil
| open_cons => sep_lemma_open_cons
| close_cons => sep_lemma_close_cons
| close_nil => sep_lemma_close_nil
end.
End ContractDefKit.
End ExampleSpecification.
(* Use the specification and the solver module to compose the symbolic executor
and symbolic verification condition generator. *)
Module Import ExampleExecutor :=
MakeExecutor ExampleBase ExampleSignature ExampleProgram ExampleSpecification.
Section DebugExample.
Import SymProp.notations.
Notation "x '∷' σ . P" := (@amsg.there _ (x ∷ σ) P)
(at level 200, right associativity, only printing, format "x '∷' σ . '/' P").
Notation "'error' x" := (SymProp.error x) (at level 200, only printing, format "'error' x").
Notation "P" := (amsg.mk P) (only printing).
Import ListNotations.
Lemma debug_appendloop_broken : Symbolic.ValidContract sep_contract_appendloop fun_appendloop_broken.
Proof.
compute.
idtac "Verification condition with failure:".
match goal with |- VerificationCondition ?x => idtac x end.
Abort.
End DebugExample.
(* In this section we use the symbolic VCG to verify the contracts of all the
μSail functions. In this case, the automation provided by the user-defined
and generic solvers, and the explicit ghost lemma invocations in the function
definition provide enough instructions to solve the verification conditions
fully automatically. We therefore use the reflective version of the VCGs
[ValidContractReflect] and prove each VC by reflexivity. *)
Section ContractVerification.
Goal True. idtac "Timing before: llist/valid_contract_append". Abort.
Lemma valid_contract_append : Symbolic.ValidContractReflect sep_contract_append fun_append.
Proof. reflexivity. Qed.
Goal True. idtac "Timing after: llist/valid_contract_append". Abort.
Goal True. idtac "Timing before: llist/valid_contract_appendloop". Abort.
Lemma valid_contract_appendloop : Symbolic.ValidContractReflect sep_contract_appendloop fun_appendloop.
Proof. reflexivity. Qed.
Goal True. idtac "Timing after: llist/valid_contract_appendloop". Abort.
Goal True. idtac "Timing before: llist/valid_contract_length". Abort.
Lemma valid_contract_length : Symbolic.ValidContractReflect sep_contract_length fun_length.
Proof. reflexivity. Qed.
Goal True. idtac "Timing after: llist/valid_contract_length". Abort.
Goal True. idtac "Timing before: llist/valid_contract_copy". Abort.
Lemma valid_contract_copy : Symbolic.ValidContractReflect sep_contract_copy fun_copy.
Proof. reflexivity. Qed.
Goal True. idtac "Timing after: llist/valid_contract_copy". Abort.
Goal True. idtac "Timing before: llist/valid_contract_reverse". Abort.
Lemma valid_contract_reverse : Symbolic.ValidContractReflect sep_contract_reverse fun_reverse.
Proof. reflexivity. Qed.
Goal True. idtac "Timing after: llist/valid_contract_reverse". Abort.
Goal True. idtac "Timing before: llist/valid_contract_reverseloop". Abort.
Lemma valid_contract_reverseloop : Symbolic.ValidContractReflect sep_contract_reverseloop fun_reverseloop.
Proof. reflexivity. Qed.
Goal True. idtac "Timing after: llist/valid_contract_reverseloop". Abort.
End ContractVerification.
(* Also instantiate the shallow executor for the soundness proofs and the
statistics. *)
Module Import ExampleShalExec :=
MakeShallowExecutor ExampleBase ExampleSignature ExampleProgram ExampleSpecification.
(* Instantiate the operational semantics which is an input to the Iris model. *)
Module ExampleSemantics <: Semantics ExampleBase ExampleProgram :=
MakeSemantics ExampleBase ExampleProgram.
(* This module contains the instantiation of the Iris model. It contains the
definition of several user-defined modules that are inputs to the generic
module functors defined in the library. *)
Module ExampleModel.
Import ExampleProgram.
Import ExampleSpecification.
(* The construction of the model is split up into several steps. First, the
[IrisBase] defines the operational model that only depends on the program
and the operational semantics, but not the signature or defined function
contracts. *)
Module Import ExampleIrisBase <: IrisBase ExampleBase ExampleProgram ExampleSemantics.
(* Instantiates with the step relation and sets up the ghost state for
registers. *)
Include IrisPrelims ExampleBase ExampleProgram ExampleSemantics.
(* The [IrisParameters] define the ghost state for memory which is provided
by the user. This is then combined with the ghost state for registers in
the [IrisResources] mixin below. *)
Section ExampleIrisParameters.
Import iris.bi.interface.
Import iris.bi.big_op.
Import iris.base_logic.lib.iprop.
Import iris.base_logic.lib.gen_heap.
Import iris.proofmode.tactics.
Class mcMemGS Σ :=
McMemGS {
(* ghost variable for tracking the memory state. *)
mc_ghGS : gen_heapGS Z (Z * (Z + unit)) Σ;
mc_invNs : namespace
}.
#[export] Existing Instance mc_ghGS.
Definition memGS : gFunctors -> Set := mcMemGS.
Lemma fst_pair_id2 : forall {A} {B},
(λ (x : A) (y : B), (Datatypes.fst ∘ pair x) y) = (λ (x : A) (y : B), x).
Proof.
intros; reflexivity.
Qed.
Lemma imap_pair_fst_seq {A} (l : list A) :
(imap pair l).*1 = seq 0 (length l).
Proof.
rewrite fmap_imap.
rewrite fst_pair_id2.
rewrite imap_seq_0.
rewrite list_fmap_id; reflexivity.
Qed.
Definition mem_inv : forall {Σ}, memGS Σ -> Memory -> iProp Σ :=
fun {Σ} hG μ => (gen_heap_interp (hG := mc_ghGS (mcMemGS := hG)) μ)%I.
End ExampleIrisParameters.
Include IrisResources ExampleBase ExampleProgram ExampleSemantics.
Include IrisWeakestPre ExampleBase ExampleProgram ExampleSemantics.
Include IrisTotalWeakestPre ExampleBase ExampleProgram ExampleSemantics.
Include IrisTotalPartialWeakestPre ExampleBase ExampleProgram ExampleSemantics.
End ExampleIrisBase.
Module ExampleIrisAdeqParams <: IrisAdeqParameters ExampleBase ExampleIrisBase.
Import iris.base_logic.lib.gen_heap.
Import iris.proofmode.tactics.
Definition memGpreS : gFunctors -> Set := fun Σ => gen_heapGpreS Z (Z * (Z + unit)) Σ.
Definition memΣ : gFunctors := gen_heapΣ Z (Z * (Z + unit)).
Definition memΣ_GpreS : forall {Σ}, subG memΣ Σ -> memGpreS Σ :=
fun {Σ} => subG_gen_heapGpreS (Σ := Σ) (L := Z) (V := (Z * (Z + unit))).
Definition mem_res : forall {Σ}, memGS Σ -> Memory -> iProp Σ :=
fun {Σ} hG μ => ([∗ map] l↦v ∈ μ, mapsto (hG := mc_ghGS (mcMemGS := hG)) l (DfracOwn 1) v)%I.
Lemma mem_inv_init `{! gen_heapGpreS Z (Z * (Z + unit)) Σ} (μ : Memory) :
⊢ |==> ∃ mG : memGS Σ, (mem_inv mG μ ∗ mem_res mG μ)%I.
Proof.
iMod (gen_heap_init (L := Z) (V := (Z * (Z + unit))) empty) as (gH) "[inv _]".
iMod (gen_heap_alloc_big empty μ (map_disjoint_empty_r μ) with "inv") as "(inv & res & _)".
iModIntro.
rewrite (right_id empty union μ).
iExists (McMemGS gH (nroot .@ "mem_inv")).
iFrame.
Qed.
End ExampleIrisAdeqParams.
(* After instantiating [IrisBase] we have access to the Iris base logic
with the given ghost state and can interpret the user-defined predicates in
this logic. This is then provided to the library as part of the
[IrisInstance] module. *)
Module Import ExampleIrisInstance <:
IrisInstance ExampleBase ExampleSignature ExampleProgram ExampleSemantics
ExampleIrisBase ExampleIrisAdeqParams.
Import iris.base_logic.lib.gen_heap.
Import iris.base_logic.lib.iprop.
Import iris.bi.big_op.
Import iris.bi.interface.
Import iris.proofmode.tactics.
(* This is the interpretation of the points to pair predicate in Iris. *)
Definition ptstocons_interp `{mG : mcMemGS Σ} (p : Z) (v : Z) (n : Z + unit) : iProp Σ :=
(mapsto p (DfracOwn 1) (pair v n))%I.
(* This is the recursive definition of the points to list predicate in Iris. *)
Fixpoint ptstolist_interp `{mG : mcMemGS Σ} (p : Z + unit) (vs : list Z) : iProp Σ :=
match vs with
| nil => ⌜p = inr tt⌝
| v :: vs => (∃ p' pn, ⌜p = inl p'⌝ ∗ ptstocons_interp p' v pn ∗ ptstolist_interp pn vs)%I
end.
(* Pattern match on the generic representation of abstract predicates map them
to their Iris definition. *)
Definition luser_inst `{sRG : sailRegGS Σ} `{fancy_updates.invGS Σ} (mG : mcMemGS Σ) (p : Predicate) (ts : Env Val (𝑯_Ty p)) : iProp Σ :=
(match p return Env Val (𝑯_Ty p) -> iProp Σ with
| ptstocons => fun ts => ptstocons_interp (env.head (env.tail (env.tail ts))) (env.head (env.tail ts)) (env.head ts)
| ptstolist => fun ts => ptstolist_interp (env.head (env.tail ts)) (env.head ts)
end) ts.
(* This definition verifies the soundness if duplicability. However, this
example does not contain any predicates marked as duplicable and therefore
the proof is trivial *)
Definition lduplicate_inst `{sRG : sailRegGS Σ} `{fancy_updates.invGS Σ} (mG : mcMemGS Σ) :
forall (p : Predicate) (ts : Env Val (𝑯_Ty p)),
is_duplicable p = true -> luser_inst mG p ts ⊢ luser_inst mG p ts ∗ luser_inst mG p ts.
Proof.
destruct p; now cbn.
Qed.
(* At this point we have enough information to instantiate the program logic
rules of Iris that do not refer to specific contracts. *)
Include IrisSignatureRules ExampleBase ExampleSignature ExampleProgram ExampleSemantics ExampleIrisBase.
Include IrisAdequacy ExampleBase ExampleSignature ExampleProgram ExampleSemantics ExampleIrisBase ExampleIrisAdeqParams.
End ExampleIrisInstance.
(* The [IrisInstanceWithContracts] implements the program logic rules that can
refer to the [Specification] module, which contains the contracts for
functions. These rules therefore include function call rules and the rule
for a ghost lemma statement. We split up the construction of the model in
this way to allow combinations of multiple different set of contracts for a
single program. *)
Module ExampleIrisInstanceWithContracts.
(* Include our axiomatic program logic. Note that the program logic is
parameterized over a given set of contracts so it is included here
instead of [IrisInstance]. *)
Include ProgramLogicOn ExampleBase ExampleSignature ExampleProgram
ExampleSpecification.
Include IrisInstanceWithContracts ExampleBase ExampleSignature
ExampleProgram ExampleSemantics ExampleSpecification
ExampleIrisBase ExampleIrisAdeqParams ExampleIrisInstance.
(* Import the soundness proofs for the shallow and symbolic executors. *)
Include MicroSail.ShallowSoundness.Soundness ExampleBase ExampleSignature
ExampleProgram ExampleSpecification ExampleShalExec.
Include MicroSail.RefineExecutor.RefineExecOn ExampleBase ExampleSignature
ExampleProgram ExampleSpecification ExampleShalExec ExampleExecutor.
(* In this section we verify the contracts of the foreign functions defined in
Coq and the entailments encoded in ghost lemmas using Iris Proof Mode. *)
Section WithIrisNotations.
Import iris.bi.interface.
Import iris.bi.big_op.
Import iris.base_logic.lib.iprop.
Import iris.program_logic.weakestpre.
Import iris.base_logic.lib.gen_heap.
Import iris.proofmode.string_ident.
Import iris.proofmode.tactics.
Ltac destruct_syminstance ι :=
repeat
match type of ι with
| Env _ (ctx.snoc _ (MkB ?s _)) =>
string_to_ident_cps s
ltac:(fun id =>
let fr := fresh id in
destruct (env.view ι) as [ι fr];
destruct_syminstance ι)
| Env _ ctx.nil => destruct (env.view ι)
| _ => idtac
end.
Lemma mkcons_sound `{sailGS Σ} :
ValidContractForeign sep_contract_mkcons mkcons.
Proof.
intros Γ es δ ι Heq.
destruct (env.view ι) as [ι xs].
destruct (env.view ι) as [ι x].
destruct (env.view ι). cbn.
iIntros "_".
rewrite <-semWP_unfold_nolc. cbn.
iIntros (γ1 μ1) "[Hregs Hmem]".
unfold mem_inv.
iMod (fupd_mask_subseteq empty) as "Hclose2"; first set_solver.
iModIntro.
iIntros (e2 δ2 γ2 μ2 step).
dependent elimination step. cbn.
rewrite Heq in f1. cbn in f1.
destruct_conjs; subst.
do 3 iModIntro.
iMod "Hclose2" as "_".
iMod (gen_heap_alloc μ1 (infinite_fresh (A := Z) (elements (dom μ1))) (x, xs) with "Hmem") as "[Hmem [Hres _]]".
{ rewrite <-not_elem_of_dom, <-elem_of_elements.
now eapply infinite_is_fresh.
}
iModIntro.
iFrame.
iApply wp_value.
now iFrame.
Qed.