Built with Alectryon, running vsrocq-language-server v8.20.1 5.4.0 / 2.4.3. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+βCtrl+β to navigate, Ctrl+π±οΈ to focus. On Mac, use β instead of Ctrl.
(** * Repr: Representation Predicates *)Set Implicit Arguments.#[warnings="-notation-overridden -ambiguous-paths -notation-incompatible-prefix"]
From SLF Require Import LibSepReference.Import ProgramSyntax DemoPrograms.From SLF Require Import Basic.Open Scope liblist_scope.#[warnings="-notation-overridden -ambiguous-paths -notation-incompatible-prefix"]
From Sepviz Require Import SLFNotations.Implicit Typesnm : int.Implicit Typespqsc : loc.Implicit Typesx : val.(* ################################################################# *)(** * First Pass *)(** The previous chapter ([Basic]) covered simple programs that manipulate individual mutable cells. This chapter focuses on the specification and verification of programs manipulating mutable data structures like lists and trees. In Separation Logic, such data structures are specified using _representation predicates_. A representation predicate is a heap predicate that describes a mutable data structure. For example, the heap predicate [MList L p] describes a mutable linked list whose head cell is at location [p] and whose elements are described by the Rocq list [L]. In this chapter, we will see how to define [MList] and how to use this predicate for specifying and verifying functions that operate on mutable linked lists. We will also study representation predicates for mutable trees, as well as for counter functions, which feature an internal state. As explained in the [Preface], this chapter, like all the following ones, is structured in three parts: - The _First Pass_ section presents the most important ideas only. - The _More Details_ section presents additional material explaining in more depth the meaning and the consequences of the key results. By default, readers would eventually read all this material. - The _Optional Material_ section contains more advanced material, for readers who can afford to invest more time in the topic. While going through proofs, we will introduce a few additional tactics. - [xpull] to extract pure facts and quantifiers from the LHS of [==>]. - [xchange E] for exploiting a lemma [E] with a conclusion of the form [H1 ==> H2] or [H1 = H2]. - [xchange <- E] for exploiting an entailment [H2 ==> H1] in the case [E] is a lemma with a conclusion of the form [H1 = H2]. - [xchanges] is a shorthand for [xchange] followed with [xsimpl]. - [xfun] to reason about function definitions. - [xtriple] to establish specifications for abstract functions. - [introv] (a TLC tactic) like [intros] but takes as arguments only the name of the hypotheses, not of all variables. - [rew_list] (a TLC tactic) to normalize list expressions. Furthermore, TLC tactics and most x-tactics may be followed with a star symbol to invoke [eauto]. For example, [xsimpl*] is a shorthand for [xsimpl; eauto]. The star symbol is used to make proof scripts more concise, yet exercises can all be solved without this feature. *)(* ================================================================= *)(** ** The List Representation Predicate *)(** The implementation of mutable lists and trees involves the use of records. For simplicity, field names are represented as natural numbers. For example, to represent a list cell with a head and a tail field, we define [head] as the constant zero, and [tail] as the constant one. *)Definitionhead : field := 0%nat.Definitiontail : field := 1%nat.(** The heap predicate [p ~~~>`{ head := x; tail := q }] describes a record allocated at location [p], with a head field storing [x] and a tail field storing [q]. The arrow [~~~>] is provided by the framework for describing records. The notation [`{...}] is a handy notation for a list of pairs of field names and values of arbitrary types. (Further details on the formalization of records are presented in chapter [Records].) A mutable list consists of a chain of cells. Each cell stores a tail pointer that gives the location of the next cell in the chain. The last cell stores the [null] value in its tail field. The heap predicate [MList L p] describes a list whose head cell is at location [p] and whose elements are described by the list [L]. This predicate is defined recursively on the structure of [L]. - If [L] is empty, then [p] is the null pointer. - If [L] is of the form [x::L'], then [p] is not null, the head field of [p] contains [x], and the tail field of [p] contains a pointer [q] such that [MList L' q] describes the tail of the list. This definition is formalized as follows. *)FixpointMList (L:list val) (p:loc) : hprop :=
match L with
| nil => \[p = null]
| x::L' => \existsq, (p ~~~> `{ head := x; tail := q}) \* (MList L' q)
end.Notation"'PointsTo' β p β β¦ '$MList' β x β§" :=
(MList x p)
(in custom sep at level200,
p constr, x constr at level200): sepviz_scope.(* ================================================================= *)(** ** Alternative Characterizations of [MList] *)(** Carrying out proofs directly with [MList] can be slightly cumbersome, mainly due to Rocq's limited support for re-folding definitions. We find it more practical to explicitly state equalities that paraphrase the definition of [MList]. There is one equality for the [nil] case and one for the [cons] case. *)
forallp, β¬ PointsTo β p β β¦ $MList β nil β§ β = β¬ Pure β p = null β
forallp, β¬ PointsTo β p β β¦ $MList β nil β§ β = β¬ Pure β p = null β
auto.Qed.
forallpx (L' : list val),
β¬ PointsTo β p β β¦ $MList β x :: L' β§ β =
β¬
Exist β q
β β¬
Star β β¬ PointsTo β p β β¦ $Record2_val β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β
forallpx (L' : list val),
β¬ PointsTo β p β β¦ $MList β x :: L' β§ β =
β¬
Exist β q
β β¬
Star β β¬ PointsTo β p β β¦ $Record2_val β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β
auto.Qed.(** In addition, it is also very useful in proofs to reformulate the definition of [MList L p] in the form of a case analysis on whether the pointer [p] is null or not. This corresponds to the programming pattern [if p == null then ... else ...]. This alternative characterization of [MList L p] asserts the following. - If [p] is null, then [L] is empty. - Otherwise, [L] decomposes as [x::L'], the head field of [p] contains [x], and the tail field of [p] contains a pointer [q] such that [MList L' q] describes the tail of the list. *)(** The corresponding lemma, shown below, is stated using the [If P then X else Y] construction, which generalizes Rocq's construction [if b then X else Y] to discriminate over a proposition [P] as opposed to a boolean value [b]. The [If] construct leverages classical logic; it is provided by the TLC library. The tactic [case_if] is convenient for performing the case analysis on whether [P] is true or false. *)
forallp (L : list val),
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β==>β¬* POST @
β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2_val β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
(** The proof is a bit technical: it may be skipped on a first reading. *)
forallp (L : list val),
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β==>β¬* POST @
β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2_val β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
Admitted.(** Note that the reciprocal entailment to the one stated in [MList_if] is also true, but we do not need it so we do not bother proving it here. In the rest of the course, we will never unfold the definition [MList], but only work with [MList_nil], [MList_cons], and [MList_if]. We therefore make the definition of [MList] opaque to prevent Rocq from performing undesired simplifications. *)GlobalOpaque MList.(* ================================================================= *)(** ** In-place Concatenation of Two Mutable Lists *)(** The function [append] expects two arguments: a pointer [p1] to a nonempty list, and a pointer [p2] to another list, possibly empty. The function updates the last cell from the first list in such a way that its tail points to the head cell of [p2]. After this operation, the pointer [p1] points to a list that corresponds to the concatenation of the two input lists.OCaml: let rec append p1 p2 = if p1.tail == null then p1.tail <- p2 else append p1.tail p2*)Definitionappend : val :=
<{ fix 'f 'p1 'p2 =>
let'q1 = 'p1`.tail inlet'b = ('q1 = null) inif 'b
then 'p1`.tail := 'p2
else 'f 'q1 'p2 }>.(** The append function is specified and verified as shown below. The proof pattern is representative of that of many list-manipulating functions, so it is essential that you take the time to play through every step of this proof. Several exercises will require a very similar proof pattern. *)
(** The TLC tactic [introv] is very convenient for providing explicit names for hypotheses without having to provide names for the variables already named in the lemma statement. *)
(** The induction principle provides a hypothesis for the tail of [L1]. The predicate [list_sub L1' L1] asserts that [L1] decomposes as [x::L1'] for some [x]. *)
L2: list val p2: loc L1: list val IH: forally : list val,
list_sub y L1 ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N: p1 <> null
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β L1 β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
CODE <[
Letv := `(App (val_get_field tail) p1) in
`(Letv0 := `(App val_eq v null) in
`(If_ v0 Then
`(App (val_set_field tail) p1 p2)
Else
`(App append v p2))) ]>
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β L1 ++ L2 β§ β *β
(** To begin the proof, we reveal the head cell of [p1], exploiting the assumption that [p1 <> null]. *)
L2: list val p2: loc L1: list val IH: forally : list val,
list_sub y L1 ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N: p1 <> null
β¬* PRE @ β¬
Star β β¬
IfThenElse β β¬ Pure β p1 = null β β
β¬ Pure β L1 = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L1 = x :: L' β
β β¬
Star β β¬
PointsTo β p1
β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
CODE <[
Letv := `(App (val_get_field tail) p1) in
`(Letv0 := `(App val_eq v null) in
`(If_ v0 Then
`(App (val_set_field tail) p1 p2)
Else
`(App append v p2))) ]>
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β L1 ++ L2 β§ β *β
L2: list val p2: loc L1: list val IH: forally : list val,
list_sub y L1 ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null
β¬* PRE @ β¬
Star β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L1 = x :: L' β
β β¬
Star β β¬
PointsTo β p1
β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
CODE <[
Letv := `(App (val_get_field tail) p1) in
`(Letv0 := `(App val_eq v null) in
`(If_ v0 Then
`(App (val_set_field tail) p1 p2)
Else
`(App append v p2))) ]>
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β L1 ++ L2 β§ β *β
(** Using [xpull], we extract the existentially quantified variables: we let [q1] denote the tail of [p1], [x] the head element of [L1], and [L1'] the tail of [L1]. *)
L2: list val p2: loc L1: list val IH: forally : list val,
list_sub y L1 ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null
forallx (x0 : loc) (x1 : list val),
L1 = x :: x1 ->
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $Record2 β head β x β tail β x0 β§ β
β β¬
Star β β¬ PointsTo β x0 β β¦ $MList β x1 β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β β *β
CODE <[
Letv := `(App (val_get_field tail) p1) in
`(Letv0 := `(App val_eq v null) in
`(If_ v0 Then
`(App (val_set_field tail) p1 p2)
Else
`(App append v p2))) ]>
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β L1 ++ L2 β§ β *β
(** The tactic [intros ->] is a shorthand for [intros E; rewrite E; clear E]. *)
L2: list val p2: loc x: val L1': list val IH: forally : list val,
list_sub y (x :: L1') ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null q1: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $Record2 β head β x β tail β q1 β§ β
β β¬
Star β β¬ PointsTo β q1 β β¦ $MList β L1' β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β β *β
CODE <[
Letv := `(App (val_get_field tail) p1) in
`(Letv0 := `(App val_eq v null) in
`(If_ v0 Then
`(App (val_set_field tail) p1 p2)
Else
`(App append v p2))) ]>
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β (x :: L1') ++ L2 β§ β *β
(** We then reason about the if statement. *)
L2: list val p2: loc x: val L1': list val IH: forally : list val,
list_sub y (x :: L1') ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null q1: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $Record2 β head β x β tail β q1 β§ β
β β¬
Star β β¬ PointsTo β q1 β β¦ $MList β L1' β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β β *β
CODE <[
Letv := `(App val_eq q1 null) in
`(If_ v Then
`(App (val_set_field tail) p1 p2)
Else
`(App append q1 p2)) ]>
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β (x :: L1') ++ L2 β§ β *β
L2: list val p2: loc x: val L1': list val IH: forally : list val,
list_sub y (x :: L1') ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null q1: loc
(** In this case, we reason about the assignment, then we fold back the head cell. To that end, we exploit the tactic [xchange <- MList_cons], which is similar to [xchange MList_cons] but exploits the equality asserted by [MList_cons] in the reverse direction. *)
L2: list val p2: loc x: val IH: forally : list val,
list_sub y (x :: nil) ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null q1: loc Cq1, C0: q1 = null
xchange <- MList_cons.(** Above, the tactic [xchange] discharges the goal because the LHS, after the operation, matches the RHS. To see the details, change the line of script to: [xapp. eapply himpl_trans. xchange <- MList_cons. apply himpl_refl.] *)
L2: list val p2: loc x: val L1': list val IH: forally : list val,
list_sub y (x :: L1') ->
forallp1,
p1 <> null ->
SPEC append p1 p2
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β y β§ β
β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β *β
β¬* POST @ fun_ : val => β¬ PointsTo β p1 β β¦ $MList β y ++ L2 β§ β *β p1: loc N, C: p1 <> null q1: loc Cq1: q1 <> null
xchange <- MList_cons.}Qed.(* ================================================================= *)(** ** Smart Constructors for Linked Lists *)(** We next introduce two smart constructors for linked lists, called [mnil] and [mcons]. The operation [mnil()] creates an empty list. Its implementation simply returns the value [null]. Its specification asserts that the return value is a pointer [p] such that [MList nil p]. In the specification below, recall that [funloc p => H] is a notation for [fun (r:val) => \exists (p:loc), \[r = val_loc p] \* H]. *)Definitionmnil : val :=
<{ fun'u =>
null }>.(** Note: in theory, we could present [mnil] as a constant rather than as a function that takes unit as argument. However, viewing it as a function like all the other example programs helps keep the framework minimal. *)
SPEC mnil <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β nil β§ β *β
SPEC mnil <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β nil β§ β *β
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ Val null ]>
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β nil β§ β *β
(** We are requested to prove that, under the empty precondition, the value [null] satisfies the postcondition [fun (r:val) => \exists (p:loc), \[r = val_loc p] \* (MList nil p)]. As the tactic [xval] shows, this is equivalent to proving that the precondition [\[]] entails [exists p, \[null = val_loc p] \* (MList nil p))]. *)
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬
Exist β p
β β¬
Star β
β¬ Pure β null = p β
β β¬
PointsTo β p β β¦ $MList β nil β§
β β β *β
(** To conclude the proof, it suffices to instantiate [p] with [null], and to argue that [MList nil null] can created out of thin air. This is indeed true because [MList nil null] is equivalent to [\[]]. The first step is to obtain an explicit statement of this equivalence, in the form of an hypothesis named [E]. By instantiating the lemma [MList_nil], which asserts that [forall p, MList nil p = \[p = null]], we obtain the equality [MList nil null = \[null = null]]. *)
β¬ PointsTo β null β β¦ $MList β nil β§ β = β¬ Pure β null = null β ->
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬
Exist β p
β β¬
Star β
β¬ Pure β null = p β
β β¬
PointsTo β p β β¦ $MList β nil β§
β β β *β
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬
Exist β p
β β¬
Star β
β¬ Pure β null = p β
β β¬
PointsTo β p β β¦ $MList β nil β§
β β β *β
(** Then, we invoke [xchange <- E]. In exchange for proving [null = null], this tactic replaces the empty heap predicate [\[]] with [MList nil null]. *)
auto.Qed.(** The proof above can be greatly shortened by using the tactic [xchanges], which is a shorthand for [xchange <- E] followed by [xsimpl]. In fact, we even use [xchanges* <- E], to invoke [xchanges <- E] followed by [eauto]. *)
SPEC mnil <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β nil β§ β *β
SPEC mnil <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β nil β§ β *β
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ Val null ]>
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β nil β§ β *β
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬
Exist β p
β β¬
Star β
β¬ Pure β null = p β
β β¬
PointsTo β p β β¦ $MList β nil β§
β β β *β
xchanges* <- (MList_nil null).Qed.#[global] Hint Resolve triple_mnil : triple.(** Observe that the specification [triple_mnil] does not mention the [null] pointer anywhere. Hence, this specification abstracts away from the implementation details of how mutable lists are represented internally. *)(** The operation [mcons x q] creates a fresh list cell, with [x] in the head field and [q] in the tail. Its implementation allocates and initializes a fresh record made of two fields. The allocation operation leverages the allocation construct written [`{ head := 'x; tail := 'q }] in the code. This construct is in fact a notation for a primitive operation called [val_new_hrecord_2]. The details of record constructions are explained in the chapter [Record]. There is no need to understand the details at this stage. *)Definitionmcons : val :=
<{ fun'x'q =>
`{ head := 'x; tail := 'q } }>.(** The operation [mcons] admits two specifications. The first one says that it produces a record described with a record heap predicate. *)
forallxq,
SPEC mcons x q
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $Record2_val β head β x β tail β q β§
β *β
(** To prove this lemma, we need to know that the record allocation operation is in fact a notation for a call to [val_new_hrecord_2], hence we need to invoke a library lemma called [triple_new_hrecord_2] for reasoning about this call. (Don't worry: this proof pattern will not appear in exercises!) *)
forallxq,
SPEC mcons x q
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $Record2_val β head β x β tail β q β§
β *β
x: val q: loc
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App (val_new_hrecord_2 head tail) x q ]>
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β *β
x: val q: loc
forallx1 : loc,
β¬* PRE @ β¬ PointsTo β x1 β β¦ $Record2 β head β x β tail β q β§ β *β==>β¬* POST @
β¬
Exist β p
β β¬
Star β β¬ Pure β x1 = p β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β
xsimpl*.Qed.(** The second specification asserts that [mcons] can be used to extend a mutable list. It assumes that the argument [q] comes with a list representation of the form [Mlist q L], and it specifies that the function [mcons] produces the representation predicate [Mlist p (x::L)]. This second specification is derivable from the first one, by folding the representation predicate [MList] using the tactic [xchange]. *)
forall (L : list val) xq,
SPEC mcons x q
β¬* PRE @ β¬ PointsTo β q β β¦ $MList β L β§ β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β x :: L β§ β *β
forall (L : list val) xq,
SPEC mcons x q
β¬* PRE @ β¬ PointsTo β q β β¦ $MList β L β§ β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β x :: L β§ β *β
L: list val x: val q: loc
SPEC mcons x q
β¬* PRE @ β¬ PointsTo β q β β¦ $MList β L β§ β *β
β¬* POST @ funloc p => β¬ PointsTo β p β β¦ $MList β x :: L β§ β *β
L: list val x: val q: loc
forallx1 : loc,
β¬* PRE @ β¬
Star β β¬ PointsTo β x1 β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L β§ β β *β==>β¬* POST @
β¬
Exist β p
β β¬ Star β β¬ Pure β x1 = p β β β¬ PointsTo β p β β¦ $MList β x :: L β§ β β β *β
L: list val x: val q, p: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L β§ β β *β==>β¬* POST @
β¬
Exist β p0
β β¬ Star β β¬ Pure β p = p0 β β β¬ PointsTo β p0 β β¦ $MList β x :: L β§ β β β *β
L: list val x: val q, p: loc
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β x :: L β§ β *β==>β¬* POST @
β¬
Exist β p0
β β¬ Star β β¬ Pure β p = p0 β β β¬ PointsTo β p0 β β¦ $MList β x :: L β§ β β β *β
xsimpl*.Qed.(** In practice, this second specification is more often useful than the first one, hence we register it in the database for [xapp]. It remains possible to invoke [xapp triple_mcons] to exploit the first specification, where needed. *)#[global] Hint Resolve triple_mcons' : triple.(** In what follows, we consider several classic operations on mutable lists, each illustrating an idiomatic proof pattern. *)(* ================================================================= *)(** ** Copy Function for Lists *)(** The function [mcopy] takes a mutable linked list and builds an independent copy of it, with the help of the functions [mnil] and [mcons].OCaml: let rec mcopy p = if p == null then mnil () else mcons (p.head) (mcopy p.tail)*)Definitionmcopy : val :=
<{ fix 'f 'p =>
let'b = ('p = null) inif 'b
then mnil ()
elselet'x = 'p`.head inlet'q = 'p`.tail inlet'q2 = ('f 'q) in
mcons 'x 'q2 }>.(** The precondition of [mcopy] requires a linked list described as [MList L p]. The postcondition asserts that the function returns a pointer [p'] and a list described as [MList L p'], in addition to the original list [MList L p]. The two lists are totally disjoint and independent, as captured by the separating conjunction symbol (the star). *)
forall (L : list val) p,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
(** The proof structure is like the previous ones. While playing the script, try to spot the places where - [mnil] produces an empty list of the form [MList nil p'], - the recursive call produces a list of the form [MList L' q'], and - [mcons] produces a list of the form [MList (x::L') p']. *)
forall (L : list val) p,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val p: loc
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
CODE <[
Letv := `(App val_eq p null) in
`(If_ v Then
`(App mnil <{ () }>)
Else
`(Letv0 := `(App (val_get_field head) p) in
`(Letv1 := `(App (val_get_field tail) p) in
`(Letv2 := `(App mcopy v1) in
`(App mcons v0 v2))))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
CODE <[
If_ (isTrue (p = null)) Then
`(App mnil <{ () }>)
Else
`(Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1)))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[
If_ (isTrue (p = null)) Then
`(App mnil <{ () }>)
Else
`(Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1)))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
p = null ->
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
p <> null ->
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
p = null ->
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C: p = null
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
(** Note that [C] is actually [val_loc p = val_loc null], and not simply [p = null]. This explains why a call to [subst] does nothing. If needed, use the TLC tactic, [inverts C] or [invert C] to exploit the equality. In this specific proof, though, we don't need that. *)
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
β¬* PRE @ β¬ Pure β L = nil β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
L = nil ->
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null E: L = nil
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β nil β§ β
β β¬ PointsTo β p' β β¦ $MList β nil β§ β β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
forallx0 : loc,
β¬* PRE @ β¬ PointsTo β x0 β β¦ $MList β nil β§ β *β==>β¬* POST @
β¬
Exist β p'
β β¬
Star β β¬ Pure β x0 = p' β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β nil β§ β
β β¬ PointsTo β p' β β¦ $MList β nil β§ β β β β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
forallx0 : loc, x0 = x0
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬ PointsTo β p β β¦ $MList β nil β§ β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
forallx0 : loc, x0 = x0
auto.
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬ PointsTo β p β β¦ $MList β nil β§ β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β C: null = null
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β C: null = null
null = null
auto.
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
p <> null ->
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
p <> null ->
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C: p <> null
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null
β¬* PRE @ β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null
forallx (x0 : loc) (x1 : list val),
L = x :: x1 ->
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β x0 β§ β
β β¬ PointsTo β x0 β β¦ $MList β x1 β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null x: val q: loc L': list val E: L = x :: L'
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[
Letv := `(App (val_get_field tail) p) in
`(Letv0 := `(App mcopy v) in
`(App mcons x v0)) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[ Letv := `(App mcopy q) in
`(App mcons x v) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
forallx1 : loc,
β¬* PRE @ β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬
Star β β¬ PointsTo β x1 β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β
CODE <[ App mcons x x1 ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q': loc
β¬* PRE @ β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬
Star β β¬ PointsTo β q' β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β
CODE <[ App mcons x q' ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q': loc
forallx1 : loc,
β¬* PRE @ β¬
Star β β¬ PointsTo β x1 β β¦ $MList β x :: L' β§ β
β β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β==>β¬* POST @
β¬
Exist β p'
β β¬
Star β β¬ Pure β x1 = p' β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q', p': loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β
β β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β==>β¬* POST @
β¬
Exist β p'0
β β¬
Star β β¬ Pure β p' = p'0 β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p'0 β β¦ $MList β x :: L' β§ β β β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q', p': loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β==>β¬* POST @
β¬
Exist β p'0
β β¬
Star β β¬ Pure β p' = p'0 β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p'0 β β¦ $MList β x :: L' β§ β β β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q', p': loc
p' = p'
auto.}Qed.(** A Rocq expert would typically write the same proof script in a more compact fashion, as follows. Recall that the token [->] can be provided instead of a fresh name to indicate on-the-fly substitution, and that the star token after a tactic indicates a call to [eauto]. *)
forall (L : list val) p,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
forall (L : list val) p,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val p: loc
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
CODE <[
Letv := `(App val_eq p null) in
`(If_ v Then
`(App mnil <{ () }>)
Else
`(Letv0 := `(App (val_get_field head) p) in
`(Letv1 := `(App (val_get_field tail) p) in
`(Letv2 := `(App mcopy v1) in
`(App mcons v0 v2))))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
CODE <[
If_ (isTrue (p = null)) Then
`(App mnil <{ () }>)
Else
`(Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1)))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc
β¬* PRE @ β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β L = nil β
β β¬
Exist β x
β β¬
Exist β q
β β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§
β β β¬ PointsTo β q β β¦ $MList β L' β§ β β β β β β β *β
CODE <[
If_ (isTrue (p = null)) Then
`(App mnil <{ () }>)
Else
`(Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1)))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
L = nil ->
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null
forallx (x0 : loc) (x1 : list val),
L = x :: x1 ->
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β x0 β§ β
β β¬ PointsTo β x0 β β¦ $MList β x1 β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
L = nil ->
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App mnil <{ () }> ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β nil β§ β
β β¬ PointsTo β p' β β¦ $MList β nil β§ β β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
forallx0 : loc,
β¬* PRE @ β¬ PointsTo β x0 β β¦ $MList β nil β§ β *β==>β¬* POST @
β¬
Exist β p'
β β¬
Star β β¬ Pure β x0 = p' β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β nil β§ β
β β¬ PointsTo β p' β β¦ $MList β nil β§ β β β β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p = null
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬ PointsTo β p β β¦ $MList β nil β§ β *β
IH: forally : list val,
list_sub y nil ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β C: null = null
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null
forallx (x0 : loc) (x1 : list val),
L = x :: x1 ->
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β x0 β§ β
β β¬ PointsTo β x0 β β¦ $MList β x1 β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
L: list val IH: forally : list val,
list_sub y L ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null
forallx (x0 : loc) (x1 : list val),
L = x :: x1 ->
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β x0 β§ β
β β¬ PointsTo β x0 β β¦ $MList β x1 β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β L β§ β
β β¬ PointsTo β p' β β¦ $MList β L β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[
Letv := `(App (val_get_field head) p) in
`(Letv0 := `(App (val_get_field tail) p) in
`(Letv1 := `(App mcopy v0) in
`(App mcons v v1))) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[
Letv := `(App (val_get_field tail) p) in
`(Letv0 := `(App mcopy v) in
`(App mcons x v0)) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β
β β¬ PointsTo β q β β¦ $MList β L' β§ β β *β
CODE <[ Letv := `(App mcopy q) in
`(App mcons x v) ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q: loc
forallx1 : loc,
β¬* PRE @ β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬
Star β β¬ PointsTo β x1 β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β
CODE <[ App mcons x x1 ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q': loc
β¬* PRE @ β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬
Star β β¬ PointsTo β q' β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β
CODE <[ App mcons x q' ]>
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q': loc
forallx1 : loc,
β¬* PRE @ β¬
Star β β¬ PointsTo β x1 β β¦ $MList β x :: L' β§ β
β β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β==>β¬* POST @
β¬
Exist β p'
β β¬
Star β β¬ Pure β x1 = p' β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q', p': loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β
β β¬
Star β β¬ PointsTo β q β β¦ $MList β L' β§ β
β β¬ PointsTo β p β β¦ $Record2 β head β x β tail β q β§ β β β *β==>β¬* POST @
β¬
Exist β p'0
β β¬
Star β β¬ Pure β p' = p'0 β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p'0 β β¦ $MList β x :: L' β§ β β β β *β
x: val L': list val IH: forally : list val,
list_sub y (x :: L') ->
forallp,
SPEC mcopy p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β y β§ β *β
β¬* POST @ funloc p' => β¬
Star β β¬ PointsTo β p β β¦ $MList β y β§ β
β β¬ PointsTo β p' β β¦ $MList β y β§ β β *β p: loc C, C0: p <> null q, q', p': loc
β¬* PRE @ β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p' β β¦ $MList β x :: L' β§ β β *β==>β¬* POST @
β¬
Exist β p'0
β β¬
Star β β¬ Pure β p' = p'0 β
β β¬
Star β β¬ PointsTo β p β β¦ $MList β x :: L' β§ β
β β¬ PointsTo β p'0 β β¦ $MList β x :: L' β§ β β β β *β
xsimpl*.}Qed.(* ================================================================= *)(** ** Length Function for Lists *)(** The function [mlength] computes the length of a mutable linked list.OCaml: let rec mlength p = if p == null then 0 else 1 + mlength p.tail*)Definitionmlength : val :=
<{ fix 'f 'p =>
let'b = ('p = null) inif 'b
then0else (let'q = 'p`.tail inlet'n = 'f 'q in
'n + 1) }>.(** **** Exercise: 3 stars, standard, especially useful (triple_mlength) Prove the correctness of the function [mlength]. Hint: use the tactic [rew_list] to normalize list expressions -- in particular, to prove [length L' + 1 = length (x :: L')]. *)
forall (L : list val) p,
SPEC mlength p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β p β β¦ $MList β L β§ β
β *β
forall (L : list val) p,
SPEC mlength p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β p β β¦ $MList β L β§ β
β *β
Admitted.(** [] *)(* ================================================================= *)(** ** Alternative Length Function for Lists *)(** In this section, we consider an alternative implementation of [mlength] that uses an auxiliary reference cell to keep track of the number of cells traversed so far. The list is traversed recursively, incrementing the contents of the reference once for every cell.OCaml: let rec listacc c p = if p == null then () else (incr c; listacc c p.tail) let mlength' p = let c = ref 0 in listacc c p; !c*)Definitionacclength : val :=
<{ fix 'f 'c 'p =>
let'b = ('p <> null) inif 'b then
incr 'c;
let'q = 'p`.tail in
'f 'c 'q
end }>.Definitionmlength' : val :=
<{ fun'p =>
let'c = ref 0in
acclength 'c 'p;
get_and_free 'c }>.(** (Recall that [get_and_free] was defined in chapter [Basic]. *)(** **** Exercise: 3 stars, standard, especially useful (triple_mlength') Prove the correctness of the function [mlength']. Hint: start by stating a lemma [triple_acclength] expressing the specification of the recursive function [acclength]. Make sure to generalize the appropriate variables before applying the well-founded induction tactic. Then complete the proof of the specification [triple_mlength'], using [xapp triple_acclength] to reason about the call to the auxiliary function. Recall that [rew_list] can be used to simplify [length (x :: L')] into [length L' + 1] and that [math] can be used to prove arithmetic equalities such as [(n + 1) + m = n + (m + 1)]. *)(* FILL IN HERE *)
forall (L : list val) p,
SPEC mlength' p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β p β β¦ $MList β L β§ β
β *β
forall (L : list val) p,
SPEC mlength' p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β p β β¦ $MList β L β§ β
β *β
Admitted.(** [] *)(* ================================================================= *)(** ** Free Function for Lists *)(** The operation [mfree] deallocates all the cells in a given list. Each cell consists of a record that can be deallocated using the primitive operation [delete]. The deallocation of a list is implemented by recursively traversing the list, invoking [delete] on each cell after reading its tail pointer.OCaml: let rec mfree p = if p != null then begin let q = p.tail in delete p; mfree q end*)Definitionmfree : val :=
<{ fix 'f 'p =>
let'b = ('p <> null) inif 'b thenlet'q = 'p`.tail in
delete 'p;
'f 'q
end }>.(** The precondition of [mfree] requires a full list [MList L p]. The postcondition is empty: the entire list is destroyed. *)(** **** Exercise: 3 stars, standard, especially useful (Triple_mfree) Verify the function [mfree]. *)
forall (L : list val) p,
SPEC mfree p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ fun_ : val => β¬ Opaque β emp β *β
forall (L : list val) p,
SPEC mfree p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ fun_ : val => β¬ Opaque β emp β *β
Admitted.(** [] *)(* ================================================================= *)(** ** In-Place Reversal Function for Lists *)(** The function [mrev] takes as argument a pointer to a mutable list, and returns a pointer on the reverse list, that is, a list with elements in the reverse order. The cells from the input list are reused for constructing the output list: the operation is said to be "in place".OCaml: let rec mrev_aux p1 p2 = if p2 == null then p1 else (let p3 = p2.tail in p2.tail <- p1; mrev_aux p2 p3) let mrev p = mrev_aux null p*)Definitionmrev_aux : val :=
<{ fix 'f 'p1 'p2 =>
let'b = ('p2 = null) inif 'b
then 'p1
else (
let'p3 = 'p2`.tail in
'p2`.tail := 'p1;
'f 'p2 'p3) }>.Definitionmrev : val :=
<{ fun'p =>
mrev_aux null 'p }>.(** **** Exercise: 4 stars, standard, optional (triple_mrev) Prove the correctness of the functions [mrev_aux] and [mrev]. Hint: here again, start by stating a lemma [triple_mrev_aux] expressing the specification of the recursive function [mrev_aux]. Make sure to generalize the appropriate variables before applying the well-founded induction tactic. Then complete the proof of [triple_mrev], using [xapp triple_mrev_aux]. Hint: in [triple_mrev], you will need to create an empty list out of thin air, using [xchange <- (MList_nil null)]. Also, make sure to call [rew_list] before [xsimpl]; otherwise [xsimpl] might not guess the right existential variable. The syntax [xsimpl v] is also available if you want to specify the witness [x] inside a goal of the form [H1 ==> (\exists x, H2)]. *)(* FILL IN HERE *)
forall (L : list val) p,
SPEC mrev p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc q => β¬ PointsTo β q β β¦ $MList β rev L β§ β *β
forall (L : list val) p,
SPEC mrev p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funloc q => β¬ PointsTo β q β β¦ $MList β rev L β§ β *β
Admitted.(** [] *)(* ################################################################# *)(** * More Details *)(** The rest of this chapter presents the formal verification of more advanced data structures and algorithms. The remaining chapters in the book explain how to construct the program verification framework that we have been using so far. The two aspects are completely orthogonal. Thus, if you have time and are interested in the practical aspects of program verification, you should complete the present chapter before moving on. On the other hand, if you are eager to dive into the construction of Separation Logic, then you may safely move on to the next chapter and come back to this later. *)(* ================================================================= *)(** ** A Stack Represented as a List and a Size *)ModuleSizedStack.(** In this section, we consider the implementation of a mutable stack featuring a constant-time access to the size of the stack. This stack structure consists of a 2-field record storing a pointer to a mutable linked list plus an integer recording the length of that list. The implementation includes a function [create] to allocate an empty stack, a function [sizeof] for reading the size, and functions [push], [top], and [pop] for manipulating the top of the stack.OCaml: type 'a stack = { data : 'a list; size : int } let create () = { data = null; size = 0 } let sizeof s = s.size let push p x = s.data <- mcons x s.data; s.size <- s.size + 1 let top s = let p = s.data in p.head let pop s = let p = s.data in let x = p.head in let q = p.tail in delete p; s.data <- q in s.size <- s.size - 1; x*)(** The constants [data] and [size] are introduced as identifiers for the record fields of the type ['a stack]. *)Definitiondata : field := 0%nat.Definitionsize : field := 1%nat.(** The representation predicate for the stack takes the form [Stack L s], where [s] denotes the location of the record describing the stack and [L] denotes the list of items stored in the stack. The underlying mutable list is described as [MList L p], where [p] is the location [p] stored in the first field of the record. The definition of [Stack] is as follows. *)DefinitionStack (L:list val) (s:loc) : hprop :=
\existsp, s ~~~>`{ data := p; size := length L } \* (MList L p).Notation"'PointsTo' β p β β¦ '$Stack' β x β§" :=
(Stack x p)
(in custom sep at level200,
p constr, x constr at level200): sepviz_scope.(** Observe that the predicate [Stack] does not expose the location of the mutable list; this location is existentially quantified in the definition. It also does not expose the size of the stack, as this value can be obtained from [length L]. Let's start with the specification and verification of [create] and [sizeof]. *)Definitioncreate : val :=
<{ fun'u =>
`{ data := null; size := 0 } }>.
SPEC create <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc s => β¬ PointsTo β s β β¦ $Stack β nil β§ β *β
SPEC create <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc s => β¬ PointsTo β s β β¦ $Stack β nil β§ β *β
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[ App (val_new_hrecord_2 data size) null 0 ]>
β¬* POST @ funloc s => β¬ PointsTo β s β β¦ $Stack β nil β§ β *β
forallx0 : loc,
β¬* PRE @ β¬ PointsTo β x0 β β¦ $Record2 β data β null β size β 0 β§ β *β==>β¬* POST @
β¬
Exist β s
β β¬ Star β β¬ Pure β x0 = s β β β¬ PointsTo β s β β¦ $Stack β nil β§ β β β *β
s: loc
β¬* PRE @ β¬ PointsTo β s β β¦ $Record2 β data β null β size β 0 β§ β *β==>β¬* POST @
β¬
Exist β s0
β β¬ Star β β¬ Pure β s = s0 β β β¬ PointsTo β s0 β β¦ $Stack β nil β§ β β β *β
s: loc
β¬* PRE @ β¬ PointsTo β s β β¦ $Record2 β data β null β size β 0 β§ β *β==>β¬* POST @
β¬
Exist β s0
β β¬
Star β β¬ Pure β s = s0 β
β β¬
Exist β p
β β¬
Star β β¬ PointsTo β s0 β β¦ $Record2 β data β p β size β length nil β§ β
β β¬ PointsTo β p β β¦ $MList β nil β§ β β β β β *β
xchange* <- (MList_nil null).Qed.(** The [sizeof] operation returns the contents of the [size] field of a stack. *)Definitionsizeof : val :=
<{ fun'p =>
'p`.size }>.
forall (L : list val) s,
SPEC sizeof s
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β s β β¦ $Stack β L β§ β
β *β
forall (L : list val) s,
SPEC sizeof s
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β s β β¦ $Stack β L β§ β
β *β
L: list val s: loc
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
CODE <[ App (val_get_field size) s ]>
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β s β β¦ $Stack β L β§ β
β *β
L: list val s: loc
β¬* PRE @ β¬
Exist β p
β β¬
Star β β¬ PointsTo β s β β¦ $Record2 β data β p β size β length L β§
β β β¬ PointsTo β p β β¦ $MList β L β§ β β β *β
CODE <[ App (val_get_field size) s ]>
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β
β β¬
Exist β p
β β¬
Star β β¬
PointsTo β s β β¦ $Record2 β data β p β size β length L β§
β β β¬ PointsTo β p β β¦ $MList β L β§ β β β β *β
L: list val s: loc
forallx : loc,
β¬* PRE @ β¬
Star β β¬ PointsTo β s β β¦ $Record2 β data β x β size β length L β§ β
β β¬ PointsTo β x β β¦ $MList β L β§ β β *β
CODE <[ App (val_get_field size) s ]>
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β
β β¬
Exist β p
β β¬
Star β β¬
PointsTo β s β β¦ $Record2 β data β p β size β length L β§
β β β¬ PointsTo β p β β¦ $MList β L β§ β β β β *β
L: list val s, p: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β s β β¦ $Record2 β data β p β size β length L β§ β
β β¬ PointsTo β p β β¦ $MList β L β§ β β *β
CODE <[ App (val_get_field size) s ]>
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β
β β¬
Exist β p0
β β¬
Star β β¬
PointsTo β s β β¦ $Record2 β data β p0 β size β length L
β§ β β β¬ PointsTo β p0 β β¦ $MList β L β§ β β β β *β
L: list val s, p: loc
β¬* PRE @ β¬
Star β β¬ PointsTo β s β β¦ $Record2 β data β p β size β length L β§ β
β β¬ PointsTo β p β β¦ $MList β L β§ β β *β==>β¬* POST @
β¬
Star β β¬ Pure β length L = length L β
β β¬
Exist β p0
β β¬
Star β β¬ PointsTo β s β β¦ $Record2 β data β p0 β size β length L β§ β
β β¬ PointsTo β p0 β β¦ $MList β L β§ β β β β *β
xsimpl*.Qed.(** The [push] operation extends the head of the list and increments the size field. *)Definitionpush : val :=
<{ fun's'x =>
let'p = 's`.data inlet'p2 = mcons 'x 'p in
's`.data := 'p2;
let'n = 's`.size inlet'n2 = 'n + 1in
's`.size := 'n2 }>.(** **** Exercise: 3 stars, standard, especially useful (triple_push) Prove the following specification for the [push] operation. *)
forall (L : list val) sx,
SPEC push s x
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ fun_ : val => β¬ PointsTo β s β β¦ $Stack β x :: L β§ β *β
forall (L : list val) sx,
SPEC push s x
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ fun_ : val => β¬ PointsTo β s β β¦ $Stack β x :: L β§ β *β
Admitted.(** [] *)(** The [pop] operation extracts the element at the head of the list, updates the [data] field to the tail of the list and decrements the size field. *)Definitionpop : val :=
<{ fun's =>
let'p = 's`.data inlet'x = 'p`.head inlet'p2 = 'p`.tail in
delete 'p;
's`.data := 'p2;
let'n = 's`.size inlet'n2 = 'n - 1in
's`.size := 'n2;
'x }>.(** **** Exercise: 4 stars, standard, especially useful (triple_pop) Prove the following specification for the [pop] operation. Hint: You'll need to unfold the definition of [Stack] as in the proofs above, and at some point you'll want to destruct [L]. *)
forall (L : list val) s,
L <> nil ->
SPEC pop s
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ funx =>
β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬ PointsTo β s β β¦ $Stack β L' β§ β β β *β
forall (L : list val) s,
L <> nil ->
SPEC pop s
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ funx =>
β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β
β β¬ PointsTo β s β β¦ $Stack β L' β§ β β β *β
Admitted.(** [] *)(** The [top] operation extracts the element at the head of the list. *)Definitiontop : val :=
<{ fun's =>
let'p = 's`.data in
'p`.head }>.(** **** Exercise: 2 stars, standard, optional (triple_top) Prove the following specification for the [top] operation. *)
forall (L : list val) s,
L <> nil ->
SPEC top s
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ funx =>
β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β β β¬ PointsTo β s β β¦ $Stack β L β§ β
β β *β
forall (L : list val) s,
L <> nil ->
SPEC top s
β¬* PRE @ β¬ PointsTo β s β β¦ $Stack β L β§ β *β
β¬* POST @ funx =>
β¬
Exist β L'
β β¬
Star β β¬ Pure β L = x :: L' β β β¬ PointsTo β s β β¦ $Stack β L β§ β
β β *β
Admitted.(** [] *)EndSizedStack.(* ================================================================= *)(** ** Formalization of the Tree Representation Predicate [MTree] *)(** In this section, we generalize the ideas presented for linked lists to binary trees that store integer values in their nodes. Just as mutable lists are specified with respect to Rocq's purely functional lists, mutable binary trees are specified with respect to Rocq trees. Consider the following inductive definition of the type [tree]. A leaf is represented by the constructor [Leaf], and a node takes the form [Node n T1 T2], where [n] is an integer and [T1] and [T2] are its two subtrees. *)Inductivetree : Type :=
| Leaf : tree
| Node : int -> tree -> tree -> tree.Implicit TypesT : tree.(** In a program manipulating a mutable tree, an empty tree is represented using the [null] pointer, and a node is represented in memory using a three-cell record. The first field, "item", stores an integer. The other two fields, "left" and "right", store pointers to the left and right subtrees, respectively. *)Definitionitem : field := 0%nat.Definitionleft : field := 1%nat.Definitionright : field := 2%nat.(** The heap predicate [p ~~~>`{ item := n; left := p1; right := p2 }] describes a record allocated at location [p], storing the integer [n] and the two pointers [p1] and [p2]. The representation predicate [MTree T p], of type [hprop], asserts that the mutable tree structure with root at location [p] describes the logical tree [T]. The predicate is defined recursively on the structure of [T]. - If [T] is a [Leaf], then [p] is the null pointer. - If [T] is a node [Node n T1 T2], then [p] is not null and at location [p] one finds a record with field contents [n], [p1] and [p2], with [MTree T1 p1] and [MTree T2 p2] describing the two subtrees. *)FixpointMTree (T:tree) (p:loc) : hprop :=
match T with
| Leaf => \[p = null]
| Node n T1 T2 => \existsp1p2,
(p ~~~>`{ item := n; left := p1; right := p2 })
\* (MTree T1 p1)
\* (MTree T2 p2)
end.(** SEPVIZ add Notation here **)(* ================================================================= *)(** ** Alternative Characterization of [MTree] *)(** As for [MList], it is very useful for proofs to state three lemmas that paraphrase the definition of [MTree]. The first two lemmas correspond to the folding/unfolding rules for leaves and nodes. *)
forallp, MTree Leaf p = β¬ Pure β p = null β
forallp, MTree Leaf p = β¬ Pure β p = null β
auto.Qed.
forallpnT1T2,
MTree (Node n T1 T2) p =
β¬
Exist β p1
β β¬
Exist β p2
β β¬
Star β β¬
PointsTo β p β β¦ $Record3_val β item β n β left β p1 β right β p2
β§ β β β¬ Star β MTree T1 p1 β MTree T2 p2 β β β β
forallpnT1T2,
MTree (Node n T1 T2) p =
β¬
Exist β p1
β β¬
Exist β p2
β β¬
Star β β¬
PointsTo β p β β¦ $Record3_val β item β n β left β p1 β right β p2
β§ β β β¬ Star β MTree T1 p1 β MTree T2 p2 β β β β
auto.Qed.(** The third lemma reformulates [MTree T p] using a case analysis on whether [p] is the null pointer. This formulation matches the case analysis typically performed in the code of functions that operates on trees. *)
forallpT,
β¬* PRE @ MTree T p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β β¬ Pure β T = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β β¬
Exist β T2
β β¬
Star β
β¬ Pure β
T = Node n T1 T2 β
β β¬
Star β
β¬
PointsTo β p
β β¦
$Record3_val β item β n
β left β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β β *β
forallpT,
β¬* PRE @ MTree T p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β β¬ Pure β T = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β β¬
Exist β T2
β β¬
Star β
β¬ Pure β
T = Node n T1 T2 β
β β¬
Star β
β¬
PointsTo β p
β β¦
$Record3_val β item β n
β left β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β β *β
p: loc T: tree
β¬* PRE @ MTree T p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β β¬ Pure β T = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β β¬
Exist β T2
β β¬
Star β
β¬ Pure β
T = Node n T1 T2 β
β β¬
Star β
β¬
PointsTo β p
β β¦
$Record3_val β item β n
β left β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β β *β
p: loc
β¬* PRE @ MTree Leaf p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β
β¬ Pure β Leaf = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β β¬
Exist β T2
β β¬
Star β
β¬ Pure β
Leaf = Node n T1 T2 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β
β *β
p: loc n: int T1, T2: tree
β¬* PRE @ MTree (Node n T1 T2) p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null
β β
β¬ Pure β
Node n T1 T2 = Leaf β
β β¬
Exist β n0
β
β¬
Exist β p1
β
β¬
Exist β p2
β
β¬
Exist β T0
β
β¬
Exist β T3
β
β¬
Star β
β¬
Pure β
Node n T1 T2 =
Node n0 T0 T3 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n0 β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T0 p1 β
MTree T3 p2 β β β β β β β β
β *β
p: loc
β¬* PRE @ MTree Leaf p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β
β¬ Pure β Leaf = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β β¬
Exist β T2
β β¬
Star β
β¬ Pure β
Leaf = Node n T1 T2 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β
β *β
p: loc
p = null ->
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β
β¬ Pure β Leaf = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β
β¬
Exist β T2
β
β¬
Star β
β¬ Pure β
Leaf = Node n T1 T2 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β
β *β
p: loc M: p = null
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null β β
β¬ Pure β Leaf = Leaf β
β β¬
Exist β n
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T1
β
β¬
Exist β T2
β
β¬
Star β
β¬ Pure β
Leaf = Node n T1 T2 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T1 p1 β
MTree T2 p2 β β β β β β β β
β *β
p: loc M, C: p = null
β¬* PRE @ β¬ Opaque β emp β *β==>β¬* POST @ β¬ Pure β Leaf = Leaf β *β
xsimpl*.
p: loc n: int T1, T2: tree
β¬* PRE @ MTree (Node n T1 T2) p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null
β β
β¬ Pure β
Node n T1 T2 = Leaf β
β β¬
Exist β n0
β
β¬
Exist β p1
β
β¬
Exist β p2
β
β¬
Exist β T0
β
β¬
Exist β T3
β
β¬
Star β
β¬
Pure β
Node n T1 T2 =
Node n0 T0 T3 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n0 β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T0 p1 β
MTree T3 p2 β β β β β β β β
β *β
p: loc n: int T1, T2: tree
β¬* PRE @ MTree (Node n T1 T2) p *β==>β¬* POST @ β¬
IfThenElse β β¬ Pure β
p = null
β β
β¬ Pure β
Node n T1 T2 = Leaf β
β β¬
Exist β n0
β
β¬
Exist β p1
β
β¬
Exist β p2
β
β¬
Exist β T0
β
β¬
Exist β T3
β
β¬
Star β
β¬
Pure β
Node n T1 T2 =
Node n0 T0 T3 β
β
β¬
Star β
β¬
PointsTo β p
β β¦
$Record3 β item β n0 β left
β p1 β right β p2 β§ β
β
β¬
Star β
MTree T0 p1 β
MTree T3 p2 β β β β β β β β
β *β
p: loc n: int T1, T2: tree
forallxx0 : loc,
β¬* PRE @ β¬
Star β β¬
PointsTo β p β β¦ $Record3 β item β n β left β x β right β x0
β§ β β β¬ Star β MTree T1 x β MTree T2 x0 β β *β==>β¬* POST @
β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β Node n T1 T2 = Leaf β
β β¬
Exist β n0
β β¬
Exist β p1
β β¬
Exist β p2
β β¬
Exist β T0
β β¬
Exist β T3
β β¬
Star β β¬ Pure β Node n T1 T2 = Node n0 T0 T3 β
β β¬
Star β β¬
PointsTo β p
β β¦ $Record3 β item β n0 β left β p1 β right β p2 β§ β
β β¬ Star β MTree T0 p1 β MTree T3 p2 β β β β β β β β β *β
p: loc n: int T1, T2: tree p1, p2: loc
β¬* PRE @ β¬
Star β β¬
PointsTo β p β β¦ $Record3 β item β n β left β p1 β right β p2
β§ β β β¬ Star β MTree T1 p1 β MTree T2 p2 β β *β==>β¬* POST @
β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β Node n T1 T2 = Leaf β
β β¬
Exist β n0
β β¬
Exist β p0
β β¬
Exist β p3
β β¬
Exist β T0
β β¬
Exist β T3
β β¬
Star β β¬ Pure β Node n T1 T2 = Node n0 T0 T3 β
β β¬
Star β β¬
PointsTo β p
β β¦ $Record3 β item β n0 β left β p0 β right β p3 β§ β
β β¬ Star β MTree T0 p0 β MTree T3 p3 β β β β β β β β β *β
p: loc n: int T1, T2: tree p1, p2: loc
p <> null ->
β¬* PRE @ β¬
Star β β¬
PointsTo β p β β¦ $Record3 β item β n β left β p1 β right β p2
β§ β β β¬ Star β MTree T1 p1 β MTree T2 p2 β β *β==>β¬* POST @
β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β Node n T1 T2 = Leaf β
β β¬
Exist β n0
β β¬
Exist β p0
β β¬
Exist β p3
β β¬
Exist β T0
β β¬
Exist β T3
β β¬
Star β β¬ Pure β Node n T1 T2 = Node n0 T0 T3 β
β β¬
Star β β¬
PointsTo β p
β β¦ $Record3 β item β n0 β left β p0 β right β p3 β§ β
β β¬ Star β MTree T0 p0 β MTree T3 p3 β β β β β β β β β *β
p: loc n: int T1, T2: tree p1, p2: loc N: p <> null
β¬* PRE @ β¬
Star β β¬
PointsTo β p β β¦ $Record3 β item β n β left β p1 β right β p2
β§ β β β¬ Star β MTree T1 p1 β MTree T2 p2 β β *β==>β¬* POST @
β¬
IfThenElse β β¬ Pure β p = null β β β¬ Pure β Node n T1 T2 = Leaf β
β β¬
Exist β n0
β β¬
Exist β p0
β β¬
Exist β p3
β β¬
Exist β T0
β β¬
Exist β T3
β β¬
Star β β¬ Pure β Node n T1 T2 = Node n0 T0 T3 β
β β¬
Star β β¬
PointsTo β p
β β¦ $Record3 β item β n0 β left β p0 β right β p3 β§ β
β β¬ Star β MTree T0 p0 β MTree T3 p3 β β β β β β β β β *β
p: loc n: int T1, T2: tree p1, p2: loc N, C: p <> null
β¬* PRE @ β¬
Star β β¬
PointsTo β p β β¦ $Record3 β item β n β left β p1 β right β p2
β§ β β β¬ Star β MTree T1 p1 β MTree T2 p2 β β *β==>β¬* POST @
β¬
Exist β n0
β β¬
Exist β p0
β β¬
Exist β p3
β β¬
Exist β T0
β β¬
Exist β T3
β β¬
Star β β¬ Pure β Node n T1 T2 = Node n0 T0 T3 β
β β¬
Star β β¬
PointsTo β p
β β¦ $Record3 β item β n0 β left β p0 β right β p3 β§ β
β β¬ Star β MTree T0 p0 β MTree T3 p3 β β β β β β β β *β
xsimpl*.}Qed.(** Beyond this point, the definition of [Mtree] not longer needs to be unfolded: we make it opaque. *)Opaque MTree.(* ================================================================= *)(** ** Additional Tooling for [MTree] *)(** For reasoning about recursive functions over trees, it is useful to exploit the well-founded order associated with "immediate subtrees". Concretely, [tree_sub T1 T] asserts that the tree [T1] is either the left or the right subtree of the tree [T]. This order may be exploited for verifying recursive functions over trees using the tactic [induction_wf IH: tree_sub T]. The relation [tree_sub] is defined as follows. *)Inductivetree_sub : binary (tree) :=
| tree_sub_1 : forallnT1T2,
tree_sub T1 (Node n T1 T2)
| tree_sub_2 : forallnT1T2,
tree_sub T2 (Node n T1 T2).
wf tree_sub
wf tree_sub
T: tree
Acc tree_sub T
induction T; constructor; intros t' H; inversions~ H.Qed.#[global] Hint Resolve tree_sub_wf : wf.(** For allocating fresh tree nodes as a 3-field record, we introduce the operation [mnode n p1 p2], defined and specified as follows. *)Definitionmnode : val :=
val_new_hrecord_3 item leftright.(** A first specification of [mnode] describes the allocation of a record. *)
forallnp1p2,
SPEC mnode n p1 p2
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬
PointsTo β p
β β¦ $Record3_val β item β n β left β p1 β right β p2 β§
β *β
forallnp1p2,
SPEC mnode n p1 p2
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬
PointsTo β p
β β¦ $Record3_val β item β n β left β p1 β right β p2 β§
β *β
n: int p1, p2: loc
SPEC mnode n p1 p2
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funloc p => β¬
PointsTo β p
β β¦ $Record3_val β item β n β left β p1 β right β p2 β§
β *β
apply triple_new_hrecord_3; auto.Qed.(** A second specification, derived from the first, asserts that, when provided two subtrees [T1] and [T2] at locations [p1] and [p2], the operation [mnode n p1 p2] builds, at a fresh location [p], a tree described by [Mtree [Node n T1 T2] p]. Compared with the first specification, this second specification is said to "transfer ownership" of the two subtrees. *)(** **** Exercise: 2 stars, standard, optional (triple_mnode') Prove the specification [triple_mnode'] for node allocation. Because this specification refines the previous specification [triple_mnode], the proof should begin with [xapp triple_mnode]. *)
forallT1T2np1p2,
SPEC mnode n p1 p2
β¬* PRE @ β¬ Star β MTree T1 p1 β MTree T2 p2 β *β
β¬* POST @ funloc p => MTree (Node n T1 T2) p *β
forallT1T2np1p2,
SPEC mnode n p1 p2
β¬* PRE @ β¬ Star β MTree T1 p1 β MTree T2 p2 β *β
β¬* POST @ funloc p => MTree (Node n T1 T2) p *β
Admitted.#[global] Hint Resolve triple_mnode' : triple.(** [] *)(** Similarly to what we had done for [mnil], we could implement a function [mleaf] that returns the [null] pointer and derive a specification for it in terms of [MTree]. Instead, we will see through the next examples how one may directly reason about occurrences of [null] appearing in tree- manipulating code, using [xchange <- (MTree_Leaf null)] directly. *)(* ================================================================= *)(** ** Deep-Copy of a Tree *)(** The operation [tree_copy] takes as argument a pointer [p] on a mutable tree and returns a fresh copy of that tree. It is defined in a similar way to the function [mcopy] for lists.OCaml: let rec tree_copy p = if p = null then null else mnode p.item (tree_copy p.left) (tree_copy p.right)*)Definitiontree_copy :=
<{ fix 'f 'p =>
let'b = ('p = null) inif 'b then null else (
let'n = 'p`.item inlet'p1 = 'p`.leftinlet'p2 = 'p`.rightinlet'q1 = 'f 'p1 inlet'q2 = 'f 'p2 in
mnode 'n 'q1 'q2
) }>.(** **** Exercise: 3 stars, standard, especially useful (triple_tree_copy) Prove the specification of [tree_copy]. Hint: you'll need to use [xchange <- (MTree_Leaf null)] twice. *)
forallpT,
SPEC tree_copy p
β¬* PRE @ MTree T p *β
β¬* POST @ funloc q => β¬ Star β MTree T p β MTree T q β *β
forallpT,
SPEC tree_copy p
β¬* PRE @ MTree T p *β
β¬* POST @ funloc q => β¬ Star β MTree T p β MTree T q β *β
Admitted.(** [] *)(* ================================================================= *)(** ** Computing the Sum of the Items in a Tree *)(** The operation [mtreesum] takes as argument the location [p] of a mutable tree, and it returns the sum of all the integers stored in the nodes of that tree. The implementation traverses the tree, using an auxiliary reference cell to maintain the sum of all the items visited so far.OCaml: let rec treeacc c p = if p <> null then ( c := !c + p.item; treeacc c p.left; treeacc c p.right) let mtreesum p = let c = ref 0 in treeacc c p; !c*)Definitiontreeacc : val :=
<{ fix 'f 'c 'p =>
let'b = ('p <> null) inif 'b thenlet'm = ! 'c inlet'x = 'p`.item inlet'm2 = 'm + 'x in
'c := 'm2;
let'p1 = 'p`.leftin
'f 'c 'p1;
let'p2 = 'p`.rightin
'f 'c 'p2
end }>.Definitionmtreesum : val :=
<{ fun'p =>
let'c = ref 0in
treeacc 'c 'p;
get_and_free 'c }>.(** The specification of [mtreesum] is expressed in terms of the Rocq function [treesum], which computes the sum of the node items stored in a logical tree. This operation is defined by recursion over the tree. *)Fixpointtreesum (T:tree) : int :=
match T with
| Leaf => 0
| Node n T1 T2 => n + treesum T1 + treesum T2
end.(** **** Exercise: 4 stars, standard, optional (triple_mtreesum) Prove the correctness of the function [mtreesum]. Hint: to begin with, state and prove the specification lemma [triple_treeacc]. *)(* FILL IN HERE *)
forallTp,
SPEC mtreesum p
β¬* PRE @ MTree T p *β
β¬* POST @ funr : val => β¬ Star β β¬ Pure β r = treesum T β β MTree T p β *β
forallTp,
SPEC mtreesum p
β¬* PRE @ MTree T p *β
β¬* POST @ funr : val => β¬ Star β β¬ Pure β r = treesum T β β MTree T p β *β
Admitted.(** [] *)(* ================================================================= *)(** ** Verification of a Counter Function with Local State *)(** This section is concerned with the verification of counter functions, which feature internal, mutable state. A counter function [f] is a function that, each time it is called, returns the next integer. Concretely, the first call to [f()] returns [1], the second call returns [2], the third returns [3], and so on. A counter function can be implemented using a reference cell, [p], that stores the integer last returned by the counter. Initially, the contents of the cell is zero. Each time the counter function is called, the contents is increased by one unit, and the new value of the contents is returned to the caller. The function [create_counter] produces a fresh counter function. Concretely, [create_counter()] returns a counter function [f] that is independent from any other previously existing counter function.OCaml: let create_counter () = let p = ref 0 in (fun () -> (incr p; !p))*)Definitioncreate_counter : val :=
<{ fun'u =>
let'p = ref 0in
(fun_ 'u => (incr 'p; !'p)) }>.(** In this section, we present two specifications for counter functions. The first specification is the most direct, but it exposes the existence of the reference cell, revealing implementation details about the counter function. The second specification is more abstract, hiding from the client the internal representation of the counter using an abstract representation predicate. *)(** Let us begin with the simple, direct specification. The proposition [CounterSpec f p] asserts that [f] is a counter function [f] whose internal state is stored in a reference cell at location [p]. Thus, invoking [f] in a state [p ~~> m] updates the state to [p ~~> (m+1)] and produces the output value [m+1]. *)DefinitionCounterSpec (f:val) (p:loc) : Prop :=
forallm, triple (f ())
(p ~~> m)
(funr => \[r = m+1] \* p ~~> (m+1)).Implicit Typef : val.(** The function [create_counter] creates a fresh counter. Its precondition is empty. Its postcondition asserts that the function [f] being returned satisfies [CounterSpec f p] and the output state contains a cell [p ~~> 0] for some existentially quantified location [p]. *)
SPEC create_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funf =>
β¬
Exist β p
β β¬
Star β β¬ PointsTo β p β β¦ $hsingle β 0 β§ β
β β¬ Pure β CounterSpec f p β β β *β
(** Observe how the postcondition that appears in the triple above refers to [CounterSpec], which itself involves a triple. In other words, a triple appears nested inside another triple. In technical terms, we are leveraging the "impredicativity of triples", which holds as a consequence of the "impredicativity of predicates" in the logic of Rocq. *)(** The proof involves the use of a new tactic, called [xfun], for reasoning about local function definitions. Here, [xfun] gives us the hypothesis [Hf] that specifies the code of [f]. This hypothesis is of the form: [forall v H' Q', (PRE H' CODE B POST Q') -> triple (f v) H' Q'], where [B] denotes the code of the body of the function [f] instantiated on the argument [v], and where [H'] and [Q'] denote arbitrary pre- and postconditions. Exploiting this assumption enables the user to subsequently derive triples about the local function [f], by reasoning about the code of the [f], just in the same way as when the user invokes [xwp] for reasoning about a top-level function. *)
SPEC create_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funf =>
β¬
Exist β p
β β¬
Star β β¬ PointsTo β p β β¦ $hsingle β 0 β§ β
β β¬ Pure β CounterSpec f p β β β *β
β¬* PRE @ β¬ Opaque β emp β *β
CODE <[
Letv := `(App val_ref 0) in
`(Fun _ => `(Seq `(App incr v) ;
`(App val_get v)) ) ]>
β¬* POST @ funf =>
β¬
Exist β p
β β¬
Star β β¬ PointsTo β p β β¦ $hsingle β 0 β§ β
β β¬ Pure β CounterSpec f p β β β *β
forallx0 : loc,
β¬* PRE @ β¬ PointsTo β x0 β β¦ $hsingle β 0 β§ β *β
CODE <[ Fun _ => `(Seq `(App incr x0) ;
`(App val_get x0)) ]>
β¬* POST @ funf =>
β¬
Exist β p
β β¬
Star β β¬ PointsTo β p β β¦ $hsingle β 0 β§ β
β β¬ Pure β CounterSpec f p β β β *β
p: loc
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β 0 β§ β *β
CODE <[ Fun _ => `(Seq `(App incr p) ;
`(App val_get p)) ]>
β¬* POST @ funf =>
β¬
Exist β p0
β β¬
Star β β¬ PointsTo β p0 β β¦ $hsingle β 0 β§ β
β β¬ Pure β CounterSpec f p0 β β β *β
p: loc
forallvf : val,
(forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC vf vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β) ->
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β 0 β§ β *β==>β¬* POST @
β¬
Exist β p0
β β¬
Star β β¬ PointsTo β p0 β β¦ $hsingle β 0 β§ β β β¬ Pure β CounterSpec vf p0 β
β β *β
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β 0 β§ β *β==>β¬* POST @
β¬
Exist β p0
β β¬ Star β β¬ PointsTo β p0 β β¦ $hsingle β 0 β§ β β β¬ Pure β CounterSpec f p0 β
β β *β
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β
CounterSpec f p
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β
CounterSpec f p
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β m: int
SPEC f <{ () }>
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β m β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = m + 1 β
β β¬ PointsTo β p β β¦ $hsingle β m + 1 β§ β β *β
(* To reason about the call to the function [f], we can exploit [Hf], either explicitly by calling [apply Hf], or automatically by calling [xapp]. *)
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β m: int
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β m β§ β *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = m + 1 β
β β¬ PointsTo β p β β¦ $hsingle β m + 1 β§ β β *β
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β m: int
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β m + 1 β§ β *β
CODE <[ App val_get p ]>
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = m + 1 β
β β¬ PointsTo β p β β¦ $hsingle β m + 1 β§ β β *β
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β m: int
β¬* PRE @ β¬ PointsTo β p β β¦ $hsingle β m + 1 β§ β *β==>β¬* POST @
β¬ Star β β¬ Pure β m + 1 = m + 1 β β β¬ PointsTo β p β β¦ $hsingle β m + 1 β§ β β *β
p: loc f: val Hf: forall (vx : val) (H' : hprop) (Q' : val -> hprop),
(β¬* PRE @ H' *β
CODE <[ Seq `(App incr p) ;
`(App val_get p) ]>
β¬* POST @ Q' *β) -> SPEC f vx
β¬* PRE @ H' *β
β¬* POST @ Q' *β m: int
m + 1 = m + 1
auto.}Qed.(** Let us move on to the presentation of more abstract specifications. Their purpose is to hide from the client the existence of the reference cell used to represent the internal state of the counter functions. To that end, we introduce the heap predicate [IsCounter f n], which relates a function [f], its current value [n], and the piece of memory state involved in the implementation of this function. This piece of memory is of the form [p ~~> n], for some location [p], such that [CounterSpec f p] holds. *)DefinitionIsCounter (f:val) (n:int) : hprop :=
\existsp, p ~~> n \* \[CounterSpec f p].(** Using [IsCounter], we can reformulate the specification of [create_counter] with a postcondition asserting that the output function [f] is described by the heap predicate [IsCounter f 0]. *)
SPEC create_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funf => IsCounter f 0 *β
(** This lemma is the same as [triple_create_counter], except that the reference cell [p] is no longer explicitly mentioned in the postcondition. In other words, the address of the reference cell [p] used internally by the counter becomes hidden from the user. *)
SPEC create_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funf => IsCounter f 0 *β
SPEC create_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funf =>
β¬
Exist β p
β β¬
Star β β¬ PointsTo β p β β¦ $hsingle β 0 β§ β
β β¬ Pure β CounterSpec f p β β β *β
apply triple_create_counter.Qed.(** Next, we reformulate the specification of a call to a counter function. A call to [f()], in a state satisfying [IsCounter f n], produces a state satisfying [IsCounter f (n+1)], and returns [n+1]. *)(** **** Exercise: 4 stars, standard, especially useful (triple_apply_counter_abstract) Prove the abstract specification for a counter function. You will need to begin the proof using the tactic [xtriple], for turning goal into a form on which other x-tactics can be invoked. Then, use [xpull] to extract facts from the precondition. *)
forallfn,
SPEC f <{ () }>
β¬* PRE @ IsCounter f n *β
β¬* POST @ funr : val =>
β¬ Star β β¬ Pure β r = n + 1 β β IsCounter f (n + 1) β *β
forallfn,
SPEC f <{ () }>
β¬* PRE @ IsCounter f n *β
β¬* POST @ funr : val =>
β¬ Star β β¬ Pure β r = n + 1 β β IsCounter f (n + 1) β *β
Admitted.(** [] *)Opaque IsCounter.(** Finally, let us illustrate how a client might work with counter functions.OCaml: let test_counter () = let c1 = create_counter () in let c2 = create_counter () in let n1 = c1() in let n2 = c2() in let n3 = c1() in n2 + n3*)Definitiontest_counter : val :=
<{ fun'u =>
let'c1 = create_counter () inlet'c2 = create_counter () inlet'n1 = 'c1 () inlet'n2 = 'c2 () inlet'n3 = 'c1 () in
'n2 + 'n3 }>.(** **** Exercise: 2 stars, standard, optional (triple_test_counter) Prove the example function manipulating abstract counters. In the specification below, the heap predicate [\exists H, H] corresponds to the two counters, which we currently have no way of deallocating. The necessary mechanism for garbage collection will be introduced later in chapter [Affine]. Hint: use [xapp triple_create_counter_abstract] and [xapp triple_apply_counter_abstract] to invoke the specifications. *)
SPEC test_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Star β β¬ Pure β r = 3 β β β¬ Exist β H β H β β *β
SPEC test_counter <{ () }>
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Star β β¬ Pure β r = 3 β β β¬ Exist β H β H β β *β
Admitted.(** [] *)(* ################################################################# *)(** * Optional Material *)(** The verification of higher-order functions, and of their interaction with mutable state, was a formidable challenge prior to the introduction of Separation Logic. The rest of this chapter illustrates how to reason about imperative, higher-order programs. *)(* ================================================================= *)(** ** Specification of a Higher-Order Repeat Operator *)(** Consider the higher-order iterator [repeat]: a call to [repeat f n] executes [n] times the call [f()].OCaml: let rec repeat f n = if n > 0 then (f(); repeat f (n-1))*)Definitionrepeat : val :=
<{ fix 'g 'f 'n =>
let'b = ('n > 0) inif 'b then
'f ();
let'n2 = 'n - 1in
'g 'f 'n2
end }>.(** For simplicity, let us assume for now [n >= 0]. The specification of [repeat n f] can be expressed in terms of an invariant, named [I], describing the state in between every two calls to [f]. We assume that the initial state satisfies [I 0]. Moreover, we assume that, for every index [i] in the range from [0] (inclusive) to [n] (exclusive), a call [f()] can execute in a state that satisfies [I i] and produce a state that satisfies [I (i+1)]. The specification below asserts that, under these two assumptions, after the [n] calls to [f()], the final state satisfies [I n]. The specification takes the form: n >= 0 -> Hypothesis_on_f -> triple (repeat f n) (I 0) (fun u => I n) where [Hypothesis_on_f] is a proposition that captures the following specification: forall i, 0 <= i < n -> triple (f ()) (I i) (fun u => I (i+1)) The complete specification of [repeat n f] is thus as shown below. *)
forall (I : int -> hprop) fn,
n >= 0 ->
(foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β) ->
SPEC repeat f n
β¬* PRE @ I 0 *β
β¬* POST @ fun_ : val => I n *β
forall (I : int -> hprop) fn,
n >= 0 ->
(foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β) ->
SPEC repeat f n
β¬* PRE @ I 0 *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β
SPEC repeat f n
β¬* PRE @ I 0 *β
β¬* POST @ fun_ : val => I n *β
(** To establish this specification, we carry out a proof by induction over a generalized specification, covering the case where there remains [m] iterations to perform, for any value of [m] between [0] and [n] inclusive. forall m, 0 <= m <= n -> triple (repeat f m) (I (n-m)) (fun u => I n)) We use the TLC tactics [cuts], a variant of [cut], to state show that the generalized specification entails the statement of [triple_repeat]. *)
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
SPEC repeat f n
β¬* PRE @ I 0 *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β
forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
SPEC repeat f n
β¬* PRE @ I 0 *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
SPEC repeat f n
β¬* PRE @ I (n - n) *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
n - n = 0
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
SPEC repeat f n
β¬* PRE @ I (n - n) *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
0 <= n <= n
math.
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
n - n = 0
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β G: forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
n - n = 0
math.}
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β
forallm,
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
(** We then carry a proof by induction: during the execution, the value of [m] decreases step by step down to [0]. *)
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β
0 <= m <= n ->
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n
SPEC repeat f m
β¬* PRE @ I (n - m) *β
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n
β¬* PRE @ I (n - m) *β
CODE <[
Letv := `(App val_gt m 0) in
`(If_ v Then
`(Seq `(App f <{ () }>) ;
`(Letv0 := `(App val_sub m 1) in
`(App repeat f v0)))
Else
`(Val ())) ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n
β¬* PRE @ I (n - m) *β
CODE <[
If_ (isTrue (m > 0)) Then
`(Seq `(App f <{ () }>) ;
`(Letv := `(App val_sub m 1) in
`(App repeat f v)))
Else
`(Val ()) ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m) *β
CODE <[
Seq `(App f <{ () }>) ;
`(Letv := `(App val_sub m 1) in
`(App repeat f v)) ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: ~ m > 0
β¬* PRE @ I (n - m) *β
CODE <[ Val () ]>
β¬* POST @ fun_ : val => I n *β
(** We reason about the call to [f] *)
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m) *β
CODE <[
Seq `(App f <{ () }>) ;
`(Letv := `(App val_sub m 1) in
`(App repeat f v)) ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
0 <= n - m < n
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m + 1) *β
CODE <[ Letv := `(App val_sub m 1) in
`(App repeat f v) ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
0 <= n - m < n
math.
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m + 1) *β
CODE <[ Letv := `(App val_sub m 1) in
`(App repeat f v) ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m + 1) *β
CODE <[ App repeat f (m - 1) ]>
β¬* POST @ fun_ : val => I n *β
(** We next reason about the recursive call. *)
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
downto 0 (m - 1) m
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
0 <= m - 1 <= n
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m + 1) *β==>β¬* POST @ β¬
Star β I (n - (m - 1))
β (fun_ : val => I n) \--*
(fun_ : val => I n) β *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
downto 0 (m - 1) m
math.
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
0 <= m - 1 <= n
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m + 1) *β==>β¬* POST @ β¬
Star β I (n - (m - 1))
β (fun_ : val => I n) \--*
(fun_ : val => I n) β *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
0 <= m - 1 <= n
math.
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - m + 1) *β==>β¬* POST @ β¬
Star β I (n - (m - 1))
β (fun_ : val => I n) \--*
(fun_ : val => I n) β *β
(** We need to exploit an arithmetic equality. We do so using [math_rewrite], which is a convenient TLC tactic to assert an equality proved by the [math] tactic, then immediately invoke [rewrite] with this equality. *)
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: m > 0
β¬* PRE @ I (n - (m - 1)) *β==>β¬* POST @ β¬
Star β I (n - (m - 1))
β (fun_ : val => I n) \--*
(fun_ : val => I n) β *β
xsimpl.
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: ~ m > 0
β¬* PRE @ I (n - m) *β
CODE <[ Val () ]>
β¬* POST @ fun_ : val => I n *β
(** Finally, when [m] reaches zero, we check that we obtain [I n]. *)
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: ~ m > 0
β¬* PRE @ I (n - m) *β
CODE <[ Val () ]>
β¬* POST @ fun_ : val => I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: ~ m > 0
β¬* PRE @ I (n - m) *β==>β¬* POST @ I n *β
I: int -> hprop f: val n: int Hn: n >= 0 Hf: foralli : int,
0 <= i < n ->
SPEC f <{ () }>
β¬* PRE @ I i *β
β¬* POST @ fun_ : val => I (i + 1) *β m: int IH: forally : int,
downto 0 y m ->
0 <= y <= n ->
SPEC repeat f y
β¬* PRE @ I (n - y) *β
β¬* POST @ fun_ : val => I n *β Hm: 0 <= m <= n C: ~ m > 0
β¬* PRE @ I n *β==>β¬* POST @ I n *β
xsimpl.}Qed.(* ================================================================= *)(** ** Specification of an Iterator on Mutable Lists *)(** The operation [miter] takes as argument a function [f] and a pointer [p] on a mutable list and invokes [f] on each of the items stored in that list.OCaml: let rec miter f p = if p <> null then (f p.head; miter f p.tail)*)Definitionmiter : val :=
<{ fix 'g 'f 'p =>
let'b = ('p <> null) inif 'b thenlet'x = 'p`.head in
'f 'x;
let'q = 'p`.tail in
'g 'f 'q
end }>.(** The specification of [miter] follows the same structure as that of the function [repeat] from the previous section, with two main differences. The first difference is that the invariant is expressed not in terms of an index [i] ranging from [0] to [n], but in terms of a prefix of the list [L] being traversed. This prefix ranges from [nil] to the full list [L]. The second difference is that the operation [miter f p] requires in its precondition, in addition to [I nil], the description of the mutable list [MList L p]. This predicate is returned in the postcondition, unchanged, reflecting the fact that the iteration process does not alter the contents of the list. *)(** **** Exercise: 5 stars, standard, especially useful (triple_miter) Prove the correctness of [triple_miter]. *)
forall (I : list val -> hprop) (L : list val) fp,
(forallx (L1L2 : list val),
L = L1 ++ x :: L2 ->
SPEC f x
β¬* PRE @ I L1 *β
β¬* POST @ fun_ : val => I (L1 & x) *β) ->
SPEC miter f p
β¬* PRE @ β¬ Star β β¬ PointsTo β p β β¦ $MList β L β§ β β I nil β *β
β¬* POST @ fun_ : val => β¬ Star β β¬ PointsTo β p β β¦ $MList β L β§ β β I L β *β
forall (I : list val -> hprop) (L : list val) fp,
(forallx (L1L2 : list val),
L = L1 ++ x :: L2 ->
SPEC f x
β¬* PRE @ I L1 *β
β¬* POST @ fun_ : val => I (L1 & x) *β) ->
SPEC miter f p
β¬* PRE @ β¬ Star β β¬ PointsTo β p β β¦ $MList β L β§ β β I nil β *β
β¬* POST @ fun_ : val => β¬ Star β β¬ PointsTo β p β β¦ $MList β L β§ β β I L β *β
Admitted.(** [] *)(** For exploiting the specification [triple_miter] to reason about a call to [miter], it is necessary to provide an invariant [I] of type [list val -> hprop], that is, of the form [fun (K:list val) => ...]. This invariant, which generally cannot be inferred automatically, should describe the state at the point where the iteration has traversed a prefix [K] of the list [L]. Concretely, for reasoning about a call to [miter], one should exploit the tactic [xapp (triple_miter (fun K => ...))]. An example appears next. *)(* ================================================================= *)(** ** Computing the Length of a Mutable List using an Iterator *)(** The function [mlength_using_miter] computes the length of a mutable list by iterating over that list a function that increments a reference cell once for every item.OCaml: let mlength_using_miter p = let c = ref 0 in miter (fun x -> incr c) p; !c*)(** **** Exercise: 4 stars, standard, especially useful (triple_mlength_using_miter) Prove the correctness of [mlength_using_iter]. Hint: as explained earlier, use [xfun; intros f Hf] for reasoning about the function definition, then use [xapp] for reasoning about a call to [f]. Use of [xlet] is optional. *)Definitionmlength_using_miter : val :=
<{ fun'p =>
let'c = ref 0inlet'f = (fun_ 'x => incr 'c) in
miter 'f 'p;
get_and_free 'c }>.
forallp (L : list val),
SPEC mlength_using_miter p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β p β β¦ $MList β L β§ β
β *β
forallp (L : list val),
SPEC mlength_using_miter p
β¬* PRE @ β¬ PointsTo β p β β¦ $MList β L β§ β *β
β¬* POST @ funr : val =>
β¬
Star β β¬ Pure β r = length L β β β¬ PointsTo β p β β¦ $MList β L β§ β
β *β
Admitted.(** [] *)(** ** Factorial Function in Continuation-Passing Style*)(** This section and the next one present examples of functions involving "continuations". As a warm-up, we first consider consider a function that does not involve any mutable state. The function [cps_facto_aux n k] performs a call to the function [k] on the value [facto n]. The function [cps_facto n] computes the value of [facto n] by applying [cps_facto_aux] to the identity function.OCaml: let rec cps_facto_aux n k = if n <= 1 then k 1 else cps_facto_aux (n-1) (fun r -> k (n * r)) let cps_facto n = cps_facto_aux n (fun r -> r)*)Definitioncps_facto_aux : val :=
<{ fix 'f 'n 'k =>
let'b = 'n <= 1inif 'b
then 'k 1elselet'k2 = (fun_ 'r => let'r2 = 'n * 'r in 'k 'r2) inlet'n2 = 'n - 1in
'f 'n2 'k2 }>.Definitioncps_facto : val :=
<{ fun'n =>
let'k = (fun_ 'r => 'r) in
cps_facto_aux 'n 'k }>.Import Facto.(** **** Exercise: 4 stars, standard, optional (triple_cps_facto_aux) Verify [cps_facto_aux]. Hints: To set up the induction, use the usual pattern [induction_wf IH: (downto 0) n]. To reason about the function definition, use [xfun]. To reason about the recursive call, you'll need to exploit the induction hypothesis, called [IH], which universally quantifies over a function [F] of type [int->int]. To do so, use the syntax [xapp (>> IH (fun a => ..))]. The function provided should describe the behavior of the continuation [k2] that appears in the code. For the mathematical reasoning, use the same pattern as in the proof of [factorec], applying the tactics [rewrite facto_init] and [rewrite (@facto_step n)]. *)
foralln (k : val) (F : int -> int),
n >= 0 ->
(foralla : int,
SPEC k a
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Pure β r = F a β *β) ->
SPEC cps_facto_aux n k
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Pure β r = F (facto n) β *β
foralln (k : val) (F : int -> int),
n >= 0 ->
(foralla : int,
SPEC k a
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Pure β r = F a β *β) ->
SPEC cps_facto_aux n k
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Pure β r = F (facto n) β *β
Admitted.(** [] *)(** **** Exercise: 2 stars, standard, optional (triple_cps_facto) Verify [cps_facto]. Hint: use the syntax [xapp (>> triple_cps_append_aux F)] to provide the function [F] that describes the behavior of the identity continuation. *)
foralln,
n >= 0 ->
SPEC cps_facto n
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Pure β r = facto n β *β
foralln,
n >= 0 ->
SPEC cps_facto n
β¬* PRE @ β¬ Opaque β emp β *β
β¬* POST @ funr : val => β¬ Pure β r = facto n β *β
Admitted.(** [] *)(* ================================================================= *)(** ** In-Place Concatenation Function in Continuation-Passing Style *)(** This section presents an example verification of a function involving "continuations". The function [cps_append] is similar to the function [append] presented previously: it also performs in-place concatenation of two lists. The main difference is that it is implemented using an auxiliary recursive function in "continuation-passing style" (CPS). The presentation of [cps_append p1 p2] is also slightly different: this operation returns a pointer [p3] that describes the head of the result of the concatenation. In the general case, [p3] is equal to [p1], but if [p1] is the null pointer, meaning that the first list is empty, then [p3] is equal to [p2]. The code of [cps_append] involves the auxiliary function [cps_append_aux p1 p2 k], which invokes the continuation function [k] on the result of concatenating the lists at locations [p1] and [p2]. Its code appears at first quite puzzling, because the recursive call is performed inside the continuation. It takes a good drawing and at least a couple minutes to figure out how the function works.OCaml: let rec cps_append_aux p1 p2 k = if p1 == null then k p2 else cps_append_aux p1.tail p2 (fun r => (p1.tail <- r); k p1) let cps_append p1 p2 = cps_append_aux p1 p2 (fun r => r)*)Definitioncps_append_aux : val :=
<{ fix 'f 'p1 'p2 'k =>
let'b = ('p1 = null) inif 'b
then 'k 'p2
elselet'q1 = 'p1`.tail inlet'k2 = (fun_ 'r => ('p1`.tail := 'r; 'k 'p1)) in
'f 'q1 'p2 'k2 }>.Definitioncps_append : val :=
<{ fun'p1'p2 =>
let'f = (fun_ 'r => 'r) in
cps_append_aux 'p1 'p2 'f }>.(** The goal is to establish the following specification for [cps_append]. Lemma triple_cps_append : forall (L1 L2:list val) (p1 p2:loc), triple (cps_append p1 p2) (MList L1 p1 \* MList L2 p2) (funloc p3 => MList (L1++L2) p3). If you are interested in the challenge of solving a 6-star exercise, then try to prove the above specification without reading any further. If, however, you are only looking for a 5-star exercise, keep reading. *)(** **** Exercise: 5 stars, standard, optional (triple_cps_append_aux) The specification of [cps_append_aux] involves an hypothesis describing the behavior of the continuation [k]. For this function, and more generally for code in CPS form, we cannot easily leverage the frame property, thus we need to quantify explicitly over a heap predicate [H] for describing the "rest of the state". Prove that specification. Hint: you can use the syntax [xapp (>> IH H')] to instantiate the induction hypothesis [IH] on a specific heap predicate [H']. *)
forall (H : hprop) (Q : val -> hprop) (L1L2 : list val) p1p2 (k : val),
(forallp3,
SPEC k p3
β¬* PRE @ β¬ Star β β¬ PointsTo β p3 β β¦ $MList β L1 ++ L2 β§ β β H β *β
β¬* POST @ Q *β) ->
SPEC cps_append_aux p1 p2 k
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β L1 β§ β
β β¬ Star β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β H β β *β
β¬* POST @ Q *β
forall (H : hprop) (Q : val -> hprop) (L1L2 : list val) p1p2 (k : val),
(forallp3,
SPEC k p3
β¬* PRE @ β¬ Star β β¬ PointsTo β p3 β β¦ $MList β L1 ++ L2 β§ β β H β *β
β¬* POST @ Q *β) ->
SPEC cps_append_aux p1 p2 k
β¬* PRE @ β¬
Star β β¬ PointsTo β p1 β β¦ $MList β L1 β§ β
β β¬ Star β β¬ PointsTo β p2 β β¦ $MList β L2 β§ β β H β β *β
β¬* POST @ Q *β
Admitted.(** [] *)(** **** Exercise: 3 stars, standard, optional (triple_cps_append) Verify [cps_append]. Hint: use the syntax [@triple_cps_append_aux H' Q'] to specialize the specification of the auxiliary function to specific pre- and post-conditions. *)