CFML Documentation

Using CFML

Installation of CFML

The following steps explain how to create a folder named "cfml" that contains a working version of CFML and next to it a folder named "tlc" that contains the required TLC library.

Step 1 Checkout the source code of CFML:

   svn checkout svn://scm.gforge.inria.fr/svn/cfml/branches/v1 cfml 

Step 2 Checkout the source code of the Coq library TLC:

   svn checkout svn://scm.gforge.inria.fr/svn/tlc/branches/v3 tlc

Step 3 Install Coq version v8.3pl1. (If you have a "local" installation, you will later need to adjust the file "local_settings.sh" which will be created by the Makefile.)

Step 4 Install OCaml, any version >= 10.1 should work. (If you have a "local" installation, you will also need to adjust "local_settings.sh".)

You should now be ready to compile the files, as explained next.

Compilation of the developments

Step 0 Enter the "cfml" directory:

   cd cfml

Step 1 Generate the "local_settings.sh" file:

   make init
The file "local_settings.sh" contains the path to the binary for Coq and OCaml, and the path to the TLC library. You do not need to edit the file "local_settings.sh" if you used the default installation paths. Note that "make clean" does not remove this file (only "make clean_all" does).

Step 2 Generate the dependencies:

   make depend
Note that this procedure needs to be done again whenever you edit the Makefile to add a new development.

Step 3 Generate characteristic formulae and compile all scripts:

   make 

The Makefile supports the following other commands.

Interactive verification proofs

To open a development file using CoqIDE, open a terminal to the "cfml" folder and the run the script "go.sh" as follows:

   ./go.sh user/Demo_proof.v

If you want to use another IDE or launch CoqIDE yourself, you need to make sure that you include the path to the CFML library, the path to the TLC library, and the path to the folder containing your source code (needed when you have dependencies between your Caml files). For example:

   coqide -I . -I lib/v3 -I user user/Demo_proof.v

Organization of the files

The Makefile supports the following other commands.

The following file extensions are used.

The following dependencies are enforced by the Makefile.

Structure of a proof file

A verification file (%_proof.ml) typically contains the following section.

  1. Coq general-purpose declarations, for example:
       Set Implicit Arguments.
       Generalizable Variables a A.
    
  2. Include of the CFML and TLC libraries, and include of the %_ml.v file, for example:
       Require Import CFLib LibArray Demo_ml.
    
  3. Auxiliary definitions used to state the invariants,
  4. Statement of the invariants,
  5. Setting up of automation, for example:
       Ltac auto_tilde ::= try solve [ auto ]. 
       Ltac auto_star ::= try solve [ auto | jauto ]. 
       Hint Extern 1 (_ \in _ \u _) => multiset_in.
    
  6. Statement and proof of mathematical lemmas used in the verification proofs (those lemmas typically do not refer at all to characteristic formulae),
  7. Specification and verification of the code, which includes for each top-level definition:
    1. Statement of the specification theorem, for example:
         Lemma fst_spec: 
           Spec fst p |R>> let '(x,y) := p in R [] (fun r => [r = x])
      
    2. Interactive proof based on the characteristic formula, for example:
         Proof.
           xcf. intros [x y]. xgo*.
         Qed.
      
    3. Registration of the specification in a database, using the following syntax:
         Hint Extern 1 (RegisterSpec fst) => Provide fst_spec.
      

Notation used in CFML

Notation for characteristic formulae

The following syntax is used for pretty-printing characteristic formulae.
   Ret v                           return a value
   App f v1 .. vN ;                application
   F1 ;; F2                        sequence
   LetVal x := v in F              let-binding of a value
   Let x := F1 in F2               let-binding of a term
   LetFun f x1 .. xN := F1 in F2   let-binding of a function
   _If v Then F1 Else F2           conditional on a value
   If_ F0 Then F1 Else F2          conditional on a term
   While F1 Do F2 Done             while-loop
   For i = v1 to v2 Do F Done      for-loop
