├── .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 | --------------------------------------------------------------------------------