├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── _CoqProject
├── coq-hoare-tut.opam
├── exgcd.v
├── hoarelogic.v
├── hoarelogicsemantics.v
├── meta.yml
├── partialhoarelogic.v
└── totalhoarelogic.v
/.gitignore:
--------------------------------------------------------------------------------
1 | .*.aux
2 | .*.d
3 | *.a
4 | *.cma
5 | *.cmi
6 | *.cmo
7 | *.cmx
8 | *.cmxa
9 | *.cmxs
10 | *.glob
11 | *.ml.d
12 | *.ml4.d
13 | *.mli.d
14 | *.mllib.d
15 | *.mlpack.d
16 | *.native
17 | *.o
18 | *.v.d
19 | *.vio
20 | *.vo
21 | *.vok
22 | *.vos
23 | .coq-native/
24 | .csdp.cache
25 | .lia.cache
26 | .nia.cache
27 | .nlia.cache
28 | .nra.cache
29 | csdp.cache
30 | lia.cache
31 | nia.cache
32 | nlia.cache
33 | nra.cache
34 |
35 | Makefile.coq
36 | Makefile.coq.conf
37 |
38 | html
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: Makefile.coq
2 | +make -f Makefile.coq all
3 |
4 | clean: Makefile.coq
5 | +make -f Makefile.coq clean
6 | rm -f Makefile.coq Makefile.coq.conf
7 |
8 | Makefile.coq:
9 | $(COQBIN)coq_makefile -f _CoqProject -o Makefile.coq
10 |
11 | %: Makefile.coq
12 | +make -f Makefile.coq $@
13 |
14 | .PHONY: all clean
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tutorial on Hoare Logic
2 |
3 | [![Contributing][contributing-shield]][contributing-link]
4 | [![Code of Conduct][conduct-shield]][conduct-link]
5 | [![Zulip][zulip-shield]][zulip-link]
6 |
7 |
8 | [contributing-shield]: https://img.shields.io/badge/contributions-welcome-%23f7931e.svg
9 | [contributing-link]: https://github.com/coq-community/manifesto/blob/master/CONTRIBUTING.md
10 |
11 | [conduct-shield]: https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-%23f15a24.svg
12 | [conduct-link]: https://github.com/coq-community/manifesto/blob/master/CODE_OF_CONDUCT.md
13 |
14 | [zulip-shield]: https://img.shields.io/badge/chat-on%20zulip-%23c1272d.svg
15 | [zulip-link]: https://coq.zulipchat.com/#narrow/stream/237663-coq-community-devs.20.26.20users
16 |
17 |
18 |
19 | Hoare logics are "program logics" suitable for reasoning about
20 | imperative programs.
21 | This work is both an introduction to Hoare logic and a demo
22 | illustrating Coq nice features. It formalizes the generation of PO
23 | (proof obligations) in a Hoare logic for a very basic imperative
24 | programming language. It proves the soundness and the completeness of
25 | the PO generation both in partial and total correctness. At last, it
26 | examplifies on a very simple example (a GCD computation) how the PO
27 | generation can simplify concrete proofs. Coq is indeed able to compute
28 | PO on concrete programs: we say here that the generation of proof
29 | obligations is reflected in Coq. Technically, the PO generation is
30 | here performed through Dijkstra's weakest-precondition calculus.
31 |
32 |
33 | ## Meta
34 |
35 | - Author(s):
36 | - Sylvain Boulmé (initial)
37 | - Coq-community maintainer(s):
38 | - Kartik Singhal ([**@k4rtik**](https://github.com/k4rtik))
39 | - License: [GNU Lesser General Public License v3.0 or later](LICENSE)
40 | - Compatible Coq versions: 8.10 or later
41 | - Additional dependencies: none
42 | - Coq namespace: `HoareTut`
43 | - Related publication(s): none
44 |
45 | ## Build instructions
46 |
47 | ``` shell
48 | git clone https://github.com/coq-community/hoare-tut
49 | cd hoare-tut
50 | make # or make -j
51 | make html # to build html documentation
52 | ```
53 |
54 |
55 | ## Aims of this library
56 |
57 |
58 | This work is both an introduction to Hoare logic and a demo
59 | illustrating Coq nice features. Indeed, the power of Coq higher order
60 | logic allows to give a very simple description of Hoare
61 | logic. Actually, I find this presentation simpler than those found in
62 | some hand-written books. In particular, because we are in a rich
63 | language, some notions which are not in the "kernel" of the logic
64 | (e.g. the return types of expressions or the assertion language) can
65 | be simply "shallow embedded": we do need to introduce an abstract
66 | syntax for these notions, and we can handle directly their semantics
67 | instead. Furthermore, because Coq is a programming language with
68 | propositions as first class citizens, it is able to compute the PO of
69 | Hoare logic on concrete imperative programs. At last, most of proofs
70 | in this development are discharged by Coq, using only a few hints
71 | given by the user. Most of these hints are simply some key ideas of
72 | the proofs.
73 |
74 | In summary, this work aims to illustrate the following ideas:
75 | - Coq is suitable both for reasoning about program logics and for
76 | emulating these logics.
77 | - Reflecting Dijkstra's weakest-precondition calculus is an elegant and powerful
78 | way to reason about non-functional programs in Coq.
79 |
80 | I use this library in some talks presenting Coq. It takes me usually
81 | between 45 minutes and 1 hour to present its different part.
82 |
83 |
84 | ## How to read the sources
85 |
86 |
87 | The best way to read the sources is to read them through coqide (or your
88 | favorite coq interface): you will be able to replay the (short) proofs
89 | of this development. In particular, replaying proof of `gcd_partial_proof`
90 | and `gcd_total_proof` in file `exgcd.v` allows to see
91 | how Coq generates PO on a concrete example. To do so, you
92 | may first need to download the sources and then to compile them using `make`.
93 | Alternatively, you can browse the html documentation through your favorite web browser.
94 | In case of trouble, please contact me.
95 |
96 | To read the sources, you may follow this order:
97 | - Start by reading `hoarelogicsemantics.v` (the semantics of my Hoare logic)
98 | and `exgcd.v` (which examplifies how to use this Hoare logic).
99 | It seems a good idea to read these files more or less in parallel.
100 | - Then, read `partialhoarelogic.v` the PO generation in partial correctness.
101 | - At last, read `totalhoarelogic.v` the PO generation in total correctness.
102 | Indeed, the PO generation in total correctness may be considered as a
103 | refinement of the PO generation in partial correctness.
104 | - It seems almost useless to read `hoarelogic.v`.
105 | Its purpose is only to glue all this stuff together.
106 |
107 |
108 | ## Contact
109 |
110 |
111 | If you have any comment, suggestion, question or trouble about this work,
112 | please send a mail to Sylvain.Boulme@imag.fr
113 |
114 | For more information, see my web page at
115 | http://www-verimag.imag.fr/~boulme
116 |
117 |
--------------------------------------------------------------------------------
/_CoqProject:
--------------------------------------------------------------------------------
1 | -R . HoareTut
2 |
3 | hoarelogicsemantics.v
4 | partialhoarelogic.v
5 | totalhoarelogic.v
6 | hoarelogic.v
7 | exgcd.v
8 |
--------------------------------------------------------------------------------
/coq-hoare-tut.opam:
--------------------------------------------------------------------------------
1 | opam-version: "2.0"
2 | authors: [
3 | "Sylvain Boulmé"
4 | ]
5 | maintainer: "kartiksinghal@gmail.com"
6 | version: "8.11.1"
7 |
8 | homepage: "https://github.com/coq-community/hoare-tut"
9 | dev-repo: "git+https://github.com/coq-community/hoare-tut.git"
10 | bug-reports: "https://github.com/coq-community/hoare-tut/issues"
11 | license: "LGPL-3.0-or-later"
12 |
13 | synopsis: "A Tutorial on Reflecting in Coq the generation of Hoare proof obligations"
14 | description: """
15 | Hoare logics are "program logics" suitable for reasoning about
16 | imperative programs.
17 | This work is both an introduction to Hoare logic and a demo
18 | illustrating Coq nice features. It formalizes the generation of PO
19 | (proof obligations) in a Hoare logic for a very basic imperative
20 | programming language. It proves the soundness and the completeness of
21 | the PO generation both in partial and total correctness. At last, it
22 | examplifies on a very simple example (a GCD computation) how the PO
23 | generation can simplify concrete proofs. Coq is indeed able to compute
24 | PO on concrete programs: we say here that the generation of proof
25 | obligations is reflected in Coq. Technically, the PO generation is
26 | here performed through Dijkstra's weakest-precondition calculus.
27 | """
28 |
29 | build: [make "-j%{jobs}%" ]
30 | install: [make "install"]
31 | depends: [
32 | "coq" {>= "8.8"}
33 | ]
34 |
35 | tags: [
36 | "category:Mathematics/Logic/See also"
37 | "category:Computer Science/Semantics"
38 | "category:Compilation/Semantics"
39 | "keyword:Hoare logic"
40 | "keyword:imperative program"
41 | "keyword:weakest precondition"
42 | "keyword:reflection"
43 | "logpath:HoareTut"
44 | ]
45 |
--------------------------------------------------------------------------------
/exgcd.v:
--------------------------------------------------------------------------------
1 | (** * The very classical example of GCD computation in Hoare logic.
2 |
3 | This file is part of the "Tutorial on Hoare Logic".
4 | For an introduction to this Coq library,
5 | see README #or index.html#.
6 |
7 | This file illustrates how to use the Hoare logic described in file
8 | ##[hoarelogicsemantics]##
9 |
10 |
11 | I use here the very classical example of "great common divisor"
12 | computations through successive subtractions.
13 | *)
14 |
15 | Set Implicit Arguments.
16 |
17 | Require Import ZArith.
18 | Require Import Znumtheory.
19 | Require Import hoarelogic.
20 | Require Import Zwf.
21 | Require Import Wellfounded.
22 | Require Import Lia.
23 |
24 | (** * Implementation of the expression language *)
25 | Module Example <: ExprLang.
26 |
27 | (** Here, I use only two global variables [VX] and [VY] of type [Z]
28 | (binary integers). *)
29 | Inductive ExVar: Type -> Type :=
30 | VX: (ExVar Z) |
31 | VY: (ExVar Z).
32 |
33 | Definition Var:=ExVar.
34 |
35 | (** An environment is just a pair of integers. First component
36 | represents [VX] and second component represents [VY]. This is
37 | expressed in [upd] and [get] below. *)
38 | Definition Env:= (Z*Z)%type.
39 |
40 | Definition upd (A:Type): (ExVar A) -> A -> Env -> Env :=
41 | fun x =>
42 | match x in (ExVar A) return A -> Env -> Env with
43 | | VX => fun vx e => (vx,snd e)
44 | | VY => fun vy e => (fst e,vy)
45 | end.
46 |
47 | Definition get (A:Type): (ExVar A) -> Env -> A :=
48 | fun x =>
49 | match x in (ExVar A) return Env -> A with
50 | | VX => fun e => fst e
51 | | VY => fun e => snd e
52 | end.
53 |
54 | (** I consider only two binary operators [PLUS] and [MINUS]. Their
55 | meaning is given by [eval_binOP] below *)
56 | Inductive binOP: Type := PLUS | MINUS.
57 |
58 | Definition eval_binOP: binOP -> Z -> Z -> Z :=
59 | fun op => match op with
60 | | PLUS => Zplus
61 | | MINUS => Zminus
62 | end.
63 |
64 | (** I consider only three comparison operators [EQ], [NEQ] and
65 | [LE]. Their meaning is given by [eval_relOP] below *)
66 | Inductive relOP: Type := EQ | NEQ | LE.
67 |
68 | Definition eval_relOP: relOP -> Z -> Z -> bool :=
69 | fun op => match op with
70 | | EQ => Zeq_bool
71 | | NEQ => Zneq_bool
72 | | LE => Zle_bool
73 | end.
74 |
75 | (** Here is the abstract syntax of expressions. The semantics is given
76 | by [eval] below *)
77 | Inductive ExExpr: Type -> Type :=
78 | | const: forall (A:Type), A -> (ExExpr A)
79 | | binop: binOP -> (ExExpr Z) -> (ExExpr Z) -> (ExExpr Z)
80 | | relop: relOP -> (ExExpr Z) -> (ExExpr Z) -> (ExExpr bool)
81 | | getvar: forall (A:Type), (ExVar A) -> (ExExpr A).
82 |
83 | Definition Expr:= ExExpr.
84 |
85 | Fixpoint eval (A:Type) (expr:Expr A) (e:Env) { struct expr } : A :=
86 | match expr in ExExpr A return A with
87 | | const A v => v
88 | | binop op e1 e2 => eval_binOP op (eval e1 e) (eval e2 e)
89 | | relop op e1 e2 => eval_relOP op (eval e1 e) (eval e2 e)
90 | | getvar A x => (get x e)
91 | end.
92 |
93 | End Example.
94 |
95 | (** * Instantiation of the Hoare logic on this language. *)
96 | Module HL := HoareLogic(Example).
97 | Import HL.
98 | Import Example.
99 |
100 | (** These coercions makes the abstract syntax more user-friendly *)
101 | Coercion getvar: ExVar >-> ExExpr.
102 | Coercion binop: binOP >-> Funclass.
103 | Coercion relop: relOP >-> Funclass.
104 |
105 | (** A last coercion useful for assertions *)
106 | Coercion get: ExVar >-> Funclass.
107 |
108 | (** ** A [gcd] computation in this language *)
109 | Definition gcd :=
110 | (Iwhile (NEQ VX VY)
111 | (Iif (LE VX VY)
112 | (Iset VY (MINUS VY VX))
113 | (Iset VX (MINUS VX VY)))).
114 |
115 | (** A small technical lemma on the mathematical notion of gcd (called
116 | [Zis_gcd]) *)
117 | Lemma Zgcd_minus: forall a b d:Z, Zis_gcd a (b - a) d -> Zis_gcd a b d.
118 | Proof.
119 | intros a b d H; case H; constructor; intuition (auto with zarith).
120 | replace b with (b-a+a)%Z.
121 | auto with zarith.
122 | lia.
123 | Qed.
124 |
125 | Global Hint Resolve Zgcd_minus: zarith.
126 |
127 | (** Two other lemmas relating [Zneq_bool] function with inequality
128 | relation *)
129 | Lemma Zneq_bool_false: forall x y, Zneq_bool x y=false -> x=y.
130 | Proof.
131 | intros x y H0; apply Zcompare_Eq_eq; generalize H0; clear H0; unfold Zneq_bool. case (x ?= y)%Z; auto;
132 | try (intros; discriminate); auto.
133 | Qed.
134 |
135 | Lemma Zneq_bool_true: forall x y, Zneq_bool x y=true -> x<>y.
136 | Proof.
137 | intros x y; unfold Zneq_bool.
138 | intros H H0; subst.
139 | rewrite Z.compare_refl in H.
140 | discriminate.
141 | Qed.
142 |
143 | Global Hint Resolve Zneq_bool_true Zneq_bool_false Zle_bool_imp_le Zis_gcd_intro: zarith.
144 |
145 | (** ** Partial correctness proof of [gcd] *)
146 | Lemma gcd_partial_proof:
147 | forall x0 y0, (fun e => (VX e)=x0 /\ (VY e)=y0)
148 | |= gcd {= fun e => (Zis_gcd x0 y0 (VX e)) =}.
149 | Proof.
150 | intros x0 y0.
151 | apply PHL.soundness.
152 | simpl.
153 | intros e; intuition subst.
154 | (** after PO generation, I provide the invariant and simplify the goal *)
155 | constructor 1 with (x:=fun e'=>
156 | forall d, (Zis_gcd (VX e') (VY e') d)
157 | ->(Zis_gcd (VX e) (VY e) d)); simpl.
158 | intuition auto with zarith.
159 | (** - invariant => postcondition *)
160 | replace (snd e') with (fst e') in H; auto with zarith.
161 | Qed.
162 |
163 |
164 | (** ** Total correctness proof of [gcd] *)
165 |
166 | Lemma gcd_total_proof:
167 | forall x0 y0, (fun e => (VX e)=x0 /\ (VY e)=y0 /\ x0 > 0 /\ y0 > 0)
168 | |= gcd [= fun e => (Zis_gcd x0 y0 (VX e)) =].
169 | Proof.
170 | intros x0 y0.
171 | apply THL.soundness.
172 | simpl.
173 | intros e; intuition subst.
174 | (** after simplification, I provide the invariant and then the variant *)
175 | constructor 1 with (x:=fun e' => (VX e') > 0 /\ (VY e') > 0 /\
176 | forall d, (Zis_gcd (VX e') (VY e') d)
177 | ->(Zis_gcd (VX e) (VY e) d)); simpl.
178 | constructor 1 with (x:=fun e1 e0 => Zwf 0 ((VX e1)+(VY e1)) ((VX e0)+(VY e0))).
179 | (** - proof that my variant is a well_founded relation *)
180 | constructor 1.
181 | apply wf_inverse_image with (f:=fun e=>(VX e)+(VY e)).
182 | auto with datatypes.
183 | (** - other goals *)
184 | unfold Zwf; simpl; (intuition auto with zarith).
185 | (** -- invariant => postcondition
186 | --- gcd part like in partial correctness proof
187 | *)
188 | replace (snd e') with (fst e') in H5; auto with zarith.
189 | (** --- new VY in branch "then" is positive *)
190 | cut ((fst e')<=(snd e')); auto with zarith.
191 | cut ((fst e')<>(snd e')); auto with zarith.
192 | Qed.
193 |
194 | (** ** Another example: infinite loops in partial correctness.
195 |
196 | Basic Hoare logic is not well-suited for reasoning about non-terminating programs.
197 | In total correctness, postconditions of non-terminating programs are not provable.
198 | In partial correctness, a non-terminating program satisfies any (unsatisfiable) postcondition.
199 |
200 | For example, in an informal "meaning", the program below enumerates all multiples of 3. But this meaning
201 | can not be expressed here (even in partial correctness).
202 | *)
203 |
204 | Definition enum_3N :=
205 | (Iseq (Iset VX (const 0))
206 | (Iwhile (const true)
207 | (Iset VX (PLUS VX (const 3))))).
208 |
209 | Lemma enum_3N_stupid:
210 | (fun e => True) |= enum_3N {= fun e => False =}.
211 | Proof.
212 | apply PHL.soundness.
213 | simpl.
214 | constructor 1 with (x:=fun _:Env => True).
215 | intuition (discriminate || auto).
216 | Qed.
217 |
218 |
219 | (** "Tutorial on Hoare Logic" Library. Copyright 2007 Sylvain Boulme.
220 |
221 | This file is distributed under the terms of the
222 | "GNU LESSER GENERAL PUBLIC LICENSE" version 3.
223 | *)
224 |
--------------------------------------------------------------------------------
/hoarelogic.v:
--------------------------------------------------------------------------------
1 | (**
2 |
3 | This file is part of the "Tutorial on Hoare Logic".
4 | For an introduction to this Coq library,
5 | see README #or index.html#.
6 |
7 | This file is mainly verbous. It defines a functor
8 | "[HoareLogic: ExprLang -> HoareLogicSem]".
9 | It is almost a copy/paste of definitions found in
10 | ##
11 | [hoarelogicsemantics]##.
12 | (This is due to the lack of inheritance in the module system of Coq).
13 |
14 |
15 | *)
16 |
17 | Set Implicit Arguments.
18 |
19 | Require Export hoarelogicsemantics.
20 | Require Import partialhoarelogic.
21 | Require Import totalhoarelogic.
22 |
23 | Module HoareLogic(Ex: ExprLang)<: HoareLogicSem with Module E:=Ex.
24 |
25 | Module E:=Ex.
26 |
27 | Module HLD <: HoareLogicDefs with Module E:=E.
28 |
29 | Module E:=E.
30 |
31 | Inductive ImpProg: Type :=
32 | | Iskip: ImpProg
33 | | Iset (A:Type) (v:E.Var A) (expr:E.Expr A): ImpProg
34 | | Iif (cond:E.Expr bool) (p1 p2:ImpProg): ImpProg
35 | | Iseq (p1 p2:ImpProg): ImpProg
36 | | Iwhile (cond:E.Expr bool) (p:ImpProg): ImpProg.
37 |
38 | Inductive exec: E.Env -> ImpProg -> E.Env -> Prop :=
39 | | exec_Iskip:
40 | forall e, (exec e Iskip e)
41 | | exec_Iset:
42 | forall (A:Type) e x (expr: E.Expr A),
43 | (exec e (Iset x expr) (E.upd x (E.eval expr e) e))
44 | | exec_Iif:
45 | forall e (cond: E.Expr bool) p1 p2 e',
46 | (exec e (if (E.eval cond e) then p1 else p2) e')
47 | -> (exec e (Iif cond p1 p2) e')
48 | | exec_Iseq:
49 | forall e p1 p2 e' e'',
50 | (exec e p1 e')
51 | -> (exec e' p2 e'')
52 | -> (exec e (Iseq p1 p2) e'')
53 | | exec_Iwhile:
54 | forall e cond p e',
55 | (exec e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e')
56 | -> (exec e (Iwhile cond p) e').
57 |
58 | Lemma exec_Iif_true:
59 | forall e cond p1 p2 e',
60 | (E.eval cond e)=true
61 | -> (exec e p1 e')
62 | -> (exec e (Iif cond p1 p2) e').
63 | Proof.
64 | intros e cond p1 p2 e' H1 H2.
65 | apply exec_Iif.
66 | rewrite H1; auto.
67 | Qed.
68 |
69 | Lemma exec_Iif_false:
70 | forall e cond p1 p2 e',
71 | (E.eval cond e)=false
72 | -> (exec e p2 e')
73 | -> (exec e (Iif cond p1 p2) e').
74 | Proof.
75 | intros e cond p1 p2 e' H1 H2.
76 | apply exec_Iif.
77 | rewrite H1; auto.
78 | Qed.
79 |
80 | Definition Pred := E.Env -> Prop.
81 |
82 | Definition wlp: ImpProg -> Pred -> Pred
83 | := fun prog post e => (forall e', (exec e prog e') -> (post e')).
84 |
85 | Definition wp: ImpProg -> Pred -> Pred
86 | := fun prog post e => exists e', (exec e prog e') /\ (post e').
87 |
88 |
89 | Notation "p |= q" := (forall e, (p e) -> (q e)) (at level 80, no associativity).
90 | Notation "p {= post =}" := (wlp p post) (at level 70).
91 | Notation "p [= post =]" := (wp p post) (at level 70).
92 |
93 | End HLD.
94 |
95 | Export HLD.
96 |
97 | Module PHL<: HoareProofSystem := PartialHoareLogic(HLD).
98 | Module THL<: HoareProofSystem := TotalHoareLogic(HLD).
99 |
100 | Import THL.
101 |
102 | Lemma wp_entails_wlp: forall prog post, prog [= post =] |= prog {= post =}.
103 | Proof.
104 | unfold wp, wlp. intros prog post e H e' H'.
105 | dec2 e0 H.
106 | dec2 H0 H.
107 | rewrite (exec_deterministic H' H0).
108 | auto.
109 | Qed.
110 |
111 | End HoareLogic.
112 |
113 | (** "Tutorial on Hoare Logic" Library. Copyright 2007 Sylvain Boulme.
114 |
115 | This file is distributed under the terms of the
116 | "GNU LESSER GENERAL PUBLIC LICENSE" version 3.
117 | *)
118 |
--------------------------------------------------------------------------------
/hoarelogicsemantics.v:
--------------------------------------------------------------------------------
1 | (** * Semantics of my Hoare Logic
2 |
3 | This file is part of the "Tutorial on Hoare Logic".
4 | For an introduction to this Coq library,
5 | see README #or index.html#.
6 |
7 | *)
8 |
9 | Set Implicit Arguments.
10 |
11 | (** * Expression Language
12 |
13 | My Hoare logic is parametrized by the expression language.
14 | The language is here assumed to have global variables.
15 | Furthermore, expressions are purely functional (they terminates
16 | and have no side effects). However, expressions and variables are
17 | strongly-typed. Here, this typing is directly performed by Coq.
18 | - "[Var A]" is the type of variables storing values of type [A]
19 | - "[Expr A]" is the type of expressions return values of type [A]
20 | - [Env] is the type of environment. An environment is a mapping from variables into values.
21 | - "[upd x v e]" sets [v] as the new value of [x] from environment [e]
22 | - [eval] evaluates an expression in a given environment.
23 |
24 | See the file ##[exgcd]## to get a simple example
25 | of how such a language can be instantiated.
26 | *)
27 |
28 | Module Type ExprLang.
29 |
30 | Parameter Var: Type -> Type.
31 | Parameter Expr: Type -> Type.
32 | Parameter Env: Type.
33 |
34 | Parameter upd: forall (A:Type), (Var A) -> A -> Env -> Env.
35 | Parameter eval: forall (A:Type), (Expr A) -> Env -> A.
36 |
37 | End ExprLang.
38 |
39 | (** * Definitions of my Hoare logic *)
40 | Module Type HoareLogicDefs.
41 |
42 | Declare Module E: ExprLang.
43 |
44 | (** ** The programming language: syntax and semantics *)
45 |
46 | (** Abstract syntax of the imperative programming language
47 | - [Iskip] is an instruction to "do nothing"
48 | - "[Iset x expr]" stores in [x] the result of [expr]
49 | - [Iif] represents conditional branching
50 | - [Iseq] represents the sequence of two instructions
51 | - [Iwhile] represents the while-loop
52 |
53 | This semantics is formalizes by [exec] predicate below.
54 | *)
55 | Inductive ImpProg: Type :=
56 | | Iskip: ImpProg
57 | | Iset (A:Type) (x:E.Var A) (expr:E.Expr A): ImpProg
58 | | Iif (cond:E.Expr bool) (p1 p2:ImpProg): ImpProg
59 | | Iseq (p1 p2:ImpProg): ImpProg
60 | | Iwhile (cond:E.Expr bool) (p:ImpProg): ImpProg.
61 |
62 | (** The semantics of the programming language is given by the following natural semantics.
63 | Here "[exec e0 p e1]" means that "in the initial environment [e0] the execution of [p]
64 | terminates in the final environment [e1]".
65 | *)
66 | Inductive exec: E.Env -> ImpProg -> E.Env -> Prop :=
67 | | exec_Iskip:
68 | forall e, (exec e Iskip e)
69 | | exec_Iset:
70 | forall (A:Type) e x (expr: E.Expr A),
71 | (exec e (Iset x expr) (E.upd x (E.eval expr e) e))
72 | | exec_Iif:
73 | forall e (cond: E.Expr bool) p1 p2 e',
74 | (exec e (if (E.eval cond e) then p1 else p2) e')
75 | -> (exec e (Iif cond p1 p2) e')
76 | | exec_Iseq:
77 | forall e p1 p2 e' e'',
78 | (exec e p1 e')
79 | -> (exec e' p2 e'')
80 | -> (exec e (Iseq p1 p2) e'')
81 | | exec_Iwhile:
82 | forall e cond p e',
83 | (exec e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e')
84 | -> (exec e (Iwhile cond p) e').
85 |
86 | (** This language is thus deterministic.
87 | This is proved by lemma [exec_deterministic] in file
88 | ##[totalhoarelogic]##.
89 | *)
90 |
91 | (** ** The specification language: syntax and semantics
92 |
93 | If [prog] is a program, [pre] a precondition (i.e. a required
94 | property on the initial environment) and [post] a postcondition
95 | (i.e. a property on the final environment that [prog] must ensure
96 | when [pre] is initially satisfied), then a specification in Hoare
97 | logic is written:
98 | - "[pre |= prog {= post =}]" in partial correctness (i.e. even if
99 | [pre] is satisfied, [prog] may not terminate)
100 | - "[pre |= prog [= post =]]" in total correctness (i.e. when [pre]
101 | is satisfied, [prog] must terminate).
102 |
103 | Below, we give the semantics of these notations. Here the assertion
104 | language is shallow embedded (there is no syntax for it: we use
105 | directly the Coq proposition language).
106 |
107 | [Pred] is the type of assertions.
108 | *)
109 | Definition Pred := E.Env -> Prop.
110 |
111 | (** Deduction operator "|=" is here only a syntactic sugar.
112 | In the following, [p |= q] can also be read "[q] is weaker than [p]".
113 | *)
114 | Notation "p |= q" := (forall e, (p e) -> (q e)) (at level 80, no associativity).
115 |
116 | (** [wlp] represents the Weakest Liberal Precondition (i.e.
117 | "[wlp prog post]" is the weakest condition on the initial environment ensuring
118 | that [post] is ensured on any final environment of [prog], when such environment
119 | exists)
120 | *)
121 | Definition wlp: ImpProg -> Pred -> Pred
122 | := fun prog post e => (forall e', (exec e prog e') -> (post e')).
123 |
124 | Notation "p {= post =}" := (wlp p post) (at level 70).
125 |
126 | (** [wp] represents the Weakest Precondition (i.e. "[wp prog post]" is
127 | the weakest condition on the initial environment ensuring that there
128 | exists a final environment of [prog] satisfying [post]). Here as the language is
129 | deterministic, I proved that "[(wp prog post) |= (wlp prog post)]"
130 | see lemma [wp_entails_wlp] below.
131 | *)
132 | Definition wp: ImpProg -> Pred -> Pred
133 | := fun prog post e => exists e', (exec e prog e') /\ (post e').
134 |
135 | Notation "p [= post =]" := (wp p post) (at level 70).
136 |
137 |
138 | (** ** A useful base of hints
139 |
140 | These hints are used in the metatheoritical proofs of the logic.
141 | *)
142 |
143 | Global Hint Resolve exec_Iskip exec_Iset exec_Iif exec_Iseq exec_Iwhile: hoare.
144 |
145 | Parameter exec_Iif_true:
146 | forall e cond p1 p2 e',
147 | (E.eval cond e)=true
148 | -> (exec e p1 e')
149 | -> (exec e (Iif cond p1 p2) e').
150 |
151 | Parameter exec_Iif_false:
152 | forall e cond p1 p2 e',
153 | (E.eval cond e)=false
154 | -> (exec e p2 e')
155 | -> (exec e (Iif cond p1 p2) e').
156 |
157 |
158 | Global Hint Resolve exec_Iif_true exec_Iif_false: hoare.
159 |
160 | End HoareLogicDefs.
161 |
162 |
163 | (** * Generation of proof obligations (POs)
164 |
165 | Given [sem_wp] one of the two preceding notion of
166 | weakest-precondition, the generation of proof obligations is a
167 | function [synt_wp] logically equivalent to [sem_wp], which proceeds
168 | recursively on the abstract syntax of the program. Hence,
169 | "[synt_wp prog post]" is an assertion which does not involve [exec].
170 |
171 | In practice, the PO generation is run by invoking the [soundness] lemma.
172 | The [completeness] lemma is only ensuring that if the generated PO
173 | seems hard to prove, this is not due to a bug in [synt_wp]
174 | implementation.
175 |
176 | *)
177 |
178 | Module Type HoareProofSystem.
179 |
180 | Declare Module HLD: HoareLogicDefs.
181 |
182 | Import HLD.
183 |
184 | Parameter sem_wp: ImpProg -> Pred -> Pred.
185 | Parameter synt_wp: ImpProg -> Pred -> Pred.
186 |
187 | Parameter soundness: forall pre p post, pre |= (synt_wp p post) -> pre |= (sem_wp p post).
188 | Parameter completeness: forall pre p post, pre |= (sem_wp p post) -> pre |= (synt_wp p post).
189 |
190 | End HoareProofSystem.
191 |
192 |
193 | (** * The whole meta-theory
194 | The whole meta-theory is described by this module signature. This signature is implemented in file ##[hoarelogic]##.
195 |
196 | See the file ##[exgcd]## to get a simple example
197 | of how the "proof obligation generation" is used.
198 | - module [PHL] contains the PO generation in partial correctness.
199 | - module [THL] contains the PO generation in total correctness.
200 | *)
201 |
202 | Module Type HoareLogicSem.
203 |
204 | Declare Module E: ExprLang.
205 |
206 | Declare Module HLD: HoareLogicDefs with Module E:=E.
207 | Import HLD.
208 |
209 | Declare Module PHL: HoareProofSystem with Module HLD:=HLD with Definition sem_wp:=wlp.
210 | Declare Module THL: HoareProofSystem with Module HLD:=HLD with Definition sem_wp:=wp.
211 |
212 | Parameter wp_entails_wlp: forall prog post, prog [= post =] |= prog {= post =}.
213 |
214 | End HoareLogicSem.
215 |
216 | (** "Tutorial on Hoare Logic" Library. Copyright 2007 Sylvain Boulme.
217 |
218 | This file is distributed under the terms of the
219 | "GNU LESSER GENERAL PUBLIC LICENSE" version 3.
220 | *)
221 |
--------------------------------------------------------------------------------
/meta.yml:
--------------------------------------------------------------------------------
1 | ---
2 | fullname: Tutorial on Hoare Logic
3 | shortname: hoare-tut
4 | namespace: HoareTut
5 | organization: coq-community
6 | community: true
7 |
8 | synopsis: >-
9 | A Tutorial on Reflecting in Coq the generation of Hoare proof obligations
10 |
11 | description: |
12 | Hoare logics are "program logics" suitable for reasoning about
13 | imperative programs.
14 | This work is both an introduction to Hoare logic and a demo
15 | illustrating Coq nice features. It formalizes the generation of PO
16 | (proof obligations) in a Hoare logic for a very basic imperative
17 | programming language. It proves the soundness and the completeness of
18 | the PO generation both in partial and total correctness. At last, it
19 | examplifies on a very simple example (a GCD computation) how the PO
20 | generation can simplify concrete proofs. Coq is indeed able to compute
21 | PO on concrete programs: we say here that the generation of proof
22 | obligations is reflected in Coq. Technically, the PO generation is
23 | here performed through Dijkstra's weakest-precondition calculus.
24 |
25 | authors:
26 | - name: Sylvain Boulmé
27 | initial: true
28 |
29 | maintainers:
30 | - name: Kartik Singhal
31 | nickname: k4rtik
32 |
33 | opam-file-maintainer: kartiksinghal@gmail.com
34 |
35 | license:
36 | fullname: GNU Lesser General Public License v3.0 or later
37 | identifier: LGPL-3.0-or-later
38 |
39 | supported_coq_versions:
40 | text: 8.10 or later
41 |
42 | keywords:
43 | - name: Hoare logic
44 | - name: imperative program
45 | - name: weakest precondition
46 | - name: reflection
47 |
48 | categories:
49 | - name: Mathematics/Logic/See also
50 | - name: Computer Science/Semantics
51 | - name: Compilation/Semantics
52 |
53 | build: |
54 | ## Build instructions
55 |
56 | ``` shell
57 | git clone https://github.com/coq-community/hoare-tut
58 | cd hoare-tut
59 | make # or make -j
60 | make html # to build html documentation
61 | ```
62 |
63 | documentation: |
64 | ## Aims of this library
65 |
66 |
67 | This work is both an introduction to Hoare logic and a demo
68 | illustrating Coq nice features. Indeed, the power of Coq higher order
69 | logic allows to give a very simple description of Hoare
70 | logic. Actually, I find this presentation simpler than those found in
71 | some hand-written books. In particular, because we are in a rich
72 | language, some notions which are not in the "kernel" of the logic
73 | (e.g. the return types of expressions or the assertion language) can
74 | be simply "shallow embedded": we do need to introduce an abstract
75 | syntax for these notions, and we can handle directly their semantics
76 | instead. Furthermore, because Coq is a programming language with
77 | propositions as first class citizens, it is able to compute the PO of
78 | Hoare logic on concrete imperative programs. At last, most of proofs
79 | in this development are discharged by Coq, using only a few hints
80 | given by the user. Most of these hints are simply some key ideas of
81 | the proofs.
82 |
83 | In summary, this work aims to illustrate the following ideas:
84 | - Coq is suitable both for reasoning about program logics and for
85 | emulating these logics.
86 | - Reflecting Dijkstra's weakest-precondition calculus is an elegant and powerful
87 | way to reason about non-functional programs in Coq.
88 |
89 | I use this library in some talks presenting Coq. It takes me usually
90 | between 45 minutes and 1 hour to present its different part.
91 |
92 |
93 | ## How to read the sources
94 |
95 |
96 | The best way to read the sources is to read them through coqide (or your
97 | favorite coq interface): you will be able to replay the (short) proofs
98 | of this development. In particular, replaying proof of `gcd_partial_proof`
99 | and `gcd_total_proof` in file `exgcd.v` allows to see
100 | how Coq generates PO on a concrete example. To do so, you
101 | may first need to download the sources and then to compile them using `make`.
102 | Alternatively, you can browse the html documentation through your favorite web browser.
103 | In case of trouble, please contact me.
104 |
105 | To read the sources, you may follow this order:
106 | - Start by reading `hoarelogicsemantics.v` (the semantics of my Hoare logic)
107 | and `exgcd.v` (which examplifies how to use this Hoare logic).
108 | It seems a good idea to read these files more or less in parallel.
109 | - Then, read `partialhoarelogic.v` the PO generation in partial correctness.
110 | - At last, read `totalhoarelogic.v` the PO generation in total correctness.
111 | Indeed, the PO generation in total correctness may be considered as a
112 | refinement of the PO generation in partial correctness.
113 | - It seems almost useless to read `hoarelogic.v`.
114 | Its purpose is only to glue all this stuff together.
115 |
116 |
117 | ## Contact
118 |
119 |
120 | If you have any comment, suggestion, question or trouble about this work,
121 | please send a mail to Sylvain.Boulme@imag.fr
122 |
123 | For more information, see my web page at
124 | http://www-verimag.imag.fr/~boulme
125 | ---
126 |
--------------------------------------------------------------------------------
/partialhoarelogic.v:
--------------------------------------------------------------------------------
1 | (** * Generation of Hoare proof obligations in partial correctness
2 |
3 | This file is part of the "Tutorial on Hoare Logic".
4 | For an introduction to this Coq library,
5 | see README #or index.html#.
6 |
7 | This file gives a syntactic definition of the weakest liberal precondition [wlp]
8 | introduced in ##[hoarelogicsemantics]##.
9 | *)
10 |
11 | Global Set Asymmetric Patterns.
12 | Set Implicit Arguments.
13 | Require Export hoarelogicsemantics.
14 |
15 | Module PartialHoareLogic (HD: HoareLogicDefs).
16 |
17 | Export HD.
18 | Module HLD:=HD.
19 |
20 | Definition sem_wp := wlp.
21 |
22 | (** * Syntactic definition of the weakest liberal precondition.
23 |
24 | In the following, we show that this definition is logically
25 | equivalent to [wlp].
26 | *)
27 | Fixpoint synt_wp (prog: ImpProg) : Pred -> Pred
28 | := fun post e =>
29 | match prog with
30 | | Iskip => post e
31 | | (Iset A x expr) => post (E.upd x (E.eval expr e) e)
32 | | (Iif cond p1 p2) =>
33 | ((E.eval cond e)=true -> (synt_wp p1 post e))
34 | /\ ((E.eval cond e)=false -> (synt_wp p2 post e))
35 | | (Iseq p1 p2) => synt_wp p1 (synt_wp p2 post) e
36 | | (Iwhile cond p) =>
37 | exists inv:Pred,
38 | (inv e)
39 | /\ (forall e', (inv e')
40 | -> (E.eval cond e')=false -> (post e'))
41 | /\ (forall e', (inv e')
42 | -> (E.eval cond e')=true -> (synt_wp p inv e'))
43 | end.
44 |
45 | (** This property is also trivially satisfied by [wlp].
46 | We need it here to prove the soundness.
47 | *)
48 | Lemma synt_wp_monotonic:
49 | forall (p: ImpProg) (post1 post2: Pred),
50 | (post1 |= post2) -> (synt_wp p post1) |= (synt_wp p post2).
51 | Proof.
52 | induction p; simpl; firstorder eauto with hoare.
53 | Qed.
54 |
55 | Global Hint Resolve synt_wp_monotonic: hoare.
56 |
57 | (** * Soundness
58 |
59 | The proof of soundness proceeds by induction over the derivation
60 | [exec ... prog ...] in implicit hypothesis induced by [wlp] definition.
61 |
62 | Please, notice that coq performs the [exec_Iwhile] case alone (that's where
63 | monotonicity is used). Unfortunately, the case [exec_Iif] which seems
64 | trivial to a human is not discharged by Coq.
65 | *)
66 | Lemma wp_sound: forall prog post, synt_wp prog post |= prog{=post=}.
67 | Proof.
68 | intros prog post e H0 e' H; generalize post H0; clear H0 post.
69 | elim H; clear H e' e prog; simpl; try ((firstorder eauto 20 with hoare); fail).
70 | (** - case [exec_Iif] *)
71 | intros e cond p1 p2 e'.
72 | case (E.eval cond e); simpl; firstorder auto.
73 | Qed.
74 |
75 | (** * Completeness
76 |
77 | The proof of completeness proceeds by induction over [prog] syntax.
78 |
79 | Please, notice that coq performs this proof almost alone. The only
80 | hint given here is the invariant.
81 | *)
82 | Lemma wp_complete: forall prog post, prog{=post=} |= (synt_wp prog post).
83 | Proof.
84 | unfold wlp; intros prog; elim prog; clear prog; simpl;
85 | try ((firstorder auto with hoare); fail).
86 | (** - case [Iseq] *)
87 | eauto with hoare.
88 | (** - case [Iwhile]: I provide the invariant below *)
89 | intros.
90 | constructor 1 with (x:=wlp (Iwhile cond p) post).
91 | unfold wlp; intuition eauto 20 with hoare.
92 | Qed.
93 |
94 | (** * Combining the previous results with transitivity of [ |= ] *)
95 |
96 | Global Hint Resolve wp_complete wp_sound: hoare.
97 |
98 | Theorem soundness: forall pre p post, pre |= (synt_wp p post) -> pre |= p {=post=}.
99 | Proof.
100 | auto with hoare.
101 | Qed.
102 |
103 | Theorem completeness: forall pre p post, pre |= p {=post=} -> pre |= (synt_wp p post).
104 | Proof.
105 | intuition auto with hoare.
106 | Qed.
107 |
108 |
109 | End PartialHoareLogic.
110 |
111 | (** "Tutorial on Hoare Logic" Library. Copyright 2007 Sylvain Boulme.
112 |
113 | This file is distributed under the terms of the
114 | "GNU LESSER GENERAL PUBLIC LICENSE" version 3.
115 | *)
116 |
--------------------------------------------------------------------------------
/totalhoarelogic.v:
--------------------------------------------------------------------------------
1 | (** * Generation of Hoare proof obligations in total correctness
2 |
3 | This file is part of the "Tutorial on Hoare Logic".
4 | For an introduction to this Coq library,
5 | see README #or index.html#.
6 |
7 | This file gives a syntactic definition of the weakest precondition [wp]
8 | introduced in ##[hoarelogicsemantics]##.
9 | We refine here the approach of ##[partialhoarelogic]##.
10 | *)
11 |
12 | Global Set Asymmetric Patterns.
13 | Set Implicit Arguments.
14 | Require Export hoarelogicsemantics.
15 |
16 | Module TotalHoareLogic (HD: HoareLogicDefs).
17 |
18 | Export HD.
19 | Module HLD:=HD.
20 |
21 | Definition sem_wp := wp.
22 |
23 | Export Wf.
24 |
25 | (** * Syntactic definition of the weakest precondition.
26 |
27 | In the following, we show that this definition is logically
28 | equivalent to [wp].
29 | *)
30 | Fixpoint synt_wp (prog: ImpProg) : Pred -> Pred
31 | := fun post e =>
32 | match prog with
33 | | Iskip => post e
34 | | (Iset A x expr) => post (E.upd x (E.eval expr e) e)
35 | | (Iif cond p1 p2) =>
36 | ((E.eval cond e)=true -> (synt_wp p1 post e))
37 | /\ ((E.eval cond e)=false -> (synt_wp p2 post e))
38 | | (Iseq p1 p2) => synt_wp p1 (synt_wp p2 post) e
39 | | (Iwhile cond p) =>
40 | exists inv:Pred,
41 | exists R:E.Env -> E.Env -> Prop,
42 | (well_founded R)
43 | /\ (inv e)
44 | /\ (forall e', (inv e')
45 | -> (E.eval cond e')=false -> post e')
46 | /\ (forall e', (inv e')
47 | -> (E.eval cond e')=true -> synt_wp p inv e')
48 | /\ (forall e0, (inv e0)
49 | -> (E.eval cond e0)=true -> synt_wp p (fun e1 => R e1 e0) e0)
50 | end.
51 |
52 | (** * Soundness *)
53 |
54 | (** Monotonicity is also trivially satisfied by [wp].
55 | We need it here to prove the soundness.
56 | *)
57 | Lemma synt_wp_monotonic:
58 | forall (p: ImpProg) (post1 post2: Pred),
59 | (forall e, post1 e -> post2 e)
60 | -> forall e, (synt_wp p post1 e) -> (synt_wp p post2 e).
61 | Proof.
62 | induction p; simpl; firstorder eauto.
63 | Qed.
64 |
65 | Global Hint Resolve synt_wp_monotonic: hoare.
66 |
67 | (** Below, a little tactic to decompose a pair in hypothesis [H]
68 | by giving the name [n] to the first component.
69 | *)
70 | Ltac dec2 n H := case H; clear H; intros n H.
71 |
72 | (** The property below is also satisfied by [wp] (using the fact that
73 | the language is deterministic).
74 | We need it here to prove the soundness.
75 | *)
76 | Lemma synt_wp_conj:
77 | forall (p: ImpProg) (post1 post2: Pred) e,
78 | (synt_wp p post1 e) -> (synt_wp p post2 e)
79 | -> (synt_wp p (fun e => post1 e /\ post2 e) e).
80 | Proof.
81 | induction p; simpl; try ((intuition auto); fail).
82 | (* Iseq *)
83 | intros post1 post2 e H1 H2.
84 | intros; eapply synt_wp_monotonic.
85 | 2: apply (IHp1 _ _ _ H1 H2).
86 | simpl; intuition auto.
87 | (* Iwhile *)
88 | intros post1 post2 e H1 H2.
89 | dec2 inv1 H1.
90 | dec2 R1 H1.
91 | dec2 inv2 H2.
92 | intros;
93 | constructor 1 with (x:=fun e => inv1 e /\ inv2 e).
94 | constructor 1 with (x:=R1).
95 | firstorder auto.
96 | Qed.
97 |
98 |
99 | (** The proof of soundness proceeds by induction over [prog].
100 | *)
101 | Lemma wp_sound: forall prog post, synt_wp prog post |= prog [=post=].
102 | Proof.
103 | unfold wp.
104 | induction prog; simpl; try ((intuition eauto with hoare); fail).
105 | (* - case [Iif] *)
106 | intros post e.
107 | set (b:=E.eval cond e).
108 | cut (E.eval cond e=b); auto.
109 | case b; firstorder eauto with hoare.
110 | (* - case [Iseq] *)
111 | intros post e H; case (IHprog1 _ _ H).
112 | intros e0 H0; case (IHprog2 post e0); firstorder eauto with hoare.
113 | (* - case [Iwhile] *)
114 | intros post e H.
115 | dec2 inv H.
116 | dec2 R H.
117 | dec2 Rwf H.
118 | dec2 Hinv H.
119 | dec2 H1 H.
120 | dec2 H2 H.
121 | generalize Hinv.
122 | pattern e.
123 | (* -- here the proof proceeds by induction on the well-founded relation *)
124 | eapply well_founded_ind; eauto.
125 | clear Hinv e.
126 | intros e' X H'.
127 | set (b:=E.eval cond e').
128 | cut (E.eval cond e'=b); auto.
129 | case b; [ idtac | firstorder eauto with hoare ].
130 | intros H5.
131 | case (IHprog (wp (Iwhile cond prog) post) e');
132 | [ idtac | (unfold wp; firstorder eauto with hoare) ].
133 | eapply synt_wp_monotonic.
134 | 2:apply (synt_wp_conj _ _ _ _ (H2 _ H' H5) (H _ H' H5)).
135 | simpl; unfold wp; intuition auto.
136 | Qed.
137 |
138 | (** * Auxiliary lemmas for completeness
139 |
140 | The proof of completeness requires to exhibit a variant.
141 | The purpose of the following lemmas is to build this variant.
142 | *)
143 |
144 | (** ** A technical issue: the inversion of [exec]
145 |
146 | If your are not interested in Coq details, you may skip this part
147 | which only explains how to avoid the assumption of a (consistent
148 | and standard) axiom to prove the completeness.
149 |
150 | Because the use of dependent types in constructor [exec_Iset], the
151 | standard inversion of Coq may fail on [exec] (see
152 | [exec_test_inversion] below).
153 |
154 | This comes from the fact the following property is not provable in
155 | the core theory of Coq (although it is consistent with it) :
156 |
157 | [forall A (x1 x2:E.Var A) e1 e2, (Iset x1 e1)=(Iset x2 e2) -> x1=x2 /\ e1=e2.]
158 |
159 | To deal with this problem, we may assume a (consistent) axiom given
160 | in ##
161 | [EqdepFacts]##.
162 | But here, we can avoid this axiom.
163 |
164 | Indeed, I define an ad-hoc inversion lemma for [exec] called
165 | [exec_inversion] below. This lemma is directly derived from the
166 | notion of weakest liberal precondition: [aux_wlp] is an other
167 | alternative definition of [wlp].
168 |
169 | *)
170 | Definition aux_wlp (prog: ImpProg) : Pred -> Pred
171 | := fun post e =>
172 | match prog with
173 | | Iskip => post e
174 | | (Iset A x expr) => post (E.upd x (E.eval expr e) e)
175 | | (Iif cond p1 p2) =>
176 | forall e', exec e (if E.eval cond e then p1 else p2) e'
177 | -> post e'
178 | | (Iseq p1 p2) => forall e1 e2, exec e p1 e1 -> exec e1 p2 e2 -> post e2
179 | | (Iwhile cond p) => forall e', exec e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e' -> post e'
180 | end.
181 |
182 | (** This lemma is my inversion lemma of [exec]. It expresses the "soundness" of [aux_wlp]. *)
183 | Lemma exec_inversion:
184 | forall prog e e', (exec e prog e') -> forall post, (aux_wlp prog post e) -> post e'.
185 | Proof.
186 | induction 1; simpl;
187 | try ((firstorder eauto with hoare); fail).
188 | Qed.
189 |
190 | (** Here is the case, where the previous lemma is better than the standard inversion of Coq. *)
191 | Lemma exec_test_inversion:
192 | forall A (x:E.Var A) expr e e',
193 | (exec e (Iset x expr) e') -> e'=(E.upd x (E.eval expr e) e).
194 | Proof.
195 | intros A x expr e e' H.
196 | (** Here, try "[inversion H]" instead the tactic below.
197 | The generated goal is not directly provable. *)
198 | pattern e'; apply (exec_inversion H); simpl; auto.
199 | Qed.
200 |
201 | (** Below, a little tactic to helps in applying [exec_inversion]. *)
202 | Ltac exec_inversion H :=
203 | match type of H with
204 | | (exec ?e ?p ?e') => pattern e'; apply (exec_inversion H); simpl; clear H
205 | end.
206 |
207 | (** ** The programming language is deterministic
208 |
209 | This property is probably not necessary to prove the correctness of my
210 | variant, but it simplifies the proof a lot.
211 |
212 | This lemma is a trivial induction over the first [exec] derivation,
213 | provided the ad-hoc inversion tactic on the second [exec] derivation.
214 | *)
215 | Lemma exec_deterministic: forall ei p ef,
216 | (exec ei p ef) -> forall ef', (exec ei p ef') -> ef=ef'.
217 | Proof.
218 | induction 1; intros ef' X; exec_inversion X; eauto.
219 | (* - case [Iseq] *)
220 | intros e1 e2 X1 X2; assert (X3: e'=e1); auto.
221 | subst; auto.
222 | Qed.
223 |
224 | (** ** Definition of the variant
225 | Given a program [p] and a boolean expression [cond], the relation on environment
226 | "[reduces cond p]" is the variant required by "[synt_wp (Iwhile cond p)]".
227 |
228 | I prove below that this relation is well-founded.
229 | *)
230 | Definition reduces cond p e1 e0 :=
231 | (E.eval cond e0)=true /\ (exec e0 p e1) /\ exists ef, (exec e1 (Iwhile cond p) ef).
232 |
233 | (** To prove that "[reduces cond p]" is well-founded, I want to count
234 | the number of execution of [p] in the computation of "[Iwhile cond p]".
235 | Indeed, as the language is deterministic, this number is unique.
236 |
237 | Hence, "[execn n e (Iwhile cond p) e']" means that "[exec e (Iwhile cond p) e']"
238 | in a sequence of [n] execution of [p].
239 | *)
240 | Inductive execn: nat -> E.Env -> ImpProg -> E.Env -> Prop :=
241 | | execn_Iskip:
242 | forall e, (execn 0 e Iskip e)
243 | | execn_Iset:
244 | forall (A:Type) e x (expr: E.Expr A),
245 | (execn 0 e (Iset x expr) (E.upd x (E.eval expr e) e))
246 | | execn_Iif:
247 | forall n e (cond: E.Expr bool) p1 p2 e',
248 | (execn n e (if (E.eval cond e) then p1 else p2) e')
249 | -> (execn n e (Iif cond p1 p2) e')
250 | | execn_Iseq:
251 | forall n e p1 p2 e' e'',
252 | (exec e p1 e')
253 | -> (execn n e' p2 e'')
254 | -> (execn n e (Iseq p1 p2) e'')
255 | | execn_Iwhile:
256 | forall n e cond p e',
257 | (execn n e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e')
258 | -> (execn (S n) e (Iwhile cond p) e').
259 |
260 | Global Hint Resolve execn_Iskip execn_Iset execn_Iif execn_Iseq execn_Iwhile: hoare.
261 |
262 | Lemma exec_execn: forall ei p ef,
263 | (exec ei p ef) -> (exists n, execn n ei p ef).
264 | Proof.
265 | induction 1; firstorder (eauto with hoare).
266 | Qed.
267 |
268 |
269 | (** In the proof below, I mainly use that "[reduces cond p e1 e0]"
270 | implies that there exists [n] and [ef] such that "[execn (S n) e0 (Iwhile cond p) ef]"
271 | and "[execn n e1 (Iwhile cond p) ef]".
272 | *)
273 | Lemma reduces_wf: forall cond p, well_founded (reduces cond p).
274 | Proof.
275 | unfold well_founded.
276 | intros cond p e0; apply Acc_intro.
277 | intros e1 H; unfold reduces in H.
278 | decompose [ex and] H; clear H.
279 | clear H2 H0 e0.
280 | case (exec_execn H1).
281 | intros n.
282 | generalize cond p e1 x; clear cond p e1 x H1.
283 | elim n.
284 | (* case 0 *)
285 | intros cond p e0 e1 H; inversion_clear H.
286 | (* recursive case *)
287 | clear n; intros n HR cond p e0 e1 H.
288 | inversion_clear H.
289 | inversion_clear H0.
290 | set (b:=E.eval cond e0) in * |-.
291 | cut (E.eval cond e0=b); auto.
292 | generalize H; clear H; case b; simpl.
293 | (* case cond=true *)
294 | intros H;
295 | inversion_clear H.
296 | intros;
297 | apply Acc_intro.
298 | intros e2 H3; unfold reduces in H3.
299 | intuition.
300 | rewrite (exec_deterministic H3 H0); eauto.
301 | (* case cond=false *)
302 | intros H H0; apply Acc_intro.
303 | unfold reduces; rewrite H0.
304 | intuition.
305 | discriminate.
306 | Qed.
307 | Global Hint Resolve reduces_wf: hoare.
308 |
309 | (** * Completeness
310 |
311 | The proof of completeness proceeds by induction over [prog] syntax.
312 |
313 | *)
314 | Lemma wp_complete: forall prog post, prog [= post =] |= (synt_wp prog post).
315 | Proof.
316 | unfold wp.
317 | intros prog post e H; case H; clear H.
318 | intros e' H; case H; clear H.
319 | generalize post e e'; clear post e e'; elim prog; clear prog; simpl.
320 | (* - case [Iskip] *)
321 | intros post e e' H; exec_inversion H; auto.
322 | (* - case [Iset] *)
323 | intros A v expr post e e' H; exec_inversion H; auto.
324 | (* - case [Iif] *)
325 | intros cond p1 Hp1 p2 Hp2 post e e' H; exec_inversion H.
326 | case (E.eval cond e); simpl; firstorder auto || discriminate.
327 | (* - case [Iseq] *)
328 | intros p1 Hp1 p2 Hp2 post e e' H.
329 | exec_inversion H.
330 | eauto.
331 | (* - case [Iwhile] *)
332 | intros cond p Hp post e e' H H0.
333 | constructor 1 with (x:=wp (Iwhile cond p) post).
334 | constructor 1 with (x:=reduces cond p).
335 | unfold wp; (intuition eauto with hoare);
336 | dec2 e1 H1;
337 | case H1; clear H1; intros H1;
338 | exec_inversion H1;
339 | intros e2 H1; exec_inversion H1;
340 | rewrite H2; intros e3 H1; exec_inversion H1;
341 | unfold reduces; eauto with hoare.
342 | Qed.
343 |
344 | (** * Combining the previous results with transitivity of [ |= ] *)
345 |
346 | Global Hint Resolve wp_complete wp_sound: hoare.
347 |
348 | Theorem soundness: forall pre p post, pre |= (synt_wp p post) -> pre |= p [=post=].
349 | Proof.
350 | auto with hoare.
351 | Qed.
352 |
353 | Theorem completeness: forall pre p post, pre |= p [=post=] -> pre |= (synt_wp p post).
354 | Proof.
355 | intuition auto with hoare.
356 | Qed.
357 |
358 |
359 | End TotalHoareLogic.
360 |
361 | (** "Tutorial on Hoare Logic" Library. Copyright 2007 Sylvain Boulme.
362 |
363 | This file is distributed under the terms of the
364 | "GNU LESSER GENERAL PUBLIC LICENSE" version 3.
365 | *)
366 |
--------------------------------------------------------------------------------