Pattern-matching is decomposed in the series of nested "case" constructs, which can be viewed as pattern-matching with only two branches: either the term matches a given pattern, or it does not. For technical reasons, the list of free variables occuring in a pattern needs to be mentioned explicitly, so it appears enclosed in brackets in the formula.
   Match n F                               pattern-matching with n branches
   Case v = p [x1 .. xN] Then F1 Else F2   case of a pattern
   Fail                                    remaining of an incomplete pattern matching
   Done                                    remaining of an complete pattern matching 
   Alias x := v in F                       aliases occuring in patterns

Mutually recursive is supported using the auxiliary "Body" notation.

   Body f x1 .. xN => Q                    body of one function
   LetFuns f1 := H1 and .. and fN := HN    mutually-recursive functions

Moreover, notation for let-bindings and functions is generalized to support polymorphism.

   Body f [A1 .. AM] x1 .. xN => Q
   LetFun f [A1 .. AM] x1 .. xN := F1 in F2  

Note that not all arities are supported, so if a formula is not displayed nicely then the file "CFPrim.v" needs to be extended to support the arity that is being used.

Notation for pre- and post-conditions

The grammar of heap description is as follows.

   H ::= []                     empty heap  
         [P]                    fact, where [P:Prop]
         H1 \* H2               disjoint union of two heaps
         Hexists x1 .. xn, H    existential quantification
         Hexists (x1:T1) .., H  typed existential quantification
         r ~> T V               heap described by a representation predicate
         r ~> Id V              equivalent to ([r = V])
         r ~~> v                shorthand for (r ~> Ref V)
         r ~>> V                shorthand for (r ~> Channel V)

The grammar of post-conditions is as follows.

   Q ::= (fun x => H)           general form of a post-condition
         (fun x:T => H)         general form of a type-annotated post-condition
         # H                    shorthand for (fun _:unit => H)
         \[ M ]                 shorthand for (fun x => [M x])
         \= V                   shorthand for (fun x => [x = V]), same as (\[=V])
         Q \*+ H                post-condition extended with a heap predicate

Note that to extend a post-condition with two heap predicates, one may write either Q \*+ H1 \*+ H2 or Q \*+ (H1 \* H2), but not Q \*+ H1 \* H2, which would fail to typecheck.

The entailment relations are as follows.

   H1 ==> H2       heap description H1 implies heap description H2
   Q1 ===> Q2      post-condition Q1 implies post-condition Q2

Notation for specifications

The "Spec" notation is used to specify a curried function.

   Spec f x1 .. xN |R>> H
   Spec f (x1:T1) .. (xN:TN) |R>> H
The notation "Spec" intuitively corresponds to the specification of the behavior of an application ("App"). For example:
   Spec incr x |R>> forall n, R (x ~> n) (x ~> n+1)
is the same as
   forall x, forall n, App incr x (x ~> n) (x ~> n+1).

Due to a limitation of Coq, it is not possible to decompose tupled arguments directly in the specification. For example, one cannot write:

   Spec fst (x,y) |R>> R [] (fun r => [r = x]).

Instead, one needs to be specified as a function of one argument that is latter decomposed as a pair. For example:

   Spec fst p |R>> let '(x,y) := p in R [] (fun r => [r = x])

Tactics for proving heap implications

This section describes the tactics that can be used to prove goals of the form H1 ==> H2 or of the form Q1 ===> Q2.

Extracting, cancelling and rewriting in heap descriptions

hextract

[hextract] takes all the facts and all the existential quantifiers out of the predicondition [H1], and place them in the context. In order to control the names produced, one should call [hextract as]. In this case, the new hypotheses are produced in the context, and they can be named using [intros] or [introv]. To name directly the results, you can simply call [hextract as X Y Z] . The items of the form [x ~> Id X] are treated in a special way, in the sense that they are systematically replaced by [x = X].

hcancel

[hcancel] cancels out matching pieces of heap in [H1] and [H2]. If [H1] has an item of the form [x ~> T1 V1] and [H2] has an item of the form [x ~> T2 V2], then it generates the equality [T1 V1 = T2 V2], or even directly [T1 = T2] and [V1 = V2]. If the equalities are not provable, it is likely that the tactic [hchange] needed to be called beforehand. The items of the form [x ~> Id X] are treated in a special way, in the sense that they are systematically replaced by [x = X].

If after doing all simplifications the remaining goal is of the form [H3 ==> ?H4], where [?H4] is a Coq unification variable, then [H4] is set to be equal to [H3]. This unification will be rejected by Coq in the case [H3] contains variables that did not exist at the time [?H4] was created. This usually happens either because an earlier call to [hextract] was made later than it should have been, or because an [hchange (unfocus ..)] needs to be done just before reaching this point in the proof.

[hcancel (>> V1 V2 .. VN)] is a syntax that allows to provide hints to instantiate the existential quantifiers in [H2]. The hints need to be given in the same order as the quantifiers, however some of the quantifiers may not be given hints. (Use a double underscore to denote that you want to skip a quantifier in the case where you want to provide a hint for the next quantifier and that this next quantifier quantifies a variable of the same type as the current one.)

hsimpl

[hsimpl] calls [hextract] and [hcancel]. The syntax [hsimpl (>> V1 V2 ... VN)], or simply [hsimpl V1 .. VN] can be used when providing hints for existentials is required. In general, for the sake of making proofs more robust, it is recommanded to call [hsimpl] even when [hcancel] is sufficient.

hchange

[hchange E], where [E] has type [H3 ==> H4] applies to a goal [H1 ==> H2]. It finds a subset of [H1] that corresponds to [H3] and then replace this subset with [H4].

[hchange -> E] or [hchange <- E] can be used to when [E] has for conclusion an equality of the form [H3 = H4], that should be used as [H3 ==> H4] or [H4 ==> H3].

[hchange_debug] is similar to [hchange] except that it does not perform any simplification automatically. In theory, this tactic should not be required.

[hchanges] is similar to [hchange] except that it tries to make many simplifications automatically. It is a useful shorthand when it works, but [hchanges] cannot be used when for example another step of [hchange] need to be performed, or when some existentials need to be instantiated explicitly.

Proving goals of the form Q1 ===> Q2

One should call intros r to turn a goal Q1 ===> Q2 into the goal Q1 r ==> Q2 r. Here, r denotes the name of the result (another name could be used).

Note that the tactics hextract and hsimpl also apply to a goal of the form Q1 ===> Q2. On such a goal, these tactics automatically run intros r. In the particular case where Q1 and Q2 have type unit, the goal is directly replaced with Q1 tt ==> Q2 tt.

Tactics for manipulating characteristic formulae

Reasoning on function definitions

xcf

[xcf] applies to a goal of the form [Spec f x1 .. xN |R>> ...], and exploits the characteristic formula for [f] to make progress.

[xcf_app] applies to a goal of the form [App f x1 .. xN H Q], and exploits the characteristic formula for [f] to make progress.

When [xcf] or [xcf_app] fails, it is usually because the types inferred for the source code do not match the types that appear in the specification of the function. To find out what when wrong, call [xfind] and set [Set Printing All], and check the types that appear right next to the [@spec] predicate.

xinduction

[xinduction E] can be used to establish a specification by induction, for a goal of the form [Spec f x1 .. xN |R>> ...]. Let [Ai] be the type of [xi]. Then, [E] should be one of

[xinduction_heap E] can be used in the case the termination argument depends on the state of the heap. In this case, the goal should be of the form [Spec f x1 .. xN |R>> forall x0, L x0 x1 xN R]. Let [Ai] be the type of [xi]. Then, [E] should be one of

Measures and binary relations can also be provided in a curried fashion, at type [A0 -> A1 -> .. -> AN -> nat] and [A0 -> A0 -> A1 -> A1 -> A2 -> A2 -> .. -> AN -> AN -> Prop], respectively. The combinators [unprojNK] are useful for building new binary relations. For example, if [R] has type [A->A->Prop], then [unproj21 B R] has type [A*B -> A*B -> Prop] and compares pairs with respect to their first component only, using [R].

xcf

[xintros] turns a goal of the form [Spec f x1 .. xN |R>> E] into the form [forall x1 .. xn, E]. It is useful to prove a function correct by induction on a predicate, because [xinduction] only works for induction on a well-founded relation.

[xintros_noauto] can be used to deactivate automatic discharging of side-conditions.

xweaken

[xweaken] can be used to prove a goal of the form [Spec f x1 .. xN |R>> ..] using a specification for [f] that has already been established and registered in the specification database.

[xweaken S] can be used to specify the original specification lemma [S].

[xweaken as x1 .. xN] can be used to name the arguments in the goal produced.

[xweaken S as x1 .. xN] combines the two above features.

[xweaken_noauto] is similar but deactivates treatement of side-conditions.

xfun

[xfun S] is used to handle a local function definition. [S] should be the specification for the function, which should be a predicate of type [Val -> Prop]. Thus, [S] typically takes the form [fun f => Spec f x1 .. xN |R>> ..].

[xfun_nointro S] can be used to avoid automatic introduction of arguments.

[xfun] without argument will automatically assign the function its most- general specification, which is the characteristic formula of its body.

[xfun S1 S2] can be used to specify a pair of mutually recursive functions.

[xfun_induction S E] combines [xfun S] with [xinduction E].

[xfun_induction_heap S E] combines [xfun S] with [xinduction_heap E].

Several combination of the above features are also available.

Reasoning on terms

xret

[xret] applies to a piece of code that simply returns a value,

[xrets] is like [xret] followed with [xsimpl],

[xret_noclean] calsl [xret] but without calling [xclean] automatically,

[xret_gc] produces a goal that allows pieces of heap to be discarded (calling [xret] instead of [xret_gc] might lead to unprovable goals).

xret

[xval] applies to a let-node that binds a value,

[xval as x] can be used to rename the bound variable,

[xval_st P] can be used to give an abstract specification to [x],

[xval_st P as x] combines the two,

[xvals] substitute the bound variable by its content right away.

xret

[xlet] applies to a let that binds a term,

[xlet as x] can be used to rename the bound variable,

[xlet Q] can be used to specify the post-condition,

[xlet Q as x] combines the two,

[xseq] applies to a sequence,

[xseq H] can be used to provide the intermediate state.

One rarely calls [xlet] because [xapp] can be called directly.

xapp

[xapp] applies to an application node, or a let-node with an application,

[xapp V1 .. VN] can be used to specify the ghost variables involved,

[xapp_spec E] can be used to provide the specification lemma,

[xapp_manual] can be used to deactivate automatic simplifications,

[xapp as x] can be used to change the name of the variable bound to the result of the application,

[xapps] is like [xapp] except that it substitutes the result in the continuation of the let-branch in which it is.

Many combination of the available options can also be used.

xif

[xif] applies to a if-then-else node, when the argument is a value. If the argument is a term, then [xlet] needs to be called first.

[xif H] can be used to specify the name of the hypothesis.

[xif_manual] deactivates post-processing of the goal.

[xif_manual H] combines the two above features.

[xifs] captures the typical proof pattern for conditionals on a simple boolean value. It applies [xif] and assume that [xextract] will produce a single equality, which then gets substituted in the goal.

Reasoning on pattern matching

xcase

[xcase_one] applies to a branch of a pattern-matching construct.

[xcase_one as H] can be used to name the hypothesis produced.

[xcases] is like [xcase_one] except that it does a little bit of post-processing.

[xcase] is like [xcase_one] except that it does a lot of post-processing, including an inversion on the equality produced by the pattern-matching.

xmatch

[xmatch] applies to a pattern-matching construct with several branches. By default, it introduces aliases as equalities.

[xmatch_keep_alias] can be used to preserve alias definitions.

[xmatch_subst_alias] can be used to directly substitute aliases.

[xmatch_simple] is like [xmatch] except that it deactivates post-processing.

Also available: [xmatch_simple_keep_alias] and [xmatch_simple_subst_alias].

[xmatch_nocases] does not analyse all the branches, but instead leaves the user do it manually using the tactic [xcase] described above.

xfail

[xfail] applies to a [assert false] goal (or the remainder of an incomplete pattern matching). It replaces the goal by [False].

xdone

[xdone] automatically proves the goal that corresponds to the remainder of a complete pattern matching.

xalias

[xalias] is used to introduce aliases, i.e. equalities arising from the ocurrence of the "as" keyword in a pattern.

[xalias as H] can be used to specify the name of the equality.

[xalias as x H] can be used to rename the variable bound by the alias-pattern and also to specify the name of the equality hypothesis.

[xalias_subst] can be used to directly substitutes the equality in the goal.

Reasoning on loops

xwhile

[xwhile] applies to a while loop that is to be proved without loop invariant but directly using the induction tactic.

[xwhile E] is similar excepts that the goal is the goal is first strengthened to [E].

[xwhile_inv W I B] can be used to prove a loop using a loop-invariant and a termiation measure, both based on a ghost variable of type [A].

xfor

[xfor] applies to a for loop that is to be proved without loop invariant but directly using the induction tactic.

[xfor E] is similar excepts that the goal is the goal is first strengthened to [E].

[xfor_inv I] can be used to prove a for-loop using a loop invariant. In this case, I should be of type [int->hprop].

[xfor_inv I as i] can be used to specify the name of the loop counter.

[xfor_inv I as i C] can be used to also specify the hypothesis providing the bounds for the loop counter.

xapply

[xapply E] is used to apply a lemma modulo weakening and framing. It is typically used to prove a goal of the form [R H' Q'] using an hypothesis of the form [R H Q]. Such hypotheses are typically generated by [xfor] and [xwhile].

[xapplys E] is similar except that it tries to automatically perform simplifications on the subgoal produced.

Manipulating pre- and post-conditions

xextract

[xextract] is like [hextract] excepts that it applies to the precondition of a goal of the form [F H Q].

[xextracts] extracts from the precondition an equality of the form [x = v] and substitutes [x] everywhere in the goal.

xframe

[xframe H] removes [H] from both the pre-condition and the post-condition.

[xframe - H] removes everything but [H] from the pre-condition and removes the same items from the post-condition.

xchange

[xchange E] is similar to [hchange E] except that it applies to the pre-condition of a goal of the form [F H Q].

[xchange -> E] and [xchange <- E] can be used when [E] is an equality.

[xchange E as V1 .. VN] can be used to name the hypotheses produced by [xextract].

[xchange_debug E] can be used to deactivate automatic simplifications.

xpost

[xpost] replaces a goal [F H Q] with [F H ?Q1] and [?Q1 ===> Q]. This is sometimes needed because a few tactics require the post-condition to be a Coq unification variable.

xgc

[xgc H] removes [H] from the pre-condition,

[xgc - H] removes everything but [H] from the pre-condition,

[xgc_all] removes everything from the pre-condition (it is similar to [xgc - []]).

[xgc] does not change the pre-condition but anticipates for the fact that some pieces of heap will be discarded from the post-condition ([xgc] typically avoids calling [xret_gc] later on).

Automation

xgo

[xgo] automatically applies the appropriate [x-tactic] repeatedly. It stops whenever it lacks information to go on.

xauto

[xauto~] and [xauto*] are the same as [auto~] and [auto*] excepts that they peform substitution more aggressively.

Other tactics

[xfind f] shows the specification registered for [f]. [xfind] show the specification associated with the function in the current goal.

[xcleanpat] removes from the context all the hypotheses that correspond to the negation of a pattern matching clause.

[xsimpl] is (roughly) a synonymous for [hsimpl].

[xlocal] solves a goal of the form [is_local F]

[xisspec] solves a goal of the form [is_spec K]

[xname_spec s] applies to a goal of the form [Spec_n f K] and defines [s] as the predicate [fun f => Spec_n f K].