Hoare Logic and VDM: Machine-Checked Soundness and Completeness Proofs

Thomas Kleymann (formerly Schreiber)

Doctor of Philosophy University of Edinburgh

Abstract

Investigating soundness and completeness of verification calculi for imperative programming languages is a challenging task. Many incorrect results have been published in the past. We take advantage of the computer-aided proof tool Lego to interactively establish soundness and completeness of both Hoare Logic and the operation decomposition rules of the Vienna Development Method (VDM) with respect to operational semantics. We deal with parameterless recursive procedures and local variables in the context of total correctness. As a case study, we use Lego to verify the correctness of Quicksort in Hoare Logic.

As our main contribution, we illuminate the rôle of auxiliary variables in Hoare Logic. They are required to relate the value of program variables in the final state with the value of program variables in the initial state. In our formalisation, we reflect their purpose by interpreting assertions as relations on states and a domain of auxiliary variables. Furthermore, we propose a new structural rule for adjusting auxiliary variables when strengthening preconditions and weakening postconditions. This rule is stronger than all previously suggested structural rules, including rules of adaptation.

With the new treatment, we are able to show that, contrary to common belief, Hoare Logic subsumes VDM in that every derivation in VDM can be naturally embedded in Hoare Logic. Moreover, we establish completeness results uniformly as corollaries of Most General Formula theorems which remove the need to reason about arbitrary assertions.

Acknowledgements

Working towards a thesis is much more demanding than I had initially anticipated. I am grateful to my colleagues and friends who have succeeded in providing a pleasant environment to work and live in. Special thanks go to my wife Beate for her patience and support.

I would like to thank my supervisors Rod Burstall and Paul Jackson. Rod’s advice has been invaluable in many respects. I have been very much enjoying discussions with him and have always left his office in good spirit. Paul has very much influenced the presentation of the thesis. He has also done a good job in pressuring me to finish.

Discussions with Healf Goguen, Martin Hofmann, Zhaohui Luo and James McKinna have been very stimulating. In fact, a remark by Healf has inspired me to investigate soundness and completeness in the first place. I would also like to thank the departmental computing support staff for providing a friendly and effective service.

I am in debt to Peter Otto for his generous support in the initial stage of this thesis. I would also like to acknowledge the financial support of the Deutsche Forschungsgemeinschaft (Sonderforschungsbereich 182, Mutliprozessor- und Netzwerkkonfigurationen), EPSRC, the British Council (ARC Project Co-Development of Object-Oriented Programs in Lego) and the European Commission (Marie Curie Fellowship).

Declaration

I declare that this thesis was composed by myself and that the work contained therein is my own, except where explicitly stated otherwise in the text. Some of this work has been published previously (Schreiber 1997).

(Thomas Kleymann)

Contents

Chapter 1 Introduction 1 1.1 Soundness and Completeness . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Main Outcome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 The Logical Framework . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.3.1 Dependent Function Types . . . . . . . . . . . . . . . . . . . 4 1.3.2 Inductively Defined Data Types . . . . . . . . . . . . . . . . 5 1.3.3 Inductively Defined Relations . . . . . . . . . . . . . . . . . 5

1.4 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

1.4.1 Implicit Quantification . . . . . . . . . . . . . . . . . . . . . 7 1.5 Overview of Thesis . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

Chapter 2 Fundamental Aspects of Hoare Logic and VDM 9 2.1 Deep and Shallow Embedding Techniques . . . . . . . . . . . . . . . 10 2.2 Program Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3 The State Space . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.4 Interpretations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.5 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.6 Imperative Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.6.1 Assignment and Arrays . . . . . . . . . . . . . . . . . . . . . 15 2.7 Structural Operational Semantics . . . . . . . . . . . . . . . . . . . . 16 2.8 Low-Level Verification . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.9 The Vienna Development Method . . . . . . . . . . . . . . . . . . . 20

2.9.1 Mechanisation . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.9.2 High-Level Verification . . . . . . . . . . . . . . . . . . . . . 23 2.9.3 Soundness . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.9.4 Completeness . . . . . . . . . . . . . . . . . . . . . . . . . . 26

2.10 Hoare Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.10.1 Verification Calculus . . . . . . . . . . . . . . . . . . . . . . 30 2.10.2 Soundness and Completeness . . . . . . . . . . . . . . . . . 32

2.10.3 Auxiliary Variables . . . . . . . . . . . . . . . . . . . . . . . 34 2.11 Hoare Logic subsumes VDM . . . . . . . . . . . . . . . . . . . . . . 42 2.12 Completeness: What does it really mean? . . . . . . . . . . . . . . . 46 2.13 Related Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

2.13.1 A Rigorous Treatment of Auxiliary Variables . . . . . . . . . 50 2.13.2 Machine-Checked Developments . . . . . . . . . . . . . . . 51

Chapter 3 Recursive Procedures and Local Program Variables 57

3.1 Parameterless Recursive Procedures . . . . . . . . . . . . . . . . . . 58 3.1.1 Declaring and Invoking Procedures . . . . . . . . . . . . . . 58 3.1.2 Structural Operational Semantics . . . . . . . . . . . . . . . 59 3.1.3 Hoare Logic with Contexts . . . . . . . . . . . . . . . . . . . 59 3.1.4 Total Correctness . . . . . . . . . . . . . . . . . . . . . . . . 62 3.1.5 Soundness . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 3.1.6 Completeness . . . . . . . . . . . . . . . . . . . . . . . . . . 65 3.1.7 An Example Derivation . . . . . . . . . . . . . . . . . . . . 69 3.1.8 Rules of Adaptation . . . . . . . . . . . . . . . . . . . . . . 71 3.1.9 Mutually Recursive Procedures . . . . . . . . . . . . . . . . 73 3.1.10 The Vienna Development Method . . . . . . . . . . . . . . . 75

3.2 Local Program Variables . . . . . . . . . . . . . . . . . . . . . . . . 75 3.2.1 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . 77 3.2.2 States with Dynamic Types . . . . . . . . . . . . . . . . . . . 78 3.2.3 Programs with Dynamic Sorts . . . . . . . . . . . . . . . . . 80 3.2.4 Hoare Logic . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.2.5 The Vienna Development Method . . . . . . . . . . . . . . . 84

3.3 Recursive Procedures and Static Binding . . . . . . . . . . . . . . . . 85 3.3.1 Static versus Dynamic Binding . . . . . . . . . . . . . . . . . 87 3.3.2 States and Environments . . . . . . . . . . . . . . . . . . . . 88 3.3.3 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 3.3.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 3.3.5 The Programming Language . . . . . . . . . . . . . . . . . . 91 3.3.6 Structural Operational Semantics . . . . . . . . . . . . . . . 91 3.3.7 Hoare Logic . . . . . . . . . . . . . . . . . . . . . . . . . . 98 3.3.8 The Vienna Development Method . . . . . . . . . . . . . . . 110

3.4 Parameter Passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Chapter 4 Case Study: Quicksort 113

4.1 A Theory of Sorted Arrays . . . . . . . . . . . . . . . . . . . . . . . 114 4.1.1 Ordered Lists . . . . . . . . . . . . . . . . . . . . . . . . . . 114 4.1.2 Subarrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 4.1.3 High-Level Correctness Argument . . . . . . . . . . . . . . . 116

4.2 Specification of Quicksort . . . . . . . . . . . . . . . . . . . . . . . 117

4.2.1 Mechanisation . . . . . . . . . . . . . . . . . . . . . . . . . 119 4.3 A Guided Tour of the Verification Proof . . . . . . . . . . . . . . . . 121

4.3.1 Verification of Quicksort Skeleton . . . . . . . . . . . . . . . 122 4.3.2 Verification of the Preparefragment . . . . . . . . . . . . . . 125

4.4 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

Chapter 5 Conclusions and Future Work 130 5.1 Syntax of Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . 130 5.2 The Rôle of Auxiliary Variables in Hoare Logic . . . . . . . . . . . . 131 5.3 Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

Appendix A Some Remarks on Working with the Lego System 133 A.1 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 A.2 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 A.3 Well-Founded Relations . . . . . . . . . . . . . . . . . . . . . . . . 135 A.4 Equality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 A.5 Updating Dependent Functions . . . . . . . . . . . . . . . . . . . . . 137

Appendix B Outline of LEGO Scripts 140 B.1 Simple Imperative Programs . . . . . . . . . . . . . . . . . . . . . . 140 B.2 Recursive Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . 156 B.3 Local Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 B.4 Recursive Procedures and Static Binding . . . . . . . . . . . . . . . . 182 B.5 Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 B.6 Additional Background Theory . . . . . . . . . . . . . . . . . . . . . 235

Bibliography 241

Index 249

Chapter 1

Introduction

The correctness of software is an important issue in software engineering. In particular, in safety-critical applications e.g., controlling chemical plants, it must be ensured that the implementation satisfies the required specification. Often, it is insufficient to validate the correctness of a complex software system by relying merely on test data. Delivering the program together with a formal proof of its correctness with respect to some specification guarantees a correct program, whereas tests can only reduce errors.

In 1969, Hoare introduced a formal system, now referred to as Hoare Logic, relating imperative programs with two assertions, both first-order logical formulae. The purpose of Hoare Logic was twofold. On the one hand, it axiomatised the semantics of the programming language. Occasionally, one therefore finds axiomatic semantics as a synonym for Hoare Logic. On the other hand, objects of the formal system correspond to correctness formulae describing properties of programs. Thus, derivations in Hoare Logic are verification proofs. Nowadays, taking Hoare Logic rules as axiomatic definitions of programming languages is no longer seen as being adequate. Instead, one investigates soundness and completeness with respect to a simpler semantics, such as operational semantics.

Much attention was devoted to this line of research in the 70s and early 80s. However, the field appeared to run into insurmountable problems. Incompleteness results seemed to indicate that it would be impossible to design any verification calculus in the style of Hoare Logic as soon as one combines more complex language features. Moreover, conducting soundness and completeness proofs for more interesting programming languages turned out to be highly error-prone and laborious. Many of the proposed verification calculi were unsound or incomplete despite being backed up by “proofs” to the contrary.

For example, Sokołowski’s (1977) presentation for recursive procedures is incomplete. Apt’s (1981) extension yields a complete but, unfortunately, unsound calculus. The first sound and complete verification calculus in the style of Hoare Logic for recursive procedures and total correctness was published in 1990 by America & de Boer.

1.1 Soundness and Completeness

Soundness is essential. If a system is unsound, deriving a property for a particular program within the formal system does not guarantee that the program actually fulfils the property.

In an incomplete formal system, one may only verify a strict subset of all true formulae. A naïve definition of completeness is bound to fail in the context of verification calculi. On the one hand, if the chosen underlying logical language is too weak, e.g., pure first-order logic together with the boolean constants false and true , some intermediate assertions cannot be expressed. Hence, derivations cannot be completed. On the other hand, if the logical language is too strong, e.g. Peano Arithmetic, it itself is already incomplete and the verification calculus inherits incompleteness.

To circumvent this dilemma, Cook (1978) proposed that instead one should investigate relative completeness. One merely considers sufficiently expressive underlying logical languages. Furthermore, the formal system is augmented by a theory^{1} of firstorder logic. Whenever a rule is protected by a first-order logic side-condition, the rule is already applicable when the side-condition is an element of the theory, and not only when the side-condition is itself derivable.

In practice, achieving relative completeness of verification calculi is highly desirable. In logic, finding valid formulae which cannot be derived is often somewhat esoteric. A different story has to be told for the notion of relative completeness in verification calculi e.g., in Sokołowski’s (1977) calculus, it is very difficult to come up with any non-contrived correctness formula of a recursive procedure which can be derived!

1.2 Main Outcome

We have produced machine-checked soundness and completeness proofs for Hoare Logic and the operation decomposition rules of the Vienna Development Method (VDM). Our imperative programming language includes (parameterless) recursive procedures and local variables. We consider static binding and total correctness. Previously, machine-checked completeness results have only been available for simple imperative programs (and partial correctness).

1the set of all valid as opposed to derivable formulae

Our message to the designers and researchers of verification calculi is that conducting computer-aided soundness and completeness proofs is both a feasible and profitable task. We are confident that when investigating further language features, the overhead caused by mechanisation is tolerable. Today’s modern proof tools are sufficiently advanced. Besides Lego, one could for example, also resort to the systems Coq (1998), HOL (1998), Isabelle (Paulson & Nipkow 1998) or PVS (Rushby 1998).

Conducting formal proofs at a level that they can be checked by a machine requires that all aspects and all details must be thoroughly investigated. To be a feasible task, it is often necessary to first simplify the concepts themselves, before establishing the desired properties. Hence, in addition to more confidence in the mechanised proofs, one can often contribute by simplifying or illuminating some aspects of the area investigated.

Our fundamental contribution has been to highlight the rôle of auxiliary variables in Hoare Logic. Usually, assertions are interpreted as predicates on states where free variables denote the value of program variables in a specific state. Variables for which no counterpart appears as a program variable in the program under consideration then take on the rôle of auxiliary variables. They are required to relate the value of program variables in different states.

Our view of assertions emphasises the pragmatic importance of auxiliary variables. We have followed a proposal by Apt & Meertens (1980) to consider assertions as relations on states and auxiliary variables. Furthermore, we stipulate a new structural rule to adjust auxiliary variables when strengthening preconditions and weakening postconditions. This rule is stronger than all previously suggested structural rules, including Hoare’s (1969) consequence rule and rules of adaptation. As a direct consequence of the new treatment of auxiliary variables,

• we were able to show that Sokołowski’s (1977) calculus is sound and complete if one replaces Hoare’s rule of consequence with ours. In particular, none of the other structural rules introduced by Apt (1981) are required.

• We have clarified the relationship between Hoare Logic and its variant VDM. We were able to show that, contrary to common belief, VDM is more restrictive than Hoare Logic in that every derivation in VDM can be naturally embedded in Hoare Logic. Intuitively, in Hoare Logic, auxiliary variables record the value of variables at an arbitrary reference point. In VDM, the reference point corresponds to the state of the precondition and must therefore be constantly adjusted during a derivation.

• Completeness is established by induction on the structure of programs. However, in the presence of recursive procedures, a direct proof does not go through, because the induction hypothesis is then too weak. Instead one first establishes, also by induction, that a particular correctness formula, the Most General Formula (MGF), can be derived. This obliterates the problems of having to cater for arbitrary assertions. Completeness follows as a corollary by appealing to structural rules.

This technique had previously only been applied to Hoare Logic dealing with recursive procedures. We have conducted all completeness proofs with the help of the MGF property, because, with our new rule of consequence, completeness follows directly from the MGF theorem.

The new approach has already been taught to undergraduate students as part of Hofmann’s (1997) course on semantics and verification at the University of Marburg.

1.3 The Logical Framework

We have employed the computer-aided proof tool Lego (1998) to investigate soundness and completeness for Hoare Logic and the operation decomposition rules of VDM. Lego is a proof assistant based on type theory which provides a higher-order (intuitionistic) logic. The system supports dependent types, inductively defined data types and inductively defined relations. The underlying type theory is UTT (Luo 1994). It is not necessary to be familiar with any proof assistant or type theory in order to read this thesis.

In our formalisation, programs may contain variables with arbitrary sorts. This leads to a more complex model of the state space. We have found it helpful to employ dependent function types in order to model the state space.

We define the syntax of programs as an inductive data type. We capture operational semantics and the notion of derivability of correctness formulae in a verification calculus as an inductive relation. This allows us to conduct (completeness) proofs by induction on the structure of programs and (soundness) proofs by induction on the derivation of correctness formulae.

We now introduce dependent function types, inductively defined data types and inductively defined relations in more detail.

1.3.1 Dependent Function Types

In type theory, every term is annotated by its type. Only well-typed terms are wellformed e.g., f : T → U is a function which, given x : T yields f(x) : U. We may only apply f to elements of type T. We will however omit such annotations in our presentation when the typing discipline is evident from the context.

UTT also provides dependent types in which types themselves are functions sort: T → Type. One writes ∏x: T· sort(x) rather than T → sortto denote a dependent function space. Let f : ∏x: T·sort(x). Given an argument x: T, the function f returns a value of type sort(x). Dependent function types are particularly useful when one wants to specify a function in which the type of some arguments depends on the value of previous arguments.

Example 1.1 (Scalar Product on Reals) With dependent functions, one can implement a single function

f(n)(x⃗,y⃗) = ∑_{1≤i≤n} x_{i}× y_{i}

to compute the scalar product of two vectors x⃗,y⃗: ℝ^{n} of arbitrary but identical dimension n: nat. Its type is

f : ∏n: nat· (ℝ^{n}× ℝ^{n}) → ℝ .

1.3.2 Inductively Defined Data Types

Consider the BNF grammar

S::= x:= t | while b do S

of a (very) simple imperative programming language. This is an inductive definition. We may prove properties about arbitrary programs by considering its two constructors for assignments and loops. Formally, we may appeal to the induction principle

\frac{∀x· ∀t· Φ(x:= t) ∀b· ∀S· Φ(S) ⇒ Φ(while b do S)}{∀S· Φ(S)}

for an arbitrary predicate Φ on programs.

1.3.3 Inductively Defined Relations

Let σ \overline{S ^{}}τ be a new relation to describe the operational semantics of the above programming language. Intuitively, it denotes that the program S when invoked in the state σ will terminate in the state τ. We define it by the least relation satisfying

σ  \overline{x:= t ^{}}σ[x ↦ eval(σ)(t)]  (1.1)
σ \overline{while b do S_{}}σ provided eval(σ)(b) = false .  (1.2)
[figure: σ; S; η; η; while b do S; τ; σ; while b do S; τ] provided eval(σ)(b) = true . (1.3)

This is an example of an inductively defined relation. Given a proof obligation of the form

∀σ · ∀S· ∀τ· σ  \overline{S ^{}}τ⇒ Φ(σ,S,τ)  (1.4)

we may pursue induction by considering all (three) possible derivations^{2}. Formally, the induction principle is

\frac{∀σ · ∀x· ∀t· Φ(σ,x:= t,σ[x ↦ eval(σ)(t)]) ∀σ · ∀b· ∀S· eval(σ)(b) = false ⇒ Φ(σ,while b do S,σ) ∀σ · ∀b· ∀S· ∀η,τ · (eval(σ)(b) = true ∧ σ  \overline{S ^{}}η ∧ η \overline{while b do S_{}}τ∧ Φ(σ,S,η) ∧ Φ(η,while b do S,τ)) ⇒ Φ(σ,while b do S,τ)}{∀σ · ∀S· ∀τ· σ  \overline{S ^{}}τ⇒ Φ(σ,S,τ)}

(1.5)

Computer-aided tools are very helpful in formally dealing with such induction principles. The Lego system generates induction principles automatically from the set of defining constructors, such as (1.1)–(1.3). It also provides an Induction tactic to bring the proof obligation into the shape of (1.4)before breaking it down into the cases according to the induction principle.

In practice, inversion is an important proof technique for dealing with inductively defined relations. Consider the proof of determinism. We want to show

∀σ · ∀S· ∀τ· σ  \overline{S ^{}}τ⇒ ∀η · σ  \overline{S ^{}}η ⇒ τ = η .

The Induction tactic splits the proof obligation into the three cases corresponding to the induction principle (1.5). We only consider the base case where we have to establish

∀η · σ \overline{x:= t ^{}}η ⇒ σ[x ↦ eval(σ)(t)]= η .

2On paper, such proofs sometimes appeal to induction on the length of a derivation. In a rigorous formal proof, it is (technically) more elegant to inductively analyse the derivation itself.

Rather than general induction, it suffices to consider the last step of the derivation of

σ  \overline{x:= t ^{}}η  (1.6)

In other words, we want to invert the derivation. Intuitively, (1.6) can only be derived from the axiom (1.1). Hence, the state η must coincide with σ[x ↦ eval(σ)(t)]. Lego provides state of the art support for inverting inductively defined relations (McBride 1998).

1.4 Notation

We refrain from exposing the reader to the (rather unreadable) Lego scripts in the main body of this thesis. Instead, we adopt a more traditional informal presentation with more flexible notation. We include sufficient details to facilitate encoding our results with different proof tools. We have decorated machine-checked developments with the symbol . An outline of the Lego scripts can be found in Appendix B. All scripts are also available on-line and can be accessed from Lego’s (1998) World Wide Web page.

The reader is encouraged to compare definitions in the text with the actual Lego scripts (Pollack 1998). Machine-checked developments are not exempt from mistakes e.g., Homeier & Martin (1996) erroneously claim

“ The correctness of these VCG functions is established by proving the following theorems from the axioms and rules of inference of the axiomatic semantics ”

But the actual encodings in HOL reveal that correctness has instead been established by appealing to the definition of operational semantics.

1.4.1 Implicit Quantification

An important convention is that all free identifiers in a rule (or axiom) are implicitly universally quantified at the level of the rule. For example, the rule

∀t : W· {p ∧ b ∧ u = t} S {p ∧ u < t}

{p} while b do S {p ∧ ¬b}

is merely a convenient short cut for

∀p · ∀u· ∀b· ∀S· \frac{∀t : W· \{p ∧ b ∧ u = t\} S \{p ∧ u < t\}}{\{p\} while b do S \{p ∧ ¬b\}}

where the order of the top-level universal quantifiers does not matter. In particular, scoping guarantees^{3} that the variable t can not occur freely in p, u, b and S.

3The rule would be unsound otherwise.

1.5 Overview of Thesis

In Chapter 2, restricting our attention to simple imperative programs, we consider fundamental aspects of verification calculi such as Hoare Logic and VDM. We axiomatise the meaning of programs via a structural operational semantics. We then demonstrate that reasoning based directly on this semantic account is tedious. A more abstract approach leads naturally to the operation decomposition rules of VDM.

Turning our attention to Hoare Logic, we discuss the rôle of auxiliary variables in detail. Based on this analysis, we interpret assertions as relations on the state space and a domain of auxiliary variables. Moreover, we introduce a new structural rule to adjust auxiliary variables when strengthening preconditions and weakening postconditions.

We mechanically establish soundness and completeness for both Hoare Logic and VDM. We also show that Hoare Logic subsumes VDM and take a closer look at the definition of (relative) completeness. At the end of this chapter, we compare our approach to related work.

Chapter 2 exposes the main ideas of this thesis. In Chapter 3, we apply the techniques to more intricate settings. America & de Boer (1990) presented the first sound and complete formal system for recursive procedures dealing with termination in the style of Hoare Logic. We show that after replacing Hoare’s (1969) rule with our new consequence rule, a third of their rules are not required; neither pragmatically, nor to achieve completeness. Furthermore, we extend our results to imperative programs supporting local variables (with static scoping) in the presence of recursive procedures.

Our meta-theoretical investigations of Hoare Logic and VDM are complemented by a case-study in Chapter 4. Focussing on the well-understood Quicksort algorithm, we hope to clarify how our new Hoare Logic rules may be used in practice.

In Chapter 5, we conclude and outline future work. All of our results have been developed using the Lego proof tool. We comment on the feasibility of this project and on the strengths and weaknesses of the Lego system.

Appendix A contains some further remarks on working with the Lego system. In particular, we document how to encode a notion of well-founded relations and extensional equality. We also show how to update dependent functions and hence machine states; this requires quite sophisticated type theory. In Appendix B, we have collected an outline of the actual Lego scripts.

Fundamental Aspects of Hoare Logic and VDM

In this chapter, we introduce syntax and an operational semantics for a simple imperative programming language with assignments, conditionals and loops. We then present two verification calculi, VDM and Hoare Logic, and show that they are sound and complete with respect to the operational semantics. We consider total correctness i.e., besides input/output specifications, the verification calculi also deal with termination of programs.

The outline of the chapter is as follows. Having built up sufficient background theory, we consider the correctness proof of an imperative program realising the exponential function. This proof is based directly on the definition of the operational semantics. As this is rather cumbersome, we motivate a more abstract framework, VDM. We then carefully introduce the issues of soundness and completeness. We prove soundness, as usual, by induction on the derivation of the correctness formula. In our completeness proof, we are able to demonstrate that one may factor out details of assertions and adapt Gorelick’s (1975) Most General Formula (MGF) theorem to the VDM setting.

Turning our attention to Hoare Logic, we discuss the rôle of auxiliary variables and propose to interpret assertions as relations on the state space and additionally a domain of auxiliary variables. Furthermore, we present a new rule of consequence. It is strictly stronger than Hoare’s (1969) version and allows us to prove completeness as a trivial corollary of the MGF theorem. With the new treatment, we are able to show that derivations in VDM can be embedded in Hoare Logic.

We conclude with a summary of related work.

2.1 Deep and Shallow Embedding Techniques

Our main objective of this thesis is to mechanically establish meta-theorems such as soundness and completeness of verification calculi. It is not our aim to show that a proof tool such as the Lego system is suitable to verify concrete programs relative to concrete specifications.

Traditionally, one defines syntax for expressions and relative to this setup, one characterises syntax of a programming language and syntax of an assertion language. Then, one describes the meaning of every syntactic construct. This approach is known as deep embedding. Alternatively one may shortcut this process and identify the syntactic representation with its denotation. This technique is known as shallow embedding.

The drawbacks of shallow embeddings are that

• one cannot exploit the inductive (syntactic) structure to prove properties

• the representation of concrete examples is often more difficult to comprehend

However, omitting syntactic representation cuts down the work load and is therefore often the preferred technique.

We will present a shallow embedding of expressions. Based on this definition, we will give a deep embedding of the programming language and a shallow embedding of the assertion language. A shallow embedding of the imperative programming language is inappropriate for our project, because the completeness proofs inherently rely on proofs by induction on the structure of programs.

A shallow embedding of Hoare Logic and VDM suffices to mechanically investigate soundness (Gordon 1989). However, to formalise the statement of completeness, one needs to explicitly refer to a notion of derivability. Thus, we give a deep embedding of Hoare Logic and VDM.

The main benefit of shallow embedding for our project has been that one does not have to worry about substitutions in assertions.

2.2 Program Variables

In practice, program variables are strings, sometimes restricted to a particular length. Strings are not directly supported in our logical framework. Instead of adopting an isomorphic structure e.g., lists over the finite set 0 – 255, we chose the most general structure. Any type will do as long as equality is decidable.

p. 236 Definition 2.1 (Program Variables) The syntactic class of program variables VAR may be an arbitrary type, provided it supports an associated decidable equality i.e., a boolean relation eq: (VAR× VAR) → bool which reflects equality

∀x,y: VAR· x = y ⇔ eq(x,y) = true .

We will investigate soundness and completeness relative to such a general notion of program variables. In logical frameworks supporting classical logic, the restriction to decidable equalities can be dropped. For concrete examples, we employ (finite) enumeration types VAR = (x,y,...).

Previous mechanisations only consider the case of a single data type, namely natural numbers. In a type-theoretic setting, it seems natural to investigate multiple sorts. For most data types present in imperative programming languages, we can immediately provide type-theoretic representations.

• Sum types yield finite data types e.g., booleans and characters.

• Inductive types yield data types such as natural numbers and lists.

• Employing quotients, we can construct integers from natural numbers and rationals from integers.

• Record types subsume records in programming languages.

• Using functions we may represent arrays. In particular, following the views of Dahl, Dijkstra & Hoare (1972) and Gries & Levin (1980), an assignment of an array element is regarded as functional updating.

• The type of program variables VAR can be used to represent pointers.

We identify the universe of data types with the universe of all types expressible in our logical framework. Conceptually, results in the sequel are therefore independent of the actual available data types. Pragmatically, we can study more interesting programming examples involving different data types. The type of variables can be declared by providing a function

sort: VAR → Type .

2.3 The State Space

The state space records the value of every program variable. In the simple scenario of this chapter, there is exactly one entry for every program variable. We will encounter more interesting state spaces in the next chapter when dealing with local variables.

The state space is one of the major ingredients to deal with semantical aspects of imperative programs. When one encounters an assignment x:= t, the semantics of the expression t depends on the value of all program variables occurring in t as recorded by the current state σ. Moreover, executing the assignment leads to a new state in which the entry for the program variable x in the state space has been updated. It suffices to consider first-order substitutions (Trakhtenbrot, Halpern & Meyer 1984) which restrict updating of an entry for x: VAR to values of type sort(x) which cannot refer to values of variables.

As we have seen, a type environment is a function from program variables to types. A state σ for a type environment sortis a function mapping program variables x to values of type sort(x). The state space itself is therefore a dependent function space:

p. 140 Definition 2.2 (State Space) Σ =^{def} ∏x: VAR· sort(x)

Updating (dependent) functions σ[x ↦ v]then corresponds to first-order substitution with x: VAR, v: sort(x) and σ: Σ. Notice that a syntactic substitution such as

σ[x ↦ x + 1]

is not well-typed. Instead, one needs to specify

σ[x ↦ σ(x)+ 1 ] .

In Lego, all functions are total. Thus, our model of the state space has no undefined values. At least for simple data types, such as integers, this is an adequate assumption, since, in the real world, one may access any entry in the memory and interpret it as a value. If one were interested in modelling undefined values for a data type T, one could store values of the sum type T + 1 where the right injection characterises an undefined value.

2.4 Interpretations

An interpretation determines the value of

• constants such as 0, +, ∧

• and (free) variables

in expressions and logical formulae. In the context of program verification, logical variables are assumed to refer to program variables. Therefore, the interpretation I for variables depends on a particular state σ i.e., [[x]](I(σ))= I(σ(x)). In the following chapter, when considering local variables, scoping dictates which program variables in the state space are to be inspected and the interpretation depends additionally on a scoping environment.

In our formalisation, we are only interested in the standard interpretation of constants. This not only simplifies the encoding, it also avoids the problematic issue of how to axiomatise the class of acceptable interpretations, see Sect. 2.12 for further details.

2.5 Expressions

Boolean expressions occur in loops and conditional statements. Other types of expressions depend on the data types expressible in the language and occur both as subexpressions of boolean expressions and in the assignment statement. One may define the syntax of expressions by a BNF grammar.

Example 2.3 (Syntax of Expression) Homeier & Martin (1996) define two classes of expressions

e::= n | x | + + x | e_{1}+ e_{2} | e_{1}− e_{2}
b::= e_{1} = e_{2} | e_{1} < e_{2} | b_{1}∧ b_{2} | b_{1}∨ b_{2} | ¬b

We will only consider expressions without side-effects^{1} and do not deal with the expression + + x. The semantics can thus be easily fixed denotationally and is determined by an interpretation function I and a state σ.

\begin{matrix}Example 2.4 (Semantics of Expressions) \\ • \end{matrix} [[n]](I(σ)) =^{def} I(n) [[x]](I(σ)) =^{def} I(σ(x))

• [[e_{1}ope_{2}]](I(σ)) =^{def} [[e_{1}]](I(σ))I(op)[[e_{2}]](I(σ)) for op ∈ {+,−,=,<}

• [[b_{1} op b_{2}]](I(σ)) =^{def} [[b_{1}]](I(σ))I(op)[[b_{2}]](I(σ)) for op ∈ {∧,∨}

• [[¬b]](I(σ)) =^{def} I(¬)[[b]](I(σ))

Whenever we come across a boolean expression in a loop or a conditional statement, we are only interested in the value it evaluates to, true or false. Similarly, in an assignment, we treat evaluation of the expression as atomic, merely a value depending on the state space. We are not interested in syntactic properties such as whether one expression is a subterm of another expression. Ignoring the syntax of expressions paves the way towards a reasonable level of abstraction when investigating properties of verification calculi for imperative programs without side-effects.

1Such a strict distinction between expressions and commands is one of the fundamental principles underlying idealised Algol (Reynolds 1982).

Restricting our attention to the standard interpretation, the semantics of an expression is determined by the state space. We only consider expressions at this semantic level:

p. 140 Definition 2.5 (Expressions) Given an arbitrary type T, we represent expressions by

expression(T : Type)=^{def} Σ → T .

Let e: expression(T) be any expression. Its evaluation depends on a concrete snapshot of the state space σ: Σ We define

eval(σ)(e) =^{def} e(σ) .

The main benefit of adopting shallow embedding is that we do not have to worry about formalising the syntax in a logical framework e.g., the expression x ∗ y where x and y are program variables with sort nat would be rendered as

λσ · σ(x)∗ σ(y): expression(nat) .

Moreover, substitutions are much easier to deal with at the semantic level. Consider the syntactic substitution (x ∗ y)[x ↦ t]. In the shallow embedding, this substitution can be realised by updating the state space i.e.

(λσ · σ(x) ∗ σ(y))∘ (λσ · σ[x ↦ eval(σ)(t)])  (2.1)

which, when applied to a concrete state σ, (β-)reduces to

(σ[x ↦ eval(σ)(t)](x))∗ (σ[x ↦ eval(σ)(t)](y)) .  (2.2)

This is equivalent to

t′∗ σ(y)  (2.3)

where t′is a suitably reduced version of eval(σ)(t). Employing a deep embedding, one would need to additionally check that the syntactic substitution function on expressions is correct e.g., [[(x ∗ y)[x ↦ t]]] must coincide with the term (2.1).

Unfortunately, the Lego system offers no automatic support in reducing (2.2) to (2.3). In concrete examples, such as the Quicksort verification in Cha. 4, this leads to excessively large proof obligations. Moreover, examples are more difficult to read. To increase the readability of examples, we shall present the (manually translated) syntactic counterparts of expressions.

2.6 Imperative Programs

For representing statements of the imperative programming language, we resort to a deep embedding i.e., we first embed the syntax in the logical framework and then axiomatise the semantics by an analysis of the structure of programs. In this chapter, we restrict ourselves to basic language features:

p. 140 Definition 2.6 (Abstract Syntax of Imperative Programs) Imperative programs S: prog are defined by the BNF grammar

S::= skip | x:= t | S_{1}; S_{2} | if b then S_{1} else S_{2} | while b do S

where x: VAR, t : expression(sort(x))and b: expression(bool).

As Lego supports definitions, we may define other, possibly generic, language constructs e.g., if b then S=^{def}if b then S else skip. However, any program S: prog may be brought into normal form e.g., by expanding definitions, so that when pursuing induction on the structure of S, we only need to consider the primitive constructors given in Def. 2.6.

In examples, we assume that sequential composition is left-associative. Furthermore, we employ begin and end in our concrete syntax to clarify precedence.

Notice that Lego actually needs to test equivalence of types to resolve the type dependency in the assignment constructor:

Example 2.7 In the assignment k:= 1, the principal type of 1 is nat. However, the typing discipline for the assignment constructor requires 1 to inhabit sort(k). The typechecker thus needs to reduce sort(k) to nat.

2.6.1 Assignment and Arrays

Data types may be any type representable in type theory. This includes arrays which, following Dahl et al. (1972) and Gries & Levin (1980), one may consider as functions inhabiting T → U, where U is an arbitrary type and T is any type where equality is decidable.

To represent the concrete statement x[c] := x[d]for an array x: VAR with sort(x)= T → U and sort(c)= sort(d)= T, the assignment constructor expects a term inhabiting (∏x: VAR· sort(x))→ T → U as its second argument. Since x represents a function, the statement x[c] := x[d]corresponds to updating the function x at position c i.e.,

x:= λσ: Σ · (σ(x))[σ(c) ↦ σ(x)(σ(d))] .

2.7 Structural Operational Semantics

In this section, we axiomatise the semantics of the imperative programming language. We employ structural operational semantics (Plotkin 1981) which provides a clean way to specify the effect of each language constructor in an arbitrary state. It relates a program with its initial and final state.

p. 141 Definition 2.8 (Semantics) The operational semantics is defined as the least relation

.  \overline{.  ^{}}. ⊆ Σ × prog × Σ satisfying
σ  \overline{skip ^{}}σ  (2.4)
σ  \overline{x:= t ^{}}σ[x ↦ eval(σ)(t)]  (2.5)

[figure: σ; S_{1}; η; η; S_{2}; τ; σ; S_{1}; S_{2}; τ]  (2.6)

[figure: σ; S_{1}; τ; σ; if b then S_{1} else S_{2}; τ] provided eval(σ)(b) = true .  (2.7)

[figure: σ; S_{2}; τ; σ; if b then S_{1} else S_{2}; τ] provided eval(σ)(b) = false .  (2.8)

σ \overline{while b do S_{}}σ provided eval(σ)(b) = false .  (2.9)

[figure: σ; S; η; η; while b do S; τ; σ; while b do S; τ] provided eval(σ)(b) = true . (2.10)

Intuitively, σ \overline{S ^{}}τdenotes that the program S when invoked in the state σ will terminate in the state τ.

We can increase our confidence in the correctness of this axiomatisation by proving properties such as determinism.

p. 142 Lemma 2.9 (Determinism) Program behaviour is deterministic i.e., for all programs S and states η, σ and τ, whenever σ S ^{}τand σ \overline{S ^{}}η, then τ = η. Proof by induction on the derivation of σ \overline{S ^{}}τ, followed by inverting σ \overline{S ^{}}η in each case. □

However, being axiomatic, it is impossible to prove the correctness of the semantic description. We have to inspect each rule carefully.

We believe a denotational^{2} semantics M : (prog × Σ) ⇀ Σ is less suitable to define the behaviour of programs, because it involves more complicated mathematical constructs such as computing least fixed points e.g., Cousot (1990) argues that a denotational account should be justified with respect to an operational semantics. In our opinion, the induction principle triggered by the inductive definition of the operational semantics is sufficiently powerful to effectively investigate soundness and completeness. We do not see the need to deal with denotational semantics.

2.8 Low-Level Verification

Having integrated the semantics of a programming language in a logical framework, we can specify program behaviour and prove specifications correct. In this section, we employ the rules of the structural operational semantics to verify an imperative program implementing exponentiation. Analysing the proof, we uncover redundant steps which suggests that one might benefit from resorting to a set of higher-level proof rules.

As global program variables, we use VAR = {x,y,r} and we assume that all program variables may also take on negative values. More precisely, we declare

fun sort( ) = int .

An implementation then amounts to S_{exp} =^{def} r:= 1; loop_{exp} with

loop_{exp} =^{def} while y ≠ 0 do begin r:= r ∗ x; y:= y − 1 end

A possible specification in our current setup might be

∀σ,τ · (σ  \overline{S_{exp} ^{}}τ) ⇒ τ(r) = σ(x)^{σ(y)}  (2.11)

This is a typical input/output specification (Liskov & Berzins 1986). The state σ captures the initial state, the state τ captures the final state of the program. The proposition τ(r) = σ(x)^{σ(y)} connects the input values of x and y with the output value of r. Notice that connecting the output value of r with the output values of x and y e.g., τ(r) = τ(x)^{τ(y)}, would be an unwise requirement: If S_{exp} terminates, the output value of y amounts to 0 and thus, the program S_{exp} would only satisfy the specification for an initial value of 1 for x.

Specifications of the above shape trivially hold if programs do not terminate. In such a case, there are no states σ, τ satisfying σ \overline{S_{exp} ^{}}τ. Hence, (2.11) only specifies partial correctness. In this thesis, we consider total correctness. The proposition ∀σ ·∃τ·σ \overline{S ^{}}τcharacterises that S terminates on all inputs. In practice, we do not want to insist on termination for all initial states. In particular, we have designed our exponential program in such a way that it only terminates for positive values of y i.e., ∀σ · 0 ≤ σ(y) ⇒ ∃τ· σ \overline{S_{exp} ^{}}τ. Combining the termination condition with (2.11) yields an adequate specification with respect to total correctness:

2To avoid partiality, one may instead use a relational denotational semantics M : prog → 2^{Σ×Σ} .

p. 144 Lemma 2.10 (Correctness of Exponential Program)

∀σ · 0 ≤ σ(y) ⇒ ∃τ· σ \overline{S_{exp} ^{}}τ∧ τ(r) = σ(x)^{σ(y)}

Proof Given a state σ such that

0 ≤ σ(y) ,  (2.12)

we need to find a final state τ such that

σ  \overline{S_{exp} ^{}}τ  (2.13)

and τ(r) = σ(x)^{σ(y)} holds. Applying the rule for sequential composition (2.6), followed by the assignment axiom (2.5) simplifies the first proof obligation (2.13) to

σ[r ↦ 1] \overline{loop_{exp} ^{}}τ .  (2.14)

We have thus reduced the original proof obligation to

∀σ · 0 ≤ σ(y) ⇒ ∃τ· σ[r ↦ 1] \overline{loop_{exp} ^{}}τ∧ τ(r) = σ(x)^{σ(y)} .  (2.15)

Since each execution of the loop body decreases the value of y until it is 0, we pursue induction. For the induction to go through, we have to strengthen the proof obligation (2.15) so that the state σ corresponds to an arbitrary intermediate state of the loop. In particular, it must not be restricted to the case where the value of r amounts to the initial 1. We choose

∀σ · 0 ≤ σ(y) ⇒ ∃τ· σ \overline{loop_{exp} ^{}}τ∧ τ(y)= 0 ∧ τ(r)= σ(r) ∗ σ(x)^{σ(y)} (2.16)

and pursue induction on σ(y). Formally, we derive

∀n· ∀σ · (0 ≤ σ(y) ∧ σ(y) = n) ⇒

∃τ· σ \overline{loop_{exp} ^{}}τ∧ τ(y)= 0 ∧ τ(r)= σ(r) ∗ σ(x)^{σ(y)}

by induction on n.

In the base case, the axiom for loops (2.9) unifies σ with τ and we have to show

σ(r) = σ(r) ∗ σ(r)^{0} .

In the step case, for σ(y) = n + 1 , we need to find a state τ which satisfies

σ loop_{exp} ^{}τ  (2.17)

and

τ(y)= 0 ∧ τ(r)= σ(r) ∗ σ(x)^{σ(y)} .  (2.18)

Unwinding the loop once reduces the proof obligation (2.17) to

η \overline{loop_{exp} ^{}}τ for η = σ[r ↦ σ(r) ∗ σ(x)][y ↦ σ(y) − 1 ].  (2.19)

We may now apply the induction hypothesis with η, because n = η(y). This yields

τ(y)= 0 ∧ τ(r)= σ(r) ∗ σ(x)∗ σ(x)^{σ(y)−1}

which is equivalent to the remaining proof obligation (2.18) for σ(y) > 0.

□

As part of the proof, given an initial state σ satisfying a particular precondition, we needed to find a final state τ such that, according to the rule of the operational semantics, we could derive σ \overline{S ^{}}τ. Furthermore, we had to establish that the postcondition relating σ and τ held.

However, being deterministic, the operational semantics uniquely determines such a final state. In addition, the initial correctness formula, the proof obligation (2.19)and the crucial proof obligation (2.16) are all of the form

∀σ · p(σ) ⇒ ∃τ· σ  \overline{S ^{}}τ∧ q(τ,σ)  (2.20)

for particular instances of p: Σ → Prop , S : prog and q: (Σ × Σ) → Prop . Instead of resorting to inference steps preserving this structure, the above proof has unnecessarily employed introduction and elimination rules for the logical connectives ∀, ⇒ and ∃ to destruct and reconstruct such forms.

In VDM, one focusses on deriving correctness formulae of the form (2.20). Hoare Logic is similar, but postconditions are restricted to predicates rather than binary relations on the state space. One can thus not directly specify input/output behaviour. To compensate for this deficiency, one needs to additionally deal with auxiliary variables. We therefore first consider VDM.

2.9 The Vienna Development Method

The Vienna Development Method (VDM) is a verification calculus which relates programs with pre- and postconditions (Jones 1990, Jones & Shaw 1990). We commence by introducing VDM, in particular its operation decomposition calculus, at an informal level. Employing a shallow embedding of assertions, we then show how one might represent such a verification calculus in a logical framework. Next, we reconsider the above verification proof in VDM before investigating soundness and completeness.

In VDM, assertions are objects of a logic of partial functions (LPF). In the postcondition one may employ hooked program variables \begin{matrix}↼ − \\ x \end{matrix} to refer to the value of the program variable in the initial state. The meaning of a postcondition is determined by an interpretation I(τ,σ) relative to a final state τ and initial state σ. In particular, for a program variable x, we have

[[x]]I(τ,σ)=^{def} I(τ(x))
[[\begin{matrix}↼ − \\ x \end{matrix} ]]I(τ,σ)=^{def} I(σ(x)) .

Hooked notation is extended to expressions and preconditions. Hooked expressions and preconditions may only appear within postconditions. On a syntactic level, one has to hook all free occurring variables. Semantically, let t be an expression and p be a precondition. One then interprets

[[\begin{matrix}↼ − \\ t \end{matrix} ]]I(τ,σ)=^{def} [[t]]I(σ)
[[\begin{matrix}↼ − \\ p \end{matrix} ]]I(τ,σ)=^{def} [[p]]I(σ).

VDM is a formal system for deriving correctness formulae {p} S {q}. Such a triple is interpreted as

∀σ · [[p]]I(σ)⇒ ∃τ· σ \overline{S ^{}}τ∧ [[q]]I(τ,σ)

and thus reflects the shape of correctness formulae as encountered in the above verification example. However, being a formal system which manipulates objects of the form {p} S {q}, VDM guides verification proofs at a more abstract level. Whereas in the above correctness proof, one was confronted with (higher-order) propositions built around a formal system relating programs with states, VDM is a formal system relating programs with relations on states. More precisely, VDM encompasses two orthogonal methods:

1. refining abstract objects into concrete data types available in the programming language, and

2. refining correctness formulae, which allows one to decompose programs.

We will only consider VDM’s operation decomposition rules and, from now on, do no longer distinguish between them and VDM in general.

For every constructor of the imperative programming language, VDM provides a rule which allows one to decompose a program. In particular, the programs mentioned in the premisses are strict subprograms of the programs mentioned in the conclusions. Unlike the operational semantics, this also holds for loops where a well-founded induction argument has been built into the rule.

\{true \} skip \{ ⋀_{v∈VAR} v =\begin{matrix}↼ − \\ v \end{matrix} \}

\{true \} x:= t \{ x =\begin{matrix}↼ − \\ t \end{matrix} ∧ ⋀_{v∈VAR\\{x\}}v =\begin{matrix}↼ − \\ v \end{matrix} \}

{p_{1}} S_{1} {p_{2}∧ r_{1}} {p_{2}} S_{2} {r_{2}}
{p_{1}} S_{1}; S_{2} {r_{2}∘ r_{1}}

Semantically, the postcondition in the conclusion corresponds to relational composition i.e., [[r_{2}∘ r_{1}]]I(τ,σ)=^{def} ∃η · [[r_{1}]]I(η,σ)∧ [[r_{2}]]I(τ,η) . Syntactically, it suffices to existentially quantify intermediate values for all free occurring program variables and substitute them for all free program variables in r_{1} and all hooked program variables in r_{2}.

Example 2.11 (VDM’s Sequential Decomposition Rule) Let

r_{1} =^{def} r = 1 ∧ 0 ≤ y ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ y =\begin{matrix}↼ − \\ y \end{matrix}
r_{2} =^{def} y = 0 ∧\begin{matrix}↼ − \\ r \end{matrix} ∗\begin{matrix}↼ − \\ x \end{matrix}\begin{matrix}↼ − \\ y \end{matrix} = r ∗ x^{y}∧ y ≤\begin{matrix}↼ − \\ y \end{matrix}

Then, r_{2}∘ r_{1} corresponds to

∃x_{i},y_{i},r_{i}· r_{i} = 1 ∧ 0 ≤ y_{i}∧ x_{i} =\begin{matrix}↼ − \\ x \end{matrix} ∧ y_{i} =\begin{matrix}↼ − \\ y \end{matrix} ∧

y = 0 ∧ r_{i}∗ x_{i}^{y_{i}} = r ∗ x^{y}∧ y ≤ y_{i}

which is equivalent to 0 ≤\begin{matrix}↼ − \\ y \end{matrix} ∧ y = 0 ∧ r =\begin{matrix}↼ − \\ x \end{matrix}\begin{matrix}↼ − \\ y \end{matrix}.

{p ∧ b} S_{1} {q} {p ∧ ¬b} S_{2} {q}
{p} if b then S_{1} else S_{2} {q}

\frac{\{p ∧ b\} S \{p ∧ sofar\}}{\{p\} while b do S \{p ∧ ¬b ∧ (sofar ∨ ⋀ _{v∈VAR} v =\begin{matrix}↼ − \\ v \end{matrix} )\}}

where sofar is^{3} transitive and well-founded.

As Jones (1990) points out, it suffices to ensure that the relation sofar is well-founded on the set of states satisfying the invariant p. It would thus suffice, if the relation λ(τ,σ) · [[sofar]]I(τ,σ)∧ [[p]]I(τ)was well-founded.

One also needs the structural rule

\frac{\{p_{1}\} S \{q_{1}\}}{\{p\} S \{q\}}  provided p ⇒ p_{1} and\begin{matrix}↼ − \\ p \end{matrix} ⇒ q_{1} ⇒ q

which allows one to weaken the precondition and strengthen the postcondition in a proof obligation. This is particularly useful when one wants to apply the rule for loops as the precondition must remain invariant with respect to the body of the loop, whereas the postcondition is expected to incorporate justification for termination.

2.9.1 Mechanisation

The above presentation of VDM is due to Aczel (1982a, 1982b). There, preconditions are treated as predicates on states, whereas postconditions are captured by binary relations on states. In particular, assertions are not considered at a syntactic level and there is no reference to LPF.

We follow Aczel’s approach and employ a shallow embedding to represent assertions. In a logical framework supporting set theory, one might use sets to capture relations. Using Lego, we only consider sets of the form {x | P(x)} where P : T → Prop is a function mapping elements of an arbitrary type T to Lego’s internal logic. In other words, we represent sets by their characteristic functions. Thus, in VDM, preconditions inhabit Σ → Prop and postconditions inhabit (Σ × Σ) → Prop.

We are interested in the meta theory of VDM. We therefore need to capture both the semantics and the notion of deriving a VDM correctness formula.

p. 144 Definition 2.12 (Semantics of VDM) Let

⊧_{VDM}{.} . {.} ⊆ (Σ → Prop) × prog × ((Σ × Σ) → Prop)

be a new judgement defined in terms of the operational semantics

⊧_{VDM}\{p\} S \{q\} =^{def} ∀σ · p(σ) ⇒ ∃τ· σ \overline{S ^{}}τ∧ q(τ,σ)

3i.e., the relation λ(τ,σ) · [[sofar]](τ,σ)

p. 145 Definition 2.13 (VDM) Let

⊢_{VDM}{.} . {.} ⊆ (Σ → Prop) × prog × ((Σ × Σ) → Prop)

be the least relation satisfying

⊢_{VDM}{λσ · true }skip {λ(τ,σ) · τ = σ}  (2.21)
⊢_{VDM}{λσ · true }x:= t {λ(τ,σ) · τ = σ[x ↦ eval(σ)(t)]}  (2.22)
\frac{⊢_{VDM}\{p_{1}\} S_{1} \{λ(τ,σ) · p_{2}(τ) ∧ r_{1}(τ,σ)\} ⊢_{VDM}\{p_{2}\} S_{2} \{r_{2}\}}{⊢_{VDM}\{p_{1}\} S_{1}; S_{2} \{r_{2}∘ r_{1}\}}  (2.23)

\frac{⊢_{VDM}\{λσ · p(σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} ⊢_{VDM}\{λσ · p(σ) ∧ eval(σ)(b) = false \} S_{2} \{q\}}{⊢_{VDM}\{p\} if b then S_{1} else S_{2} \{q\}}  (2.24)

⊢_{VDM}{λσ · p(σ) ∧ eval(σ)(b) = true }S {λ(τ,σ) · p(τ)∧ sofar(τ,σ)}

⊢_{VDM}{p} while b do S {q}

where sofar is any binary transitive relation on the state space Σ such that λ(τ,σ) · sofar(τ,σ)∧ p(τ) is well-founded

and q(τ,σ) =^{def} p(τ)∧ eval(τ)(b)= false ∧ (sofar(τ,σ)∨ τ = σ ) . (2.25)

\frac{⊢_{VDM}\{p_{1}\} S \{q_{1}\}}{⊢_{VDM}\{p\} S \{q\}}

provided ∀σ · p(σ) ⇒ p_{1}(σ) and ∀(τ,σ) · p(σ) ⇒ q_{1}(τ,σ) ⇒ q(τ,σ).  (2.26)

2.9.2 High-Level Verification

We now revisit the verification of the exponential program within the VDM framework. To increase readability, we use syntactic representations for assertions. These have been manually translated from the shallow embedding used in the actual script.

p. 148 Lemma 2.14 (VDM – Correctness of Exponential Function)

⊢_{VDM}\{0 ≤ y\} S_{exp} \{r =\begin{matrix}↼ − \\ x \end{matrix}\begin{matrix}↼ − \\ y \end{matrix} \}

Proof According to the axiom for assignments (2.22), we may derive the correctness formula ⊢_{VDM} \{true \} r:= 1 \{r = 1 ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ y =\begin{matrix}↼ − \\ y \end{matrix} \} . Taking 0 ≤ y into account, by the consequence rule (2.26) we may deduce

⊢_{VDM}\{0 ≤ y\} r:= 1 \{r = 1 ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ y =\begin{matrix}↼ − \\ y \end{matrix} ∧ 0 ≤ y\} .  (2.27)

The challenge in the correctness proof stems from instantiating the loop rule (2.25) with an appropriate invariance predicate and termination measure. From the postcondition of (2.27), we adopt 0 ≤ y as an invariant. In addition, the loop leaves r ∗ x^{y} invariant as decreases of y are compensated by increasing r appropriately:

sofar =^{def}\begin{matrix}↼ − \\ r \end{matrix} ∗\begin{matrix}↼ − \\ x \end{matrix}\begin{matrix}↼ − \\ y \end{matrix} = r ∗ x^{y}∧ y <\begin{matrix}↼ − \\ y \end{matrix} .

We thus focus on deriving

⊢_{VDM}{0 ≤ y} loop_{exp} {0 ≤ y ∧ ¬(y ≠ 0) ∧ sofar}  (2.28)

which is semantically equivalent to the proof obligation (2.16). We may decompose (2.28) by the rule for loops (2.25) as sofar is clearly transitive and in the light of 0 ≤ y, there are only finitely many smaller\begin{matrix}↼ − \\ y \end{matrix}. We now need to show

⊢_{VDM}{0 ≤ y ∧ y ≠ 0} r:= r ∗ x; y:= y − 1 {0 ≤ y ∧ sofar}  (2.29)

According to the axiom (2.22), we may deduce

⊢_{VDM}\{true \} r:= r ∗ x \{r =\begin{matrix}↼ − \\ r \end{matrix} ∗\begin{matrix}↼ − \\ x \end{matrix} ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ y =\begin{matrix}↼ − \\ y \end{matrix} \}  (2.30)
and ⊢_{VDM}\{true \} y:= y − 1 \{y =\begin{matrix}↼ − \\ y \end{matrix} − 1 ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ r =\begin{matrix}↼ − \\ r \end{matrix} \}  (2.31)

Connecting (2.30)with the precondition of (2.29), the rule of consequence (2.26)yields

⊢_{VDM}\{0 ≤ y ∧ y ≠ 0\} r:= r ∗ x \{r =\begin{matrix}↼ − \\ r \end{matrix} ∗\begin{matrix}↼ − \\ x \end{matrix} ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ 0 < y ∧ y =\begin{matrix}↼ − \\ y \end{matrix} \}  (2.32)

We now derive

⊢_{VDM}{0 ≤ y ∧ y ≠ 0} r:= r ∗ x; y:= y − 1 {postbody}  (2.33)

with

postbody =^{def} ∃x_{i},y_{i},r_{i}· r_{i} =\begin{matrix}↼ − \\ r \end{matrix} ∗\begin{matrix}↼ − \\ x \end{matrix} ∧ x_{i} =\begin{matrix}↼ − \\ x \end{matrix} ∧ 0 < y_{i}∧ y_{i} =\begin{matrix}↼ − \\ y \end{matrix} ∧

y = y_{i}− 1 ∧ x = x_{i}∧ r = r_{i}

by applying the sequential rule (2.23) to the correctness formulae (2.32) and (2.31). Appealing to the consequence rule (2.26), it suffices to show that the postcondition of (2.33) entails the postcondition of (2.29). The above postcondition is equivalent to

r =\begin{matrix}↼ − \\ r \end{matrix} ∗\begin{matrix}↼ − \\ x \end{matrix} ∧ x =\begin{matrix}↼ − \\ x \end{matrix} ∧ 0 ≤ y ∧ y =\begin{matrix}↼ − \\ y \end{matrix} − 1 .  (2.34)

It is straightforward to check that 0 ≤ y ∧ sofar follows from (2.34). Combining the initialisation (2.27) with the loop (2.28) via the rule of sequential composition (2.23) leads to

⊢_{VDM}\{0 ≤ y\} S_{exp} \{0 ≤\begin{matrix}↼ − \\ y \end{matrix} ∧ y = 0 ∧ r =\begin{matrix}↼ − \\ x \end{matrix}\begin{matrix}↼ − \\ y \end{matrix} \}

Appealing to the consequence rule (2.26), we may weaken the postcondition and derive the desired correctness formula. □

2.9.3 Soundness

In this section, we justify the VDM rules and show that VDM is merely a more abstract calculus for verifying correctness formulae. In particular, any derivation in VDM corresponds to a proof based on the rules of the operational semantics.

p. 149 Theorem 2.15 (Soundness of VDM) For any precondition p, program S and postcondition q, whenever ⊢_{VDM}{p} S {q} is derivable, ⊧_{VDM}{p} S {q} holds. Proof by induction on the derivation of ⊢_{VDM}{p} S {q}. We consider the two cases where ⊢_{VDM} {p} S {q} has been derived by the rule for conditionals or loops in more detail:

Conditional From the two induction hypotheses

⊧_{VDM}{λσ · p(σ) ∧ eval(σ)(b) = true }S_{1} {q}  (2.35)
⊧_{VDM}{λσ · p(σ) ∧ eval(σ)(b) = false } S_{2} {q}  (2.36)

we need to establish

⊧_{VDM}{p} if b then S_{1} else S_{2} {q} .

We show this by expanding the definition of ⊧_{VDM}and considering the cases for eval(σ)(b) = true and eval(σ)(b) = false . The conclusion follows from (2.35) and (2.36) respectively.

Loop Given a state satisfying the predicate p, we need to find a final state τ such that

σ \overline{while b do S_{}}τ ,  (2.37)
p(τ) ,  (2.38)
eval(τ)(b)= false  (2.39)

and

sofar(τ,σ)∨ τ = σ .  (2.40)

We commence with a case analysis on the initial value of the guard b. If it is false, (2.9) is applicable. In particular, for τ = σ, (2.38)–(2.40) trivially hold. Otherwise, we may exploit the induction hypothesis

⊧_{VDM}{λσ · p(σ) ∧ eval(σ)(b) = true }S {λ(τ,σ) · p(τ)∧ sofar(τ,σ)} .

There must be a state η such that

σ  \overline{S ^{}}η ,  (2.41)

p(η) and

sofar(η,σ) .  (2.42)

Is the guard b satisfied in state η?

No: From (2.41), we may conclude σ \overline{while b do S_{}}η. For τ = η, (2.38)– (2.40) trivially hold.

Yes: The side-condition of the rule for loops guarantees that the relation

λ(τ,σ) · sofar(τ,σ)∧ p(τ)

is well-founded. We know that η is less than σ with respect to this ordering and may thus exploit variants of (2.38)–(2.40)where σ has been substituted by η. In particular, there has to be a state τ satisfying (2.38), (2.39),

η \overline{while b do S_{}}τ  (2.43)

and

sofar(τ,η)∨ τ = η .  (2.44)

In fact, τ is the desired final state. Applying the rule for loops (2.10) to (2.41) and (2.43) justifies (2.37), whereas transitivity of sofar leads from (2.44) and (2.42) to (2.40).

□

2.9.4 Completeness

If the VDM rules are to serve as the basis of a methodology for developing programs, completeness is an important issue i.e., given any derivable correctness formula of the form ⊧_{VDM}{p} S {q}, can we also justify it only employing the VDM rules? In other words, in moving from the level of states to the level of predicates and relations over states, have we actually achieved a more abstract perspective? Our objective is to show that whenever ⊧_{VDM}{p} S {q} holds, the judgement ⊢_{VDM}{p} S {q} is derivable.

One may prove completeness directly by induction on the structure of S e.g., Aczel (1982a) establishes soundness and completeness simultaneously by induction on the structure of programs. Instead, we follow an idea due to Gorelick (1975), which, previously, has only been applied to Hoare Logic dealing with recursive procedures :

1. By induction on the structure of an arbitrary program S, one establishes that a specific correctness formula MGF_{VDM}(S), known as the Most General Formula (MGF), is derivable in the verification calculus.

2. Given the assumption ⊧_{VDM} {p} S {q}, one may derive ⊢_{VDM} {p} S {q} by applying structural rules to ⊢_{VDM}MGF_{VDM}(S). All side-conditions which arise will be dealt with by the assumption.

In other words, instead of directly deriving ⊧_{VDM}{p} S {q} ⇒ ⊢_{VDM}{p} S {q}, one considers the stronger property ⊢_{VDM} MGF_{VDM}(S) for which induction goes through more easily. In particular, the direct proof cannot be applied when one considers recursive procedures, because the induction hypotheses are not strong enough.

The proposition ⊢_{VDM} MGF_{VDM}(S) asserts that, provided that one only considers input states in which the program S terminates, one may derive a correctness formula in which the postcondition relates all inputs with the appropriate outputs according to the underlying operational semantics of the programming language. At the semantic level, ⊧_{VDM}MGF_{VDM}(S)holds trivially.

p. 149 Definition 2.16 (Termination) For a deterministic programming language, commencing execution in a state σ, a program S terminates, if there is a final state τ according to the operational semantics, S↓(σ) =^{def} ∃τ· σ \overline{S ^{}}τ.

p. 149 Definition 2.17 (MGF) MGF_{VDM}(S) =^{def} \{S↓\}S \{ λ(τ,σ) · σ  \overline{S ^{}}τ\}

Having chosen a shallow embedding, we can directly express the assertions of the MGF. If one also considers syntax of the assertion language, one needs to show that it is sufficiently expressive. We will return to this issue in more detail in Section 2.12.

p. 150 Theorem 2.18 (MGF for VDM) For an arbitrary program S, ⊢_{VDM} MGF_{VDM}(S) is derivable. Proof The proof is by induction on the structure of S. Observe that this induction strategy affects both pre- and postcondition. In the case of a loop, from

⊢_{VDM}\{S↓\}S \{ λ(τ,σ) · σ  \overline{S ^{}}τ\}  (2.45)

we have to establish

⊢_{VDM}\{while b do S↓\}while b do S \{ λ(τ,σ) · σ \overline{while b do S_{}}τ\} . (2.46)

We first apply the consequence rule (2.26) to the hypothesis (2.45) so that the precondition is of the form λσ · p(σ) ∧ eval(σ)(b) = true and the postcondition matches λ(τ,σ) · p(τ)∧ sofar(τ,σ) for some suitable invariant p and variant sofar. We then apply the rule for loops and, finally, employ again the consequence rule to transform the correctness formula into the desired (2.46).

These transformations trigger some side-conditions relative to the variant and invariant to be synthesised. Applying the consequence rule for the first time yields

∀σ · (p(σ) ∧ eval(σ)(b) = true ) ⇒ S↓(σ)  (2.47)

∀(τ,σ) · (p(σ) ∧ eval(σ)(b) = true ) ⇒ σ \overline{S ^{}}τ⇒

(p(τ)∧ sofar(τ,σ)) (2.48)

The rule for loops is concerned with λ(τ,σ) ·sofar(τ,σ)∧p(τ)being well-founded and the last appeal to the rule of consequence triggers

∀σ · while b do S↓(σ) ⇒ p(σ)  (2.49)

∀(τ,σ) · while b do S↓(σ) ⇒

(p(τ)∧ eval(τ)(b)= false ∧ sofar^{=} (τ,σ)) ⇒ σ \overline{while b do S_{}}τ (2.50)

The termination condition for loops p =^{def} while b do S↓is an appropriate invariant. A single step of the loop is captured by the relation

λ(σ,τ) · eval(σ)(b) = true ∧ σ \overline{S ^{}}τ .

Appealing to the operational semantics definition, it is easy to check that the transitive closure

sofar(τ,σ) =^{def} (λ(σ,τ) · eval(σ)(b) = true ∧ σ \overline{S ^{}}τ)^{+} (σ,τ)

approximates the loop. In particular, if τ is one of the intermediate states of the loop for which the loop guard does not hold, i.e., for some initial state σ, if sofar^{=} (τ,σ) and eval(τ)(b)= false holds, we must have σ \overline{while b do S_{}}τ. □

p. 150 Corollary 2.19 (Completeness of VDM) For any precondition p, program S and postcondition q, whenever ⊧_{VDM}{p} S {q} holds, ⊢_{VDM}{p} S {q} is derivable.

It is instructive to study the completeness proof in order to appreciate the rôle of the main theorem 2.18:

Proof By Def. 2.12, the hypothesis is equivalent to

∀σ · p(σ) ⇒ ∃τ· σ  \overline{S ^{}}τ∧ q(τ,σ) .  (2.51)

Applying the consequence rule (2.26) to the MGF i.e.,

⊢_{VDM}\{S↓\}S \{ λ(τ,σ) · σ \overline{S ^{}}τ\} ,

we may derive the desired ⊢_{VDM}{p} S {q}provided we can satisfy the side conditions

∀σ · p(σ) ⇒ S↓(σ)  (2.52)

and

∀(τ,σ) · p(σ) ⇒ σ  \overline{S ^{}}τ⇒ q(τ,σ) .  (2.53)

Weakening the precondition (2.52)is a direct consequence of (2.51). We may strengthen the postcondition as suggested in (2.53) due to determinism. □

2.10 Hoare Logic

Hoare Logic deviates from VDM in that the postcondition can only refer to the final and not to the initial state. We first present a set of rules for total correctness which reflects the traditional presentation. This includes a mechanised soundness and completeness result. Such meta-theoretical proofs are very similar to those in the VDM setting and we will merely highlight technical differences.

Using Hoare Logic, one cannot directly specify input/output specifications. In Sect. 2.10.3, we discuss various approaches to overcome this problem. Amongst these, we favour a proposal by Apt & Meertens (1980) to semantically consider assertions as relations between states and a domain of auxiliary variables. This idea has not previously been applied to Hoare Logic. It is useful in mechanising soundness and completeness of Hoare Logic, because it more adequately reflects the rôle of auxiliary variables employed in practice to relate output and input.

As our main contribution, we have designed a rule of consequence for adjusting auxiliary variables while strengthening preconditions and weakening postconditions. The new consequence rule is strictly stronger than Hoare’s (1969) version. It plays a crucial rôle in establishing completeness as a corollary of the MGF theorem. Furthermore, it obviates the need for any further structural rules when considering recursive procedures.

In the traditional understanding of Hoare Logic (Cousot 1990), both pre- and postconditions are first-order logic formulae where free variables denote the value of program variables. In particular, the validity of a formulae depends on a state σ.

For the purpose of mechanising soundness and completeness, we ignore the syntactical aspect of assertions. Employing again a shallow embedding, assertions are to inhabit the function space Σ → Prop which, intuitively, characterises predicates on states.

p. 150 Definition 2.20 (Semantics of Hoare Logic) Let

⊧_{Hoare}{.} . {.} ⊆ (Σ → Prop) × prog × (Σ → Prop)

be a new judgement defined in terms of the operational semantics

⊧_{Hoare}\{p\} S \{q\} =^{def} ∀σ · p(σ) ⇒ ∃τ· σ \overline{S ^{}}τ∧ q(τ) .

2.10.1 Verification Calculus

Based on work of Floyd (1967), Hoare (1969) proposed a verification calculus for partial correctness, now referred to as Hoare Logic, see Fig. 2.1. To ensure termination,

{p} skip {p}
{p[x ↦ t]} x:= t {p}  (2.54)
{p} S_{1} {r} {r} S_{2} {q}
{p} S_{1}; S_{2} {q}
{p ∧ b} S_{1} {q} {p ∧ ¬b} S_{2} {q}
{p} if b then S_{1} else S_{2} {q}
\frac{\{p ∧ b\} S \{p\}}{\{p\} while b do S \{p ∧ ¬b\}}  (2.55)
\frac{\{p_{1}\} S \{q_{1}\}}{\{p\} S \{q\}}  provided p ⇒ p_{1} and q_{1} ⇒ q.

Figure 2.1: Hoare Logic for Partial Correctness – Syntactic Presentation

the rule for loops (2.55) needs to be modified. We introduce a termination measure u with [[u]] : Σ → W rather than just u : expression(W). It may inspect all of the state space for some well-founded^{4} domain (W,<) which is decreased whenever the body is executed:

4As we have already observed in the case of VDM’s rule for loops, for the special case of W = Σ, it suffices to show that the relation λ(τ,σ) · p(τ)∧ τ < σ is well-founded.

\frac{∀t : W· \{p ∧ b ∧ u = t\} S \{p ∧ u < t\}}{\{p\} while b do S \{p ∧ ¬b\}}  where (W,<) is well-founded.

A similar rule for verification calculi where postconditions may explicitly refer to the value of program variables in the initial state e.g., VDM, has been put forward by Manna & Pnueli (1974). Variants of this rule tailored for W = nat (Harel 1980) or W = int (Apt & Olderog 1991) have also been published previously. We prefer the well-founded version, because it simplifies the completeness proof without any impact on the soundness proof. One may instantiate W = Σ and u = λσ · σ, and encounters almost the same proof obligations as in the case of VDM. It is well known that in practice, it is often easier to reason about termination using well-founded sets rather than being restricted to natural numbers (Dershowitz & Manna 1979).

Formalising these rules in a logical framework is as easy as in the case of VDM. In particular, representing the axiom for assignments (2.54) requires little work, because we chose a shallow embedding of assertions. Syntactically, the precondition amounts to updating a formulae at position x. We do not need to worry about formalising the notion of updating assertions. It can be expressed straightforwardly in terms of updating the state space i.e.,

[[p[x ↦ t]]]I(σ)= [[p]](I(σ[x ↦ [[t]]I(σ) ]))  (2.56)

Previous experiments (Mason 1987, Homeier 1995) have made it clear that a deep embedding of assertions requires much more work. In particular, one would have to prove the substitution lemma (2.56) (Sieber 1981).

p. 150 Definition 2.21 (Hoare Logic) A verification calculus for Hoare Logic is defined as the least relation ⊢_{Hoare}{.} . {.} ⊆ (Σ → Prop) × prog × (Σ → Prop) satisfying

⊢_{Hoare}{p} skip {p}

⊢_{Hoare}{λσ · p(σ[x ↦ eval(σ)(t)])} x:= t {p}

\frac{⊢_{Hoare}\{p\} S_{1} \{r\} ⊢_{Hoare}\{r\} S_{2} \{q\}}{⊢_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

\frac{⊢_{Hoare}\{λσ · p(σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} ⊢_{Hoare}\{λσ · p(σ) ∧ eval(σ)(b) = false \}S_{2} \{q\}}{⊢_{Hoare}\{p\} if b then S_{1} else S_{2} \{q\}}

\frac{∀t : W· ⊢_{Hoare} \{λσ · p(σ) ∧ eval(σ)(b) = true ∧ u(σ) = t \} S \{λτ· p(τ)∧ u(τ)< t \}}{⊢_{Hoare}\{p\} while b do S \{λτ· p(τ)∧ eval(τ)(b)= false \}}

where (W,<) is well-founded. (2.57)

\frac{⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}\{p\} S \{q\}}  provided ∀σ · p(σ) ⇒ p_{1}(σ) and ∀τ· q_{1}(τ)⇒ q(τ). (2.58)

2.10.2 Soundness and Completeness

p. 151 Theorem 2.22 (Soundness of Hoare Logic) The above verification calculus is sound. Proof by induction on the derivation of ⊢_{Hoare}{p} S {q}. □

p. 151 Definition 2.23 (Completeness of Hoare Logic) The above verification calculus is complete.

Proof by induction on the structure of programs. We concentrate on the case for loops. From the induction hypothesis

∀p,q· ⊧_{Hoare}{p} S {q} ⇒ ⊢_{Hoare}{p} S {q}

and the assumption

⊧_{Hoare}{p} while b do S {q}  (2.59)

we have to establish

⊢_{Hoare}{p} while b do S {q} .  (2.60)

Our strategy is to use the rule for loops to connect the induction hypothesis with the conclusion. In particular, we have to come up with a suitable invariant and a termination measure u: Σ → W for some well-founded domain (W,<). The proof is more challenging than establishing the MGF theorem^{5}, because one needs to worry about arbitrary assertions p and q. In general, the supplied precondition p cannot be expected to remain invariant. Instead, we choose

inv= wp(while b do S,q)

where, relative to a program and postcondition, the weakest precondition wpis defined as

wp(S,q)(σ) =^{def} ∃τ· σ \overline{S ^{}}τ∧ q(τ) .

It is easy to see that, for a loop, the weakest precondition^{6} is bound to remain invariant.

5In the following section, we will strengthen the rule of consequence (2.58)so that one may establish completeness as a corollary of the MGF property.

6In verifying concrete programs, the problem of constructing suitable invariants stems from having to find syntactic representations of such predicates. It does not suffice to rely on a sufficiently expressive assertion language in which one may express weakest preconditions. Instead, one needs to find an invariant specific to the domain involved; otherwise the difficulty in verification of finding suitable invariants is merely propagated to proving more difficult side-conditions arising from applications of the consequence rule.

In finding a suitable termination measure, we borrow ideas from the corresponding proof in the context of VDM. We choose W = Σ, u = λσ · σ,

τ < σ =

while b do S↓(σ)∧ (λ(σ,τ) · eval(σ)(b) = true ∧ σ \overline{S ^{}}τ)^{+} (σ,τ) (2.61)

and aim to establish

∀t : Σ· ⊢_{Hoare}{λσ · inv(σ) ∧ eval(σ)(b) = true ∧ σ = t } S {λτ· inv(τ)∧ τ < t} (2.62)

Let t be an arbitrary state. Appealing to the induction hypothesis, it suffices to show the semantic counterpart

⊧_{Hoare}{λσ · inv(σ) ∧ eval(σ)(b) = true ∧ σ = t } S {λτ· inv(τ)∧ τ < t}

In other words, relative to an initial state σ, such that

\underbrace{∃τ· σ \overline{while b do S_{}}τ∧ q(τ)}_{inv(σ)} and eval(σ)(b) = true

we have to find an intermediate state η such that

σ \overline{S ^{}}η ,

∃τ· η while b do S_{}τ∧ q(τ)

inv(η)

and

while b do S↓(σ)∧ eval(σ)(b) = true ∧ σ \overline{S ^{}}η
η<σ

These proof obligations can be resolved easily by appealing to the definition of the operational semantics. We may thus apply the rule for loops (2.57) to the correctness formula (2.62). This yields

⊢_{Hoare} \{ λσ · ∃τ· σ \overline{while b do S_{}}τ∧ q(τ)\} while b do S \{ λη · ∃τ· η \overline{while b do S_{}}τ∧ q(τ)∧ eval(η)(b) = false \} .

According to the rule of consequence, we may derive the desired (2.60), provided the two side-conditions

∀σ · p(σ) ⇒ ∃τ· σ \overline{while b do S_{}}τ∧ q(τ)  (2.63)
∀η · (∃τ· η \overline{while b do S_{}}τ∧ q(τ)∧ eval(η)(b) = false) ⇒ q(η)  (2.64)

hold. The first condition is identical to the assumption (2.59). In the second obligation, according to the definition of the operational semantics, the state τ must be equal to η in the case where the loop guard evaluates to false . □

2.10.3 Auxiliary Variables

Recall the specification of the exponential function in VDM

\{0 ≤ y\} S_{exp} \{r =\begin{matrix}↼ − \\ x \end{matrix}\begin{matrix}↼ − \\ y \end{matrix} \} ,  (2.65)

where all variables hold integer values. In Hoare Logic, we cannot directly relate the value of variables in the final with values of variables in the initial state. To overcome this deficiency, one needs to introduce auxiliary variables. These are simply program variables which only occur in assertions. In the precondition they freeze the value of other program variables so that in the postcondition one may refer to the initial value of program variables.

Let X and Y be program variables with sort int. In Hoare Logic, we may paraphrase the above correctness formula (2.65) as

{0 ≤ y ∧ x = X ∧ y = Y } S_{exp} {r = X^{Y}}

where X and Y must not occur free in S_{exp}. (2.66)

Notice that the side-condition is essential for the two correctness formulae (2.65) and (2.66) to be equivalent. However, in Hoare Logic, we may not derive correctness formulae with attached side conditions, because, formally, Hoare Logic is a calculus for deriving programs with pre- and postconditions only. Side-conditions may only appear within axioms and rules.

Strictly speaking, Hoare Logic is thus not adequate for verifying concrete programs with non-contrived specifications. Problems also arise when the program to be verified is not fully given. This is for example the case when one wants to

• develop programs hand-in-hand with their proofs of correctness (Gries 1981, Burstall & McKinna 1993)

• consider free procedures which are specified with respect to pre- and postcondition, but for which one does not consider a concrete implementation (Tarlecki 1985)

• verify programs invoking recursive procedures Assume that S is a program about which we only know that the program variable X does not occur in it. Then, the two specifications

{X = x} S {X = x}  (2.67)

and

{X = x + 1} S {X = x + 1}  (2.68)

where all variables denote integer values both assert that the program S leaves x invariant. Whenever one can derive (2.67), one may derive (2.68) and vice versa. Nevertheless, the consequence rule (2.58) does not allow one to derive (2.68) from (2.67) or vice versa, as one would have to show x = x + 1.

Pragmatically, this is quite worrying. Taking software reuse seriously, imagine that (2.67) has been derived as part of the verification in a project. It is conceivable that in another project, one needs the equivalent correctness formula (2.68). Unfortunately, in such a situation, the formalism would force us to rebuild (2.68) from scratch. Of course in the new derivation one would benefit from analysing the first derivation, but such a methodology is nevertheless undesirable.

In Chapter 3, using recursive procedures, we show that precisely this problem occurs in trying to verify a program computing the factorial function. In a nutshell, similar to an inductive step, during the verification of a recursive procedure, one needs to unroll the recursive call and can exploit the correctness formula before unrolling. Unfortunately, the hypothesis only matches modulo shifts of auxiliary variables reminiscent of the difference between (2.67) and (2.68).

We need to strengthen the consequence rule and exploit the fact that some program variables are not affected by the program e.g.,

\frac{\{p_{1}\} S \{q_{1}\}}{\{p\} S \{q\}}

provided ∀X· ∃t· (∀x· p ⇒ (p_{1}[X ↦ t])) ∧ (∀x· (q_{1}[X ↦ t]) ⇒ q) , (2.69)

where x and X are two disjoint lists of all program variables occuring in any of the assertions and no variables from X appear in the program. Furthermore, t is a list of compatible expressions.

Such a consequnce rule is strictly stronger than Hoare’s (1969), which one might view as a special case of 2.69 in which the choice of t is confined to X. In particular, employing the new rule of consequence, the two correctness formulae (2.67)and (2.68) are interderivable e.g., (2.68) follows from (2.67), because one may choose t = X − 1.

It also highlights that auxiliary variables and program variables deserve to be treated differently e.g., in the above side-condition, one only substitutes auxiliary variables. We would like to reflect this intuition in our encoding. We see two alternatives to capture auxiliary variables

1. explicit quantification

2. implicit quantification Following Sieber (1985), we could extend Hoare Logic by allowing quantifiers at

the object level i.e., correctness formulae may also take on the form of

⊢_{Hoare}∀X· {p} S {q}

where the value of X does not depend on a state^{7}.

Universal quantification is one way to ensure that auxiliary variables are bound explicitly to a specific correctness formula. It binds all occurrences of auxiliary variables in the precondition, program and postcondition. We prefer an implicit quantification in which both pre- and postcondition characterise binary relations on states. The advantages over explicit quantification are that

• programs cannot mention auxiliary variables

• it reflects current software engineering practice and does not require a syntactic modification of correctness formulae

• it is easier to encode in a logical framework

At the syntactic level, one would need to (formally) distinguish between program variables and auxiliary variables^{8}. One could for example enforce that program variables have to start with a lower-case letter, whereas auxiliary variables must start with an upper-case letter. To be well-formed, programs may only refer to program variables.

Semantically, program variables are, as before, interpreted according to the state space. However, auxiliary variables are interpreted freely. Let T be the domain of this interpretation. We revise our shallow embedding of assertions:

p. 152 Definition 2.24 (Assertions) Assertion(T : Type)=^{def} (T × Σ) → Prop

Example 2.25 Let T = {X,Y } → int. Relative to an interpretation Z : T and a state σ, we interpret

[[0 ≤ y ∧ x = X ∧ y = Y ]](Z,σ)= 0 ≤ σ(y) ∧ σ(x) = Z(X)∧ σ(y) = Z(Y) .

The universal quantification is then integrated into the semantics:

p. 152 Definition 2.26 (Semantics of Hoare Logic) Parametrised by an arbitrary type T, let ⊧_{Hoare}{.} . {.} ⊆ Assertion(T) × prog × Assertion(T) be a new judgement defined in terms of the operational semantics

⊧_{Hoare}\{p\} S \{q\} =^{def} ∀Z· ∀σ · p(Z,σ) ⇒ ∃τ· σ \overline{S ^{}}τ∧ q(Z,τ) .

tionaly assert that X is not affected by the program S.

7In Reynolds’s (1982) specification logic, X may be an arbitrary expression and one needs to addi-

8This has also been proposed by Homeier & Martin (1996).

The previous axioms and rules from Def. 2.21 can be reused without any syntactic changes when assertions are represented as relations rather than predicates. Furthermore, we can easily formally cater for the stronger rule of consequence (2.69):

\frac{⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}\{p\} S \{q\}}

provided ∀Z· ∃Z_{1}· (∀σ · p(Z,σ) ⇒ p_{1}(Z_{1},σ)) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ))

where p_{1},q_{1} : Assertion(T) and p,q : Assertion(U) are assertions for not necessarily identical domains T and U. In deriving completeness as a corollary from the MGF property, besides being able to switch the domain of auxiliary variables, we also need a weaker side-condition in which the choice of Z_{1} may additionally depend on the initial value of variables:

\frac{⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}\{p\} S \{q\}}

where p_{1},q_{1} : Assertion(U) and p,q: Assertion(T) for arbitrary types T and U

and ∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) ) . (2.70)

In classical logic, ∀Z · ∀σ · ∃Z_{1} · (p(Z,σ) ⇒ p_{1}(Z_{1},σ)) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) is an equivalent side-condition. Constructively, it is stronger and not sufficient. We will present a further variant on page 71 in connection with the rule of adaptation. However, the above consequence rule (2.70) suffices for all examples and completeness proofs in this thesis. In moving from predicates to binary relations, we retain all other axioms and rules:

p. 152 Definition 2.27 (Hoare Logic) A verification calculus for Hoare Logic which takes auxiliary variables seriously is defined as the least relation

⊢_{Hoare}{.} . {.} ⊆ Assertion(T) × prog × Assertion(T)

indexed by an arbitrary type T such that

⊢_{Hoare}{p} skip {p}  (2.71)
⊢_{Hoare}{λ(Z,σ) · p(Z,σ[x ↦ eval(σ)(t)])}x:= t {p}  (2.72)

\frac{⊢_{Hoare}\{p\} S_{1} \{r\} ⊢_{Hoare}\{r\} S_{2} \{q\}}{⊢_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

\frac{⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} ⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = false \}S_{2} \{q\}}{⊢_{Hoare}\{p\} if b then S_{1} else S_{2} \{q\}}

∀t : W· ⊢_{Hoare} \left\{{ \atop p̂}\right\} S \{λ(Z,τ)· p(Z,τ)∧ u(τ)< t \}

⊢_{Hoare}{p} while b do S {λ(Z,τ)· p(Z,τ)∧ eval(τ)(b)= false }

where p̂(Z,σ) =^{def} p(Z,σ) ∧ eval(σ)(b) = true ∧ u(σ) = t

and (W,<) is well-founded. (2.73)

\frac{⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}\{p\} S \{q\}}

where p_{1},q_{1} : Assertion(U) and p,q: Assertion(T) for arbitrary types T and U

and ∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) ) . (2.70)

2.10.3.1 Universal Quantification versus Auxiliary Variables

Notice that the universally quantified variable in the rule for loops (2.73) plays the same rôle as an auxiliary variable. It must not occur^{9} in the program S. In particular, the rule

\left\{{ \atop p̂}\right\} S \{λ((Z,t),τ)· p(Z,τ)∧ u(τ)< t \}

{p} while b do S {λ(Z,τ)· p(Z,τ)∧ eval(τ)(b)= false }

where p̂((Z,t),σ)=^{def} p(Z,σ) ∧ eval(σ)(b) = true ∧ u(σ) = t and (W,<) is well-founded.

is (semantically) equivalent.

We have chosen the version (2.73) with universal quantification to illuminate the similarities with the rule for procedure calls in the following chapter in which the universal quantification ranges over two correctness formulae and cannot be internalised as an auxiliary variable. Furthermore, the implicitly quantified version needs to extend the domain of auxiliary variables in the premiss to guarantee that t is fresh. Hence, one could not restrict the domain of auxiliary variables to the state space.

In practice, rules with universal quantifications in the premiss pose no problems. In a refinement proof, one continues deriving the pure Hoare triple in which the quantified variable is replaced by a constant.

2.10.3.2 Soundness

We refer the reader to Fig. 2.2 on the next page for a syntactic presentation of this calculus. There, the side condition of the rule of consequence is tailored to classical logic and x denotes the list of all program variables, whereas Z denotes the list of all auxiliary variables.

9in our formalisation in Lego, S is universally quantified outside the scope of t.

{p} skip {p}
{p[x ↦ t]} x:= t {p}
{p} S_{1} {r} {r} S_{2} {q}
{p} S_{1}; S_{2} {q}
{p ∧ b} S_{1} {q} {p ∧ ¬b} S_{2} {q}
{p} if b then S_{1} else S_{2} {q}
\frac{∀t : W· \{p ∧ b ∧ u = t\} S \{p ∧ u < t\}}{\{p\} while b do S \{p ∧ ¬b\}}  where (W,<) is well-founded.

\frac{\{p_{1}\} S \{q_{1}\}}{\{p\} S \{q\}}

provided ∀Z· ∀x· ∃Z_{1}· (p ⇒ (p_{1}[Z ↦ Z_{1}])) ∧ (∀x· (q_{1}[Z ↦ Z_{1}]) ⇒ q) .

Figure 2.2: Hoare Logic for Total Correctness – Syntactic Presentation

p. 154 Theorem 2.28 (Soundness of Hoare Logic) The above verification calculus is sound. Proof by induction on the derivation of ⊢_{Hoare} {p} S {q}. If the rule of consequence (2.70) has been applied last, from the induction hypothesis ⊧_{Hoare}{p_{1}} S {q_{1}} i.e.,

∀Z_{1}· ∀σ · p_{1}(Z_{1},σ) ⇒ ∃τ· σ  \overline{S ^{}}τ∧ q_{1}(Z_{1},τ)  (2.74)

and the side-condition

∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) )  (2.75)

we need to show that ⊧_{Hoare}{p} S {q} holds.

Given an auxiliary variable Z and an initial state σ satisfying p(Z,σ), we need to find a final state τ such that σ \overline{S ^{}}τ and q(Z,τ). Combining p(Z,σ) and (2.75), we can extract a witness Z_{1} such that p_{1}(Z_{1},σ) holds, and for any state τ, we may reduce the task of showing q(Z,τ) to q_{1}(Z_{1},τ). Appealing to (2.74), from p_{1}(Z_{1},σ) we may infer that there is a state τ satisfying both σ \overline{S ^{}}τ and q_{1}(Z_{1},τ). □

2.10.3.3 Completeness

Having represented assertions as binary relations over an arbitrary domain and the state space, we can immediately formulate the MGF property for Hoare Logic. Picking the state space as the domain of auxiliary variables, we could simulate

⊢_{VDM}\{S↓\}S \{ λ(τ,σ) · σ \overline{S ^{}}τ\}

by

⊢_{Hoare}\{λ(Z,σ) · σ = Z ∧ S↓(σ)\}S \{ λ(Z,τ)· Z \overline{S ^{}}τ\} .

However, if one exploits the auxiliary variable to capture the final rather than the initial state, one has the more elegant

p. 154 Definition 2.29 (MGF)

MGF_{Hoare}(S) =^{def} \{ λ(Z,σ) · σ  \overline{S ^{}}Z\} S \{λ(Z,τ)· Z = τ\}

Notice that the precondition coincides with the weakest precondition relative to the program S and postcondition λ(Z,τ)· Z = τ.

p. 154 Theorem 2.30 (MGF for Hoare Logic) For an arbitrary program S, one may derive ⊢_{Hoare}MGF_{Hoare}(S). Proof by induction on the structure of S. In the case of a loop, from the induction hypothesis

⊢_{Hoare}\{ λ(Z,σ) · σ  \overline{S ^{}}Z\} S \{λ(Z,τ)· Z = τ\}  (2.76)

we have to establish

⊢_{Hoare}\{ λ(Z,σ) · σ \overline{while b do S_{}}Z\} while b do S \{λ(Z,τ)· Z = τ\} . (2.77)

The proof follows the structure of the same property for VDM on page 27. To exploit the rule for loops, we want to transform the hypothesis into the form

⊢_{Hoare} \left\{{ \atop p̂}\right\} S \{λ(Z,τ)· p(Z,τ)∧ u(τ)< t \}

where t : Σ is a constant and the termination measure u = λσ ·σ is the identity function. For the invariant, we choose

p(Z,σ) = σ \overline{while b do S_{}}Z

and for the variant <, we reuse the well-founded relation (2.61) employed in the direct proof of completeness on page 33.

This transformation is sanctioned by the rule of consequence (2.70). As a sidecondition, for arbitrary states σ and Z, given

σ \overline{while b do S_{}}Z  (2.78)
eval(σ)(b) = true  (2.79)

we need to find a state Z_{1} such that σ \overline{S ^{}}Z_{1} and

∀τ· (Z_{1} = τ) ⇒ (τ \overline{while b do S_{}}Z_{1}∧ σ < t ) .

Inverting the derivation of (2.78) in the presence of (2.79), these obligations can be resolved easily.

To conclude the proof, we need to again apply the rule of consequence to mediate between the conclusion of the loop rule

⊢_{Hoare}{p} while b do S {λ(Z,τ)· p(Z,τ)∧ eval(τ)(b)= false }

and the desired correctness formula (2.77). In this case, for arbitrary states σ and Z such that σ \overline{while b do S_{}}Z, we have find a state Z_{1} such that

σ \overline{while b do S_{}}Z_{1}  (2.80)
∀τ· (τ \overline{while b do S_{}}Z_{1}∧ eval(τ)(b)= false ) ⇒ Z_{1} = τ  (2.81)

We resolve (2.80) by selecting Z_{1} = Z, whereas (2.81) is a direct consequence of the operational semantics. □ Having treated auxiliary variables in a rigorous manner, it is interesting to observe that the main Theorem 2.30 singles out the set of all states as the domain for auxiliary variables i.e., T = Σ. Intuitively, such a restriction ties the auxiliary variables together with the program variables. Let {x_{1},... ,x_{n}} be the set of all program variables with corresponding types {T_{1},... ,T_{n}} occurring in some program S. An assertion of the form

λ(Z : T_{1}× ··· × T_{n},σ: Σ) · P(Z.1,... ,Z.n,σ)

is equivalent to

λ(Z,σ: Σ) · P(Z(x_{1}),... ,Z(x_{n}),σ) .

p. 154 Corollary 2.31 (Completeness of Hoare Logic) For any precondition p, program S and postcondition q, whenever ⊧_{Hoare}{p} S {q} holds, then ⊢_{Hoare}{p} S {q} is derivable.

Proof Let S be a program and p,q : Assertion(T)be assertions for an arbitrary domain T. Given ⊧_{Hoare}{p} S {q} i.e.,

∀Z· ∀σ · p(Z,σ) ⇒ ∃τ· σ  \overline{S ^{}}τ∧ q(Z,τ)  (2.82)

we need to establish ⊢_{Hoare}{p} S {q}.

By the consequence rule (2.70), we may directly infer ⊢_{Hoare}{p} S {q} from the MGF, triggering the side condition

∀Z· ∀σ · p(Z,σ) ⇒ ∃Z_{1}· σ  \overline{S ^{}}Z_{1}∧ ∀τ· τ = Z_{1} ⇒ q(Z,τ) . (2.83)

Clearly, (2.82) implies (2.83). □ We learn from the proof that in order to derive the correctness of a specification using an arbitrary domain of free variables T, applying the new consequence rule will confine the domain of auxiliary variables to the state space Σ. In particular, notice that the consequence rule has mediated between the state space and the domain T as representing auxiliary variables. Implementors of computer-aided verification systems can employ this transformation and safely restrict the domain of auxiliary variables to the state space for all other rules. Alternatively, one might implement a system in which the user can only formulate specifications where auxiliary variables live in the state space. Again, the above proof suggests that this is a sensible approach. In fact, in the system VDM, such a restriction has essentially been made.

Furthermore, notice that in the completeness proof, we only needed to apply the consequence rule (2.70). Thus, completeness always follows as a corollary of the MGF theorem, irrespective of any programming language extensions.

2.11 Hoare Logic subsumes VDM

In the traditional understanding of Hoare Logic, assertions characterise predicates on states. VDM is more flexible in that postconditions may, in addition to the final state, also refer to the initial state. In that respect, one may argue that VDM subsumes Hoare Logic.

However, we question such a view, because, pragmatically, in Hoare Logic, one uses auxiliary variables to relate output with input. Thus, one may choose an arbitrary reference point. In particular, during a derivation of a correctness formula {p} S {q}in Hoare Logic, both pre- and postcondition may refer to the value of program variables before executing S. VDM is more restrictive. In a derivation, the rule of sequential composition enforces that, in order to derive {p} S_{1}; S_{2} {q} one must adapt the point of reference so that the postcondition for S_{2} is expressed relative to some intermediate state. We should emphasise that this argument reflects the intended use of both systems. Of course, one could mirror Hoare Logic derivations in VDM by also employing auxiliary variables. But notice that one would then have to also improve VDM’s rule of consequence to simulate our version for Hoare Logic.

As a novelty, we show that Hoare Logic subsumes VDM in the sense that an arbitrary derivation in VDM can be embedded in Hoare Logic. In particular, every correctness formulae in the VDM derivation is uniformly translated and every decomposition rule in VDM is simulated by the corresponding rule in Hoare Logic where both premiss and conclusion may have to be adjusted by our new rule of consequence.

This result hinges on a rigorous treatment of auxiliary variables in Hoare Logic and the new rule of consequence. In particular, VDM’s consequence rule can be simulated by our rule of consequence, but not Hoare’s (1969).

p. 155 Theorem 2.32 (Embedding VDM Correctness Formulae in Hoare Logic) Let p ⊆ Σ and q ⊆ Σ × Σ be assertions and S an arbitrary program. Whenever

⊢_{VDM}{p} S {q}

is derivable, we may also establish

⊢_{Hoare}{λ(Z,σ) · σ = Z ∧ p(σ)}S {λ(Z,τ)· q(τ,Z)} .

By appealing to soundness and completeness, it is evident that for every derivation in VDM, one can reconstruct a proof of the corresponding correctness formula in Hoare Logic:

Proof Assume ⊢_{VDM}{p} S {q}. By soundness of VDM, we have

∀σ · p(σ)⇒ ∃τ· σ  \overline{S ^{}}τ∧ q(τ,σ)

which is equivalent to

∀Z· ∀σ · (σ = Z ∧ p(σ))⇒ ∃τ· σ  \overline{S ^{}}τ∧ q(τ,Z) .

Hence, by completeness of Hoare Logic, we may derive

⊢_{Hoare}{λ(Z,σ) · σ = Z ∧ p(σ)}S {λ(Z,τ)· q(τ,Z)} .
□

However, we are primarily interested in the structure of the translation. The following constructive proof shows explicitly how to transform VDM into Hoare Logic derivations. Notice that the proof transformation is compositional! Proof by induction on the derivation of ⊢_{VDM}{p} S {q}. We consider the interesting cases where the last rule applied has been the rule for sequential composition, loops, or the rule of consequence.

Sequential Composition From the induction hypotheses

⊢_{Hoare}{λ(Z,σ) · σ = Z ∧ p_{1}(σ)}S_{1} {λ(Z,τ)· p_{2}(τ)∧ r_{1}(τ,Z)}  (2.84)

and

⊢_{Hoare}{λ(Z,σ) · σ = Z ∧ p_{2}(σ)}S_{2} {λ(Z,τ)· r_{2}(τ,Z)}  (2.85)

we have to show

⊢_{Hoare}{λ(Z,σ) · σ = Z ∧ p_{1}(σ)}S_{1}; S_{2} {λ(Z,τ)· r_{2}∘ r_{1}(τ,Z)} .

We employ the strengthened rule of consequence to shift the reference point of the second assumption (2.85) to the initial state of the compound statement, yielding

⊢_{Hoare}{λ(Z,σ) · p_{2}(σ) ∧ r_{1}(σ,Z)}S_{2} {λ(Z,τ)· r_{2}∘ r_{1}(τ,Z)}  (2.86)

This transformation triggers the proof obligation

∀Z,σ · (p_{2}(σ) ∧ r_{1}(σ,Z)) ⇒ ∃Z_{1}· (σ = Z_{1} ∧ p_{2}(σ)) ∧ (∀τ· r_{2}(τ,Z_{1})⇒ (r_{2}∘ r_{1})(τ,Z))

which can be resolved easily when choosing Z_{1} = σ. Notice that Z_{1} has to capture the value of all program variables in the initial state. In particular, neither Hoare’s (1969) nor the draft rule of consequence (2.69) would have backed up the required transformation. Finally, applying the rule of sequential composition to the correctness formulae (2.84) and (2.86) completes the case.

Loops From the induction hypothesis

⊢_{Hoare} {λ(Z,σ) · σ = Z ∧ p(σ) ∧ eval(σ)(b) = true }
S
{λ(Z,τ)· p(τ)∧ sofar(τ,Z)}

(2.87) we have to show

⊢_{Hoare}  {λ(Z,σ) · σ = Z ∧ p(σ)}
while b do S
{λ(Z,τ)· p(τ)∧ eval(τ)(b)= false ∧ sofar^{=} (τ,Z)}

(2.88)

where sofar is transitive and λ(τ,σ) · sofar(τ,σ)∧ p(τ) is well-founded.

VDM’s rule for loops is stronger than the well-founded version (2.73), because in the postcondition of the conclusion, the rule for VDM additionally records information regarding the variant sofar. To compensate, instead of p, we use the weaker invariant

inv(Z,σ) = sofar^{=} (σ,Z)∧ p(σ) .

Another difficulty arises because we have to express termination relative to a new constant t. Again, it is up to the strengthened rule of consequence to reconcile the differences between the hypothesis (2.87) and

⊢_{Hoare} {λ(Z,σ) · inv(Z,σ)∧ eval(σ)(b) = true ∧ σ = t } S {λ(Z,τ)· inv(Z,τ)∧ sofar(τ,t)} .  (2.89)

We first deal with the proof obligation triggered by the side-condition of the rule of consequence. For arbitrary states Z, σ such that

inv(Z,σ) ,  (2.90)

eval(σ)(b) = true and σ = t, we have to find a state Z_{1} such that σ = Z_{1}, p(σ), eval(σ)(b) = true and

∀τ· (p(τ)∧ sofar(τ,Z_{1})) ⇒ (inv(Z,τ)∧ sofar(τ,t)∧ p(τ))  (2.91)

holds. Obviously, we have to chose Z_{1} = σ. We consider (2.91) in more detail. Let τ be an arbitrary state such that p(τ) and

sofar(τ,σ).  (2.92)

Besides p(τ) and sofar(τ,σ), we have to establish

sofar^{=} (τ,Z) .  (2.93)

We resolve (2.93) by appealing to the transitivity of sofar in the presence of (2.92) and (2.90).

Applying the rule for loops to (2.89) produces

⊢_{Hoare} {inv} while b do S {λ(Z,τ)· inv(Z,τ)∧ eval(τ)(b)= false } .

Employing the rule of consequence to weaken the precondition and strengthen the postcondition leads directly to the desired (2.88).

Consequence VDM’s consequence rule can be seen as an instance of our strengthened consequence rule for Hoare Logic. From the induction hypothesis

⊢_{Hoare}{λ(Z,σ) · σ = Z ∧ p_{1}(σ)}S {λ(Z,τ)· q_{1}(τ,Z)}

and the side-conditions p ⊆ p_{1} and ∀σ,τ · p(σ) ⇒ q_{1}(σ,τ) ⇒ q(σ,τ), we have to derive ⊢_{Hoare} {λ(Z,σ) · σ = Z ∧ p(σ)}S {λ(Z,τ)· q(τ,Z)}. From the sideconditions we may conclude

∀Z,σ · σ = Z ∧ p(σ) ⇒ ∃Z_{1}· σ = Z_{1} ∧ p_{1}(σ) ∧ ∀τ· q_{1}(τ,Z_{1})⇒ q(τ,Z) .

Thus, our rule of consequence leads also to the desired correctness formula. □

There is a one-to-one correspondence between the rules of Hoare-style ⊢_{Hoare} and VDM’s decomposition rules ⊢_{VDM}. According to Theorem 2.32, specifications in VDM correspond to a particular class of specification in Hoare Logic, in which the auxiliary variables are devoted to freezing the values of all program variables prior to execution.

The additional flexibility available in Hoare Logic is to blame for the more elaborate consequence rule, whereas the hard-wired perspective between pre- and postconditions in VDM is responsible for a more complicated sequential rule. Ultimately, it is a matter of taste which framework to use.

2.12 Completeness: What does it really mean?

In this section, we take a closer look at the meaning of completeness for verification calculi. We introduce Cook’s (1978) notion of relative completeness and critically review some of the incompleteness results in the literature. We restrict our discussion to Hoare Logic. The case for VDM is similar.

It is unrealistic to expect than one can design a verification calculus in which one can derive all valid correctness formulae. As a prerequisite for being complete, the underlying language in which one formulates assertions must be sufficiently expressive so than one may formulate all intermediate assertions required in the derivation, such as invariants. In particular, for an arbitrary postcondition and program, the assertion language must be able to express the weakest precondition^{10}

Definition 2.33 (Weakest Precondition) Let S be an arbitrary program and q ⊆ Σ be a set of states.

wp(S,q) =^{def} \{σ | ∃τ· (σ  \overline{S ^{}}τ) ∧ (τ ∈ q)\}

As soon as one considers languages with more interesting features such as loops, closure under weakest preconditions leads to logics which, by Gödels’s incompleteness theorem, cannot themselves be complete.

To avoid this problem, Cook has proposed that one investigates relative completeness in an attempt to separate the reasoning about programs from the reasoning about the underlying logical language. One only considers expressive first-order logics. Furthermore, rules of the verification calculus may be applied in a derivation if the logical side-condition is valid rather than derivable. In particular, completeness no-longer compares a proof-theoretic with a model-theoretic account.

Expressiveness and relative completeness are defined relative to an interpretation I which itself depends on a state σ. Variables are interpreted according to the value of program variables recorded in σ whereas the interpretation of constants e.g., 0, +, ∧, may vary from interpretation to interpretation. Following the presentation in (Cousot 1990), we characterise expressive interpretations by the following definition:

Definition 2.34 (Expressiveness) The interpretation I is said to be expressive for the programming language prog and the assertion language L if and only if

∀S· ∀q· ∃p · ∀σ · [[p]]I(σ)⇔ σ ∈ wp(S,λτ· [[q]]I(τ))

For the purpose of this section, we redefine validity and derivability of correctness formulae {p} S {q}⊆ L × prog × L relative to an arbitrary interpretation I.

Definition 2.35 (Semantics of Hoare Logic)

⊧_{Hoare}^{I}\{p\} S \{q\} =^{def} ∀σ · [[p]](I(σ))⇒ ∃τ· σ  \overline{S ^{}}τ∧ [[q]](I(τ))

Definition 2.36 (Hoare Logic) Let ⊢_{Hoare}^{I}{.} . {.} be the least relation satisfying

⊢_{Hoare}^{I}{p} skip {p}

10see e.g., the completeness proof on page 32

⊢_{Hoare}^{I}{p[x ↦ t]} x:= t {p}

\frac{⊢_{Hoare}^{I}\{p\} S_{1} \{r\} ⊢_{Hoare}^{I}\{r\} S_{2} \{q\}}{⊢_{Hoare}^{I}\{p\} S_{1}; S_{2} \{q\}}

\frac{⊢_{Hoare}^{I}\{p ∧ b\} S_{1} \{q\} ⊢_{Hoare}^{I}\{p ∧ ¬b\} S_{2} \{q\}}{⊢_{Hoare}^{I}\{p\} if b then S_{1} else S_{2} \{q\}}

\frac{∀t : W· ⊢_{Hoare}^{I}\{p ∧ b ∧ u = t\} S \{p ∧ u < t\}}{⊢_{Hoare}^{I}\{p\} while b do S \{p ∧ ¬b\}}

where (I(W),I(<) ) is well-founded.

\frac{⊢_{Hoare}^{I}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}^{I}\{p\} S \{q\}}

provided for all states σ, both [[p ⇒ p_{1}]]I(σ) and [[q_{1} ⇒ q]]I(σ) hold.

Definition 2.37 (Relative Completeness) Let I be an arbitrary expressive interpretation for the programming language prog and the assertion language L. Then, for any assertion p,q: L and program S : prog, the verification calculus is deemed to be relative complete, if one can show that ⊧_{Hoare}^{I}{p} S {q} implies ⊢_{Hoare}^{I}{p} S {q}.

Our machine-checked proof from page 32 is less general. We have merely shown that Hoare Logic is complete when one considers the standard interpretation for Lego’s internal logic. In particular, we have assumed that the interpretation supports our particular choice of the well-founded domain (Σ,<). To establish relative completeness one can make no further assumption about the interpretation besides being expressive in the sense of Def. 2.34.

It is well-known that in many circumstances, this requirement is neither sufficient nor necessary i.e. there may be other interpretations for which one may establish a complete verification calculus or there may be calculi which cannot be shown to be complete for certain interpretations considered expressive according to Def. 2.34:

• Clarke Jr. (1979) has shown that one cannot establish relative completeness for imperative programming languages which support procedures as parameters, recursion, static scoping, global variables and internal procedures. The completeness proof breaks down, because one is forced to consider interpretations with finite domains.

• Clarke, German & Halpern (1983) show that expressiveness is not sufficient when one considers recursive procedures. For the completeness proof to go through they restrict interpretations to being both expressible and Herbranddefinable^{11}.

11This refined condition is sufficient but not necessary (Grabowski 1985).

• Bergstra & Tucker (1982) show that, for simple imperative programs and Peano Arithmetic as underlying logical language, all interpretations give rise to a complete verification calculus

Incompleteness results in Hoare Logic are sometimes misunderstood. They emphasise that previously investigated notions of expressiveness do not capture the precise requirements of the underlying logical language. Furthermore, such incompleteness results should not be considered as criticism of Hoare Logic as a methodology. Regardless of what language features one is interested in, one ought to always be able to cook up a complete verification calculus in the style of Hoare logic with a particular assertion language and a particular (standard) interpretation. In particular, merely considering standard interpretations, none of the above (relative) incompleteness results go through. Aczel (1982b) writes

“ In the literature the question of the completeness of the proof rules has been entwined with issues concerning the expressiveness of the formal languages to be used in formulating the pre- and post-conditions of the program specifications. In my view the expressiveness of such formal languages should be of no concern when evaluating the completeness of proof rules for program verification. ”

In setting up the notion of validity and derivability of a correctness formula in a machine-checked development, our choice of a shallow embedding of assertions reflects Aczel’s view of completeness. We do not consider syntax of an assertion language. Instead assertions are directly captured by predicates on the state space, which, for a machine-checked development, are typically formulated in the native logic of the logical framework. This is a fundamentally different approach to Hoare Logic. Traditionally, assertions are considered to be simply formulae of first-order logic, which are interpreted in the usual way, except that the value of variables is determined by a state.

Following Aczel’s approach, working out a suitable syntax for assertions can be regarded as an orthogonal problem. In particular, our new approach to auxiliary variables has led to interpreting assertions as relations on the state space and a domain of auxiliary variables. This ought to be reflected by also distinguishing program variables from auxiliary variables at the syntactic level.

Furthermore, when new programming features lead to a new notion of the state space e.g., local variables, the syntax of assertions will have to be adapted to access all information in the state space. We refer to Section 3.3.4 on page 90 for further details. As this thesis establishes, using a shallow embedding of assertions to investigate completeness works smoothly for imperative programs in the presence of recursive procedures and blocks. Modern computer-aided proof systems support such shallow embeddings because of their powerful internal logics. We see no reason why it should not also work when adding further language features.

2.13 Related Work

For a survey of Hoare Logic, we recommend (Cousot 1990). Proving soundness and completeness for Hoare Logic in the context of simple imperative programs is standard. We commence by relating our approach of employing binary relations in assertions with previous work. We then compare our representation with previous machinechecked developments. We discuss related work concerning recursive procedures and local program variables throughout Cha. 3.

2.13.1 A Rigorous Treatment of Auxiliary Variables

The crucial rôle of auxiliary variables is well known in software engineering practice (Clint 1981, Vickers 1991), but previous presentations of Hoare Logic have swept this aspect under the carpet. The idea of representing assertions as binary relations to provide a semantic counterpart for auxiliary variables goes back to Apt & Meertens (1980). They show that with the new representation, in the general case of arbitrary context-free schemes a program is (partially) correct if and only if a suitable finite system of intermediate assertions can be found.

As far as we are aware, the only other variant of Hoare Logic in which both assertions are considered as binary relations to cater for auxiliary variables is due to Tarlecki (1985). In his system, the consequence rule has been built into every axiom and the rule for loops. Essentially, it only differs from Hoare Logic in that invariants inv must not mention auxiliary variables. The assumption of the rule is in VDM form i.e., the auxiliary variables in the precondition freeze the initial value of all program variables. The conclusion caters for arbitrary assertions p and q, and thus the rule of consequence has to be built in through further side-conditions. Another deviation from VDM’s rule of loops is that Tarlecki’s (1985) variant sofar is not required to be well-founded. Termination is guaranteed by a separate relation < relative to a termination measure t : Σ → W.

\frac{\{λ(Z,σ) · Z = σ ∧ inv(σ) ∧ eval(σ)(b) = true \} S \{λ(Z,τ)· sofar(Z,τ)∧ inv(τ)∧ t(τ)< t(Z)\}}{\{p\} while b do S \{q\}}

provided

(W,<) is well-founded

∀Z,σ · p(Z,σ) ⇒ inv(σ)  (Consequence)
∀σ · p(σ) ⇒ sofar(σ,σ)  (Reflexivity)
∀Z,σ · ∃η · sofar(Z,η)∧ inv(η) ∧ sofar(η,σ) ∧ inv(σ) ⇒ sofar(Z,σ) (Transitivity)

∀Z,τ· ∃σ · (p(Z,σ) ∧ sofar(σ,τ)∧ inv(τ)∧ eval(τ)(b)= false ∧ t(τ)≤ t(σ) ) ⇒ q(τ,Z) (Consequence)

The difficulty in program verification is largely due to discovering appropriate invariants (Gries 1981, Gries 1982, Ireland & Stark 1997). Not being able to mention auxiliary variables in the invariant appears to be a troublesome requirement.

2.13.2 Machine-Checked Developments

We are not aware of any previous machine-checked proofs of soundness and completeness for VDM. Unless otherwise indicated, the following formalisations deal with Hoare Logic in the setting of partial correctness. We first review work done in the Edinburgh Logical Framework where Hoare Logic is axiomatised and neither soundness nor completeness have been formally pursued. We then discuss formalisations in the system HOL in which soundness is established by deriving the axioms and rules of Hoare Logic from the underlying operational semantics. Finally, we present Nipkow’s (1998) machine-checked development in Isabelle which caters for both soundness and completeness by separating between the semantics and a notion of derivability in Hoare Logic.

2.13.2.1 An Axiomatic Approach to Hoare Logic

Employing the Edinburgh Logical Framework, in Mason’s (1987) work, terms are either boolean expressions inhabiting the type b, or expressions of type i. All program variables denote values of type i. In particular, one can inspect their value with the help of a global lookup function ! : VAR → i. This simplifies the treatment of assignments which can thus be represented without dependent types i.e.,

:= : (VAR× i) → prog .

Assertions are objects of type o which are constructed syntactically from expressions e.g.,

=_{o} : (i × i) → o
⇒_{o} : (o × o) → o

To exploit the provisions for λ-binding in the framework, instead of a purely syntactic account of quantification e.g.,

∃_{o} : (VAR× o) → o

Mason chose

∃_{o} : (i → o) → o .

Rather than providing an interpretation for terms and assertions, Mason suggests axiomatising a predicate ⊢_{o} to capture derivability (Harper, Honsell & Plotkin 1993).

Derivability in Hoare Logic is then also axiomatised by a set of rules where correctness formulae are triples of the form o×prog×o. The only difficulty is caused by the assignment axiom

{p[x ↦ t]} x:= t {p} ,  (2.54)

because one would have to formalise the notion of updating the value of variables in an assertion. This is the major problem in choosing a deep embedding for assertions. Unlike the shallow embedding scenario, moving to function application to relay the work to the internals of the logical framework does not succeed. In particular, the axiom

⊢_{Hoare}{p(t)}x:= t {p(x!)}  (2.94)

for p: i → o is unsound. Intuitively, the abstraction in p may not necessarily bind all occurrences of references to the program variable x. More precisely, for

p = λu· x! ≠_{int} u ,  (2.95)

we would be able to erroneously derive

⊢_{Hoare}{x! ≠_{int} 1 } x:= 1 {x! ≠_{int} x! } .

2.13.2.1.1 Non-Interference Therefore, Mason suggests that one augments the assignment axiom (2.94) with a predicate # ⊆ VAR× o on x and ∀p which guarantees that x does not interfere with p. The author characterises non-interference with six further rule schema e.g.,

x # y! provided x ≠ y

\frac{x # t_{1} x # t_{2}}{x # (t_{1} =_{i} t_{2})}

Similar ideas can be found in specification logic.

2.13.2.1.2 Explicit Quantification Another solution to ensure that x # ∀p has been proposed by Caplan (1995). He extends the notion of a correctness formula CF to include quantification of program variables

{.} . {.}: (o × prog × o) → CF
∀_{CF} : (VAR → CF) → CF

His axiom

⊢_{Hoare}∀_{CF}(λx· {p(t(x))}x:= t(x) {p(x!)})  (2.96)

for p: i → o and t : VAR → i ensures that the active program variable of an assignment is bound. His system also includes a rule for permuting the order of quantification and a proof transformation rule to add quantifiers. If one were to apply the rule of assignment to an assertion in which an active program variable occurs freely such as (2.95), α-conversion of the λ-calculus guarantees that the active identifier in (2.96) is suitably renamed. In particular, one may derive

⊢_{Hoare}∀_{CF}(λy· {p(1)} y:= 1 {p(y!)}) ,

whereas

⊢_{Hoare}∀_{CF}(λx· {p(1)} x:= 1 {p(x!)})

is not an instance of the axiom (2.96). Furthermore, it is not derivable by any other means due to the exclusion of any elimination rules for quantifiers at the level of correctness formulae.

Soundness is not formally investigated. Instead, Mason (1987) and Caplan (1995) verify on paper that their encoding adequately reflects Hoare’s (1969) axiomatic presentation.

2.13.2.2 Hoare Logic as a Set of Derived Rules

Machine-checked soundness proofs for verification calculi have been pioneered by Gordon (1989). Instead of axiomatising Hoare Logic, one axiomatises a more basic semantical account^{12} for the imperative programming language. Assertions are represented by predicates on the state space. Thus, updating assertions can be expressed in terms of updating states.

Similar to Def. 2.20 on page 30, the semantics of Hoare Logic^{13} ⊧_{Hoare}{.} . {.} can then be described in terms of the underlying semantics. Deriving the Hoare Logic rules of Figure 2.1 on page 30 e.g., showing

\frac{⊧_{Hoare}\{λσ · p(σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} ⊧_{Hoare}\{λσ · p(σ) ∧ eval(σ)(b) = false \}S_{2} \{q\}}{⊧_{Hoare}\{p\} if b then S_{1} else S_{2} \{q\}}

then corresponds to a proof of soundness.

2.13.2.2.1 Side Effects In (Homeier 1995) and^{14} (Homeier & Martin 1996), expressions are also restricted to two kinds, booleans and natural numbers. Moreover, program variables may only store natural numbers.

As a novelty, expressions in programming language may have side effects. For a program variable x, the expression + + x, when evaluated, first increments the value of x and then yields its new value. To cater for expressions with side-effects, it is advisable to provide a deep embedding of expressions e.g., the meaning of + + x is given operationally by

[figure: σ; x ⇓ n; τ; σ; + + x⇓ n + 1; τ[x ↦ n + 1 ]] .

With side-effects, the order of evaluation matters e.g.,

[figure: σ; e_{1} ⇓ n_{1}; η; η; e_{2} ⇓ n_{2}; τ; σ; e_{1}+ e_{2} ⇓ n_{1}+ n_{2}; τ] .  (2.97)

Additionally, the authors give a denotational account for the side-effect free fragment of the class of expressions e.g., one has also [[e_{1}+ e_{2}]]I(σ)=^{def} [[e_{1}]]I(σ)+ [[e_{2}]]I(σ).

12Gordon chooses relational denotational semantics.

13Gordon (1989) also shows how to extend this technique to total correctness, VDM, Dijkstra’s weakest preconditions and dynamic logic.

14Beware that the presentation of Hoare Logic in (Homeier & Martin 1996) is (trivially) incomplete because the authors have omitted a consequence rule. Thus, one can e.g. not derive {x = 1} skip {true }.

Their main contribution is to derive a Verification Condition Generator which is proven sound with respect to an operational semantics.

The expression + + x must however not occur in assertions and, thus, one has a choice between deep and shallow embedding of assertions. Homeier & Martin chose a deep embedding for assertions. Instead of axiomatising derivability or shallowly embedding assertions as predicates on the state space, assertions are evaluated relative to the standard^{15} interpretation e.g.,

[[t_{1} =_{nat} t_{2}]]I(σ)=^{def} [[t_{1}]]I(σ)= [[t_{2}]]I(σ)
[[∃_{o}x · p]]I(σ)=^{def} ∃n· [[p]]I (σ[x ↦ n])

To deal with the assignment axiom {p[x ↦ t]} x:= t {p}, one needs to implement a substitution function on assertions which has to translate subexpressions of t of the form + + x to x + n for some suitable n e.g., (x =_{nat} X)[x ↦ (+ + x) + (+ + x)]yields x + 4 =_{nat} X . One ought to also verify the correctness of substitution by showing the substitution lemma

[[p[x ↦ t]]]I(σ)= [[p]]I (τ[x ↦ [[n]]I(τ) ]) where σ  \overline{t ⇓ n ^{}}τ,

but this does not seem to have been formally established by the authors.

Norrish (1996,1997) is working on formalising the operational semantics of the programming language C. In particular, he introduces non-determinism for evaluating expressions, because, in accordance with the C standard (Schildt et al. 1990), side effects do not have to be applied as they are generated. Furthermore, expressions involving binary operators are not necessarily evaluated by evaluating one argument and then the next. Thus, the rule (2.97) is not sound for C.

Based on the detailed operational semantics, Norrish sketches Hoare Logic rules for a well-behaved class of C programs e.g.,

\frac{⊧_{Hoare}\{p\} S_{1} \{r\} ⊧_{Hoare}\{r\} S_{2} \{q\}}{⊧_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

provided S_{1} contains no interrupt statements.

2.13.2.3 Semantics and Derivability of Correctness Formulae in Hoare Logic

Nipkow (1998) has been the first to conduct a machine-checked completeness proof for Hoare Logic dealing with simple imperative programs. To formally deal with completeness, one needs to distinguish between semantics ⊧_{Hoare}{.} . {.} and derivability ⊢_{Hoare}{.} . {.} of correctness formulae. For simple imperative programs, instead of appealing to the MGF, completeness is established as a corollary of

⊢_{Hoare}{wp(S,q)}S {q}

15All logical connectives are directly embedded in the native logic of the framework HOL.

for arbitrary programs S and assertions^{16} q. Following (Gordon 1989), assertions are encoded as predicates on the state space. Thus, one does not have to worry about how to overcome expressivity conditions such as having to syntactically formulate an assertions which corresponds to wp(S,q).

The development follows the first 100 pages of Winskel’s (1993) textbook on semantics. It includes various operational and denotational accounts together with equivalence proofs. Furthermore, Nipkow produces machine-checked soundness and completeness proofs for Hoare Logic and a Verification Condition Generator. This work uncovered a mistake in Winskel’s (1993) completeness proof for Hoare Logic.

16Induction on S does not go through if one investigates recursive procedures. It then becomes essential to employ the MGF property.

Recursive Procedures and Local Program Variables

In this chapter, we extend our soundness and completeness results to more interesting program language features. We consider recursive procedures and local program variables with static binding. We produce machine-checked soundness and completeness proofs for parameterless recursive procedures and local program variables. We sketch extensions to mutually recursive procedures and call-by-value parameters.

To adequately represent recursive procedures, we present the verification calculi as a Gentzen-style formal system with contexts. As a new contribution, we improve Sokołowski’s (1977) rule for recursive procedures by allowing a well-founded termination argument. In our rules for local program variables, we exploit the fact that, due to our choice of a shallow embedding, assertions may refer to the values of all program variables, and not just those in scope.

In view of the history of unsound and incomplete verification calculi for imperative programs dealing with recursive procedures, the surprising result of this chapter is that, due to our new approach to auxiliary variables, none of the soundness and completeness results are particularly difficult to achieve. Moreover, for Hoare Logic in the setting of total correctness, our results are an improvement over previous work. We require only two thirds of the total number of America & de Boer’s (1990) nine rules.

The outline of this chapter is as follows. We first consider recursive procedures. We then focus on a verification calculus for simple imperative programs and local program variables. In Sect. 3.3, we combine recursive procedures with local program variables. Finally, in Sect. 3.4, we sketch extensions to call-by-value parameter passing. We compare our approach to previous work throughout this chapter.

3.1 Parameterless Recursive Procedures

In this section, we extend both Hoare Logic and VDM with parameterless recursive procedures and establish that the new verification calculi are sound and complete. In the previous chapter, we had urged that auxiliary variables should be taken seriously for a more adequate formalisation of Hoare Logic. This section substantiates this claim.

Apt (1981) and America & de Boer (1990) present a verification calculus in the style of Hoare Logic in which, besides a decomposition rule for procedure invocations, they need to add three further structural rules in addition to Hoare’s (1969) consequence rule to guarantee completeness. We are able to show that those four structural rules are subsumed by our new consequence rule.

As a further contribution, we show that Hoare’s (1971) rule of adaptation, a specially tailored rule structural rule essential in the presence of recursion, is just a trivial instance of our new consequence rule, which we had motivated in the previous chapter for simple imperative programs.

Again, proving these meta-theoretical results for VDM is analogous and we merely highlight technical differences for VDM.

3.1.1 Declaring and Invoking Procedures

Restricting our attention to a single procedure declaration, invoking this procedure can be achieved unambiguously with a constructor call : prog.

p. 156 Definition 3.1 (Syntax) The class of imperative programs S: prog which may appeal to a recursive procedure is defined by the BNF grammar

S::= skip | x:= t | S_{1}; S_{2} | if b then S_{1} else S_{2} | while b do S | call

where x: VAR, t : expression(sort(x)) and b: expression(bool).

We assume that the procedure declaration is given statically e.g., at compile-time. In the sequel, let S_{0} : prog denote the body of the procedure.

p. 157 Example 3.2 (Factorial) The body of a recursive procedure computing the factorial of x is given by

S_{0} =^{def} if x = 0 then y:= 1
else begin x:= x − 1; call; x:= x + 1; y:= y ∗ x end

Notice that the argument x and the result y are not parameters, but global variables. Clearly, x plays the rôle of a call-by-value parameter. The procedure ensures that its value remains invariant. Similarly, the variable y is a designated call-by-name parameter.

3.1.2 Structural Operational Semantics

Whenever a procedure invocation is encountered, computation proceeds by executing the body of the procedure S_{0}.

p. 157 Definition 3.3 (Semantics) We extend the operational semantics from Def. 2.8 by

[figure: σ; S_{0}; τ; σ; call; τ] .  (3.1)

Again, notice that a denotational characterisation would require more sophisticated mathematical notions such as least fixpoints.

3.1.3 Hoare Logic with Contexts

Without recursion, deriving a correctness formula is easy. Whenever one encounters a procedure invocation, one has to proceed by instead analysing the procedure body (Apt 1981) i.e.,

\frac{⊢_{Hoare}\{p\} S_{0} \{q\}}{⊢_{Hoare}\{p\} call \{q\}} .

This rule would however lead to infinite derivations when S_{0} calls itself. Induction comes to the rescue. Let us first omit the issue of termination. We may simply assume ⊢_{Hoare}{p} call {q} to conclude ⊢_{Hoare}{p} S_{0} {q} (Hoare 1971) i.e.,

{p} call {q}⊢_{Hoare} {p} S_{0} {q} ⊢_{Hoare} {p} call {q} .  (3.2)

This rule introduces a fundamental change in deriving correctness formulae. Derivations are now to be considered with respect to a context. Instead of a Hilbert-style calculus, Hoare Logic now amounts to a Gentzen-style sequent calculus. Actually, the rule above is to be understood with respect to an arbitrary context

\frac{\{p\} call \{q\},Γ ⊢_{Hoare} \{p\} S_{0} \{q\}}{Γ ⊢_{Hoare} \{p\} call \{q\}} .  (3.3)

The previous presentation needs to be revised to support contexts e.g.,

Γ ⊢_{Hoare}{p} skip {p}

\frac{Γ ⊢_{Hoare}\{p\} S_{1} \{r\} Γ ⊢_{Hoare}\{r\} S_{2} \{q\}}{Γ ⊢_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

Furthermore, such sequent calculi are usually equipped with a collection of structural rules, see e.g. (Takeuti 1975):

\frac{Γ ⊢ P}{D,Γ ⊢ P}  (Weakening)
\frac{D,D,Γ ⊢ P}{D,Γ ⊢ P}  (Contraction)
\frac{Γ,C,D,Δ ⊢ P}{Γ,D,C,Δ ⊢ P}  (Exchange)

\frac{Γ ⊢ P P,Δ⊢ Q}{Γ,Δ ⊢ Q}  (Cut)

P ⊢ P  (Initial Sequent)

As usual, we abbreviate ∅ ⊢ P by ⊢ P.

Olderog (1981) has observed that this presentation is redundant for Hoare Logic in the sense that if Γ ⊢ P is derivable than there is also a standard proof which only involves a single application of the procedure rule (3.3). Since we are only interested in deriving correctness formulae in the empty context, it suffices to consider the original version (3.2) after all. Thus, the antecedent contains at most one correctness formula and, apart from the Initial Sequent, none of the structural rules are actually required.

Redundancy in a formal system may be a healthy feature if the additional flexibility gives rise to easier derivations. This is however not the case here when restricting antecedents to at most one correctness formula. Hoare Logic (and VDM) is a syntax-directed methodology (Harel 1980). A rule, read backwards in refinement style, reduces the complexity by focussing the attention on strict subcomponents of the program to be verified. Intuitively, when one has to verify the correctness of {p} call {q} one may reduce this task to showing {p} S_{0} {q}, where we may discharge any further intermediate obligations of the form {p′} call {q′} with the assistance of the consequence rule. Just like in the case of a loop, it is crucial to find an appropriately weak precondition p so that all appeals to {p} call {q} can be resolved.

Furthermore, unlike traditional sequent calculi, the antecedent may only mention correctness formulae dealing with procedure invocation. In particular, due to our restriction to a single procedure, it suffices to consider contexts with at most one correctness formulae about procedure invocations.

p. 158 Definition 3.4 (Contexts) A context Γ : Context can be represented by the BNF grammar Γ ::= ∅ | {p} call {q}.

Since we are working in a logical framework which supports contexts at the meta level, the question arises as to whether one can avoid explicitly introducing contexts at the object level. Having already introduced a Hilbert-style notion of derivability, one may be tempted to represent the sequent P ⊢ Q by ⊢ P ⇒ ⊢ Q. However, in general, P ⊢ Q is a stronger property.

Consider a formal system which is not complete i.e., there exists a formula P such that one can neither derive P, nor its complement i.e., ⊬ P and P ⊬ false. As ⊢ P does not hold, we may infer any property from it, in particular, ⊢ P ⇒ ⊢ false; yet, by assumption, P ⊢ false does not hold. Moreover, in the setting of Hoare Logic, de Bakker (1980) shows that ⊢ P ⇒ ⊢ Q does not imply P ⊢ Q.

An orthogonal complication arises because we define derivability of correctness formulae by considering the smallest fixpoint of a set of predicates on a relation ⊢_{Hoare} {.} . {.}. A rule of the form

(⊢ P ⇒ ⊢ Q) ⇒ ⊢ R  (3.4)

is not an adequate representation of

(P ⊢ Q)⇒ ⊢ R .  (3.5)

Assume that we have to derive ⊢ R. By (3.4), it suffices to show that from ⊢ P, we can infer ⊢ Q. We may now refine ⊢ Q and discharge any intermediate proof obligation ⊢ P. However, due to having closed off the set of rules by the smallest fixpoint, we may additionally assume that ⊢ P has been derived by one of the constructors. In particular, we may analyse the derivation of ⊢ P. This is clearly not the intention of (3.5).

Constructors of the form (3.4) are not permitted in inductive definitions due to the negative occurrence of ⊢ in the premiss. Such a syntactic check guarantees that there is a smallest fixpoint. Due to the structure of the other rules of Hoare Logic, it is however legitimate to add the rule

\frac{⊢_{Hoare}\{p\} call \{q\} ⇒ ⊢_{Hoare}\{p\} S_{0} \{q\}}{⊢_{Hoare}\{p\} call \{q\}}  (3.6)

and derive that the set of rules has a smallest fixpoint. One can show that it leads to a consistent definition in that only valid correctness formulae may be derived. Moreover, this system is elementary equivalent to the sequent style presentation i.e., it admits precisely the same correctness formulae. However, adding further (sound) rules may jeopardise the soundness of the whole system (Trakhtenbrot et al. 1984).

We have decided against working with a set of rules involving (3.6), because

• our logical framework does not offer any help in closing a set of rules unless it is an inductive definition.

• In our opinion, this presentation is not adequate in that it does not reflect the intuition of the informal rule (3.2).

• The calculus is not sound in the usual sense. In particular, adding further (sound)

rules may yield an unsound formal system. We thus prefer to formalise Hoare Logic with contexts as a Gentzen-style system.

3.1.4 Total Correctness

To guarantee termination, one needs to introduce a termination measure on states. In the case of loops, we compared states before and after an execution of the loop body. For recursive procedures, we can guarantee progress if recursive procedure invocations are only permitted in strictly smaller states with respect to the termination measure. Sokołowski (1977) suggests

\frac{∀n: nat· \{p(n)\}call \{q\}⊢_{Hoare}\{p(n + 1)\} S_{0} \{q\}}{⊢_{Hoare}\{∃n: nat· p(n)\}call \{q\}} provided ¬p(0). (3.7)

This rule is both sound and sufficient to obtain a complete set of rules for recursive procedures (America & de Boer 1990, Schreiber 1997). However, it is too cumbersome to apply in most examples, because of the requirement that the termination measure decreases by exactly one. In particular, in the Quicksort algorithm, which we verify in Chap. 4, the sorting procedure is invoked recursively where the array size is strictly smaller, but not necessarily by one element. We have therefore extended Sokołowski’s rule to an arbitrary well-founded measure

∀t : W· {∃u: W· p(u)∧ u < t}call {q}⊢_{Hoare}{p(t)}S_{0} {q}

⊢_{Hoare}{∃t : W· p(t)}call {q}

where (W,<) is well-founded. (3.8)

As Apt (1981) points out, merely adding such a rule for procedure invocations in a setting with Hoare’s (1969) consequence rule does not lead to a complete system. We will show that a cure is to employ the new rule of consequence (2.70) which is more flexible in dealing with auxiliary variables^{1}. We have collected the set of rules dealing with recursive programs which we have formalised in Figure 3.1 on the next page. Other solutions have been more elaborate. More precisely, Apt (1981) proposes to add three more rules to be able to modify auxiliary variables in correctness formulae dealing with recursive procedures, see Figure 3.2 on page 64.

However, America & de Boer (1990) have shown that this extension yields an unsound system. Their remedy consists of distinguishing different kinds of (auxiliary) variables. In particular, the universally quantified counter variable of the procedure invocation rule must not occur in the list of variables \overline{Y}and \overline{Z} in the Substitution and Elimination rules of Figure 3.2 on page 64.

Having introduced Hoare Logic as a sequent calculus, we extend the semantics to incorporate contexts by

1We have demonstrated in (Schreiber 1997) that soundness and completeness can also be established when replacing the well-founded measure by the original version from (3.7).

{p} call {q}⊢_{Hoare}{p} call {q}  (3.9)
Γ ⊢_{Hoare}{p} skip {p}

Γ ⊢_{Hoare}{λ(Z,σ) · p(Z,σ[x ↦ eval(σ)(t)])}x:= t {p}

\frac{Γ ⊢_{Hoare}\{p\} S_{1} \{r\} Γ ⊢_{Hoare}\{r\} S_{2} \{q\}}{Γ ⊢_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

\frac{Γ ⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} Γ ⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = false \}S_{2} \{q\}}{Γ ⊢_{Hoare}\{p\} if b then S_{1} else S_{2} \{q\}}

\frac{∀t : W· Γ ⊢_{Hoare} \left\{{ \atop p̂}\right\} S \{λ(Z,τ)· p(Z,τ)∧ u(τ)< t \}}{Γ ⊢_{Hoare}\{p\} while b do S \{λ(Z,τ)· p(Z,τ)∧ eval(τ)(b)= false \}}

where p̂(Z,σ) =^{def} p(Z,σ) ∧ eval(σ)(b) = true ∧ u(σ) = t

and (W,<) is well-founded.

∀t : W· {λ(Z,σ) · ∃u: W· p(u)(Z,σ)∧ u < t}call {q}⊢_{Hoare}{p(t)}S_{0} {q}

⊢_{Hoare}{λ(Z,σ) · ∃t : W· p(t)(Z,σ)}call {q}

where (W,<) is well-founded. (3.10)

\frac{Γ ⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{Γ ⊢_{Hoare}\{p\} S \{q\}}

where p_{1},q_{1} : Assertion(U) and p,q: Assertion(T) for arbitrary types T and U

and ∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) ) . Figure 3.1: Hoare Logic for Recursive Procedures – Total Correctness

\{p\} call \{q\} ; \{p[\overline{Z} ↦ \overline{Y}]\} call \{q[\overline{Z} ↦ \overline{Y}]\}

where \overline{Z}∩ free(S_{0}) = ∅ and \overline{Y}∩ free(S_{0}) = ∅. (Substitution)

{p} call {q} ; {p ∧ r} call {q ∧ r}  where free(r)∩ free(S_{0}) = ∅.  (Invariance)

\{p\} call \{q\} \{∃\overline{Z}· p\}call \{q\}  where \overline{Z}∩ free(S_{0},q)= ∅.  (Elimination)

Figure 3.2: Hoare Logic – Additional Structural Rules (Apt 1981)

p. 160 Definition 3.5 (Semantics of Hoare Logic) We define Γ ⊧_{Hoare}{p} S {q} by case analysis on the structure of the context Γ:

Γ ⊧_{Hoare}{p} S {q} =^{def}
\{ \begin{matrix}⊧_{Hoare}\{p\} S \{q\} & for Γ = ∅, \\ (⊧_{Hoare}\{p′\} call \{q′\}) ⇒ (⊧_{Hoare}\{p\} S \{q\}) & for Γ = \{p′\} call \{q′\}. \end{matrix}

Notice that, semantically, existential quantification in the precondition corresponds to universal quantification at the level of a correctness formula:

p. 160 Lemma 3.6 (Existential Quantification in Preconditions) For all programs S, indexed preconditions p: U → Assertion(T) and postconditions q: Assertion(T) for arbitrary types T and U, the propositions

⊧_{Hoare}{λ(Z,σ) · ∃t· p(t)(Z,σ)}S {q} and ∀t· ⊧_{Hoare}{p(t)}S {q}

are equivalent. Proof Existential quantification on the left side of an implication is equivalent to universal quantification. □ However, for derivability in Hoare Logic, it is preferable to work with the two existential quantifiers in rule (3.8):

• Replacing the existential quantifier in the premiss would lead to a more complicated notion of contexts.

• Introducing universal quantifiers at the level of correctness formulae in the conclusion alters the notion of derivability.

3.1.5 Soundness

p. 162 Theorem 3.7 (Soundness) Let p and q be arbitrary assertions and S be any program. For an arbitrary context Γ, whenever Γ ⊢_{Hoare}{p} S {q} is derivable, Γ ⊧_{Hoare}{p} S {q} holds. Proof by induction on the derivation of

Γ ⊢_{Hoare}{p} S {q}  (3.11)

Consider the new case that (3.11) has been derived by applying the rule for procedure invocations (3.10). From the induction hypothesis

∀t : W· {λ(Z,σ) · ∃u: W· p(u)(Z,σ)∧ u < t}call {q}⊧_{Hoare}{p(t)}S_{0} {q} (3.12)

for some well-founded structure (W,<), we need to show

⊧_{Hoare}{λ(Z,σ) · ∃t : W· p(t)(Z,σ)}call {q} .

Appealing to Lemma 3.6, we may externalise the quantification in the proof obligation, leading to

∀t : W· ⊧_{Hoare}{p(t)}call {q} .

We pursue well-founded induction. We thus have to establish ⊧_{Hoare}{p(t)}call {q} for a fixed t : W and may additionally appeal to the hypothesis

∀u: W· u < t ⇒ ⊧_{Hoare}{p(u)}call {q}

which is equivalent to the required instance of the context in (3.12). □

3.1.6 Completeness

Completeness is again a straight-forward corollary of the MGF property.

p. 165 Theorem 3.8 (MGF) ⊢_{Hoare}\{ λ(Z,σ) · σ \overline{S ^{}}Z\} S \{λ(Z,τ)· Z = τ\}

In its proof, in the case of a procedure invocation, we have to plant a termination measure in the precondition so that ∃t : W· ψ(t)(τ,σ)is equivalent to σ \overline{call ^{}}τ for a suitable ψ. We exploit ideas from a completeness proof by America & de Boer (1990) in which ψ(t)(τ,σ) asserts that the execution of σ \overline{call ^{}}τ can be accomplished with a recursive depth of at most t : nat. In a denotational setting with [[.]] : Σ ⇀ Σ, they define

ψ(t)(τ,σ) =^{def} [[S^{[t]}]](σ) = τ∧ τ ≠⊥
where S^{[t]}=^{def} S[S_{0}^{(t)}/call],
S_{0}^{(0)} =^{def} Ω
and S_{0}^{(t+1)} =^{def} S_{0}[S_{0}^{(t)}/call] .

This seems cumbersome. Denotational semantics gives a too abstract view for expressing such fine-grained attributes and one has to simulate a low-level account by selectively substituting the non-terminating program Ω and deduce properties by checking if test runs terminate. In particular, Ω has nothing to do with the notion of recursive depth. We prefer the following definition which is simply an annotated version of the operational semantics where every rule apart from procedure invocation preserves recursive depth.

p. 163 Definition 3.9 (Recursive Depth) Let σ  \overline{S  n} τ⊆ Σ × prog × nat × Σ be a new judgement which is defined as

the least relation satisfying

σ  \overline{skip  n} σ
σ  \overline{x:= t n} σ[x ↦ eval(σ)(t)]
[figure: σ; S_{1}; n; η; η; S_{2}; n; τ]
σ  \overline{^{S_{1}; S_{2}} n} τ
σ  \overline{S_{1}  n} τ
σ

if b then S_{1} else S_{2}
n

τ

provided eval(σ)(b) = true .

σ  \overline{S_{2}  n} τ
σ

if b then S_{1} else S_{2}
n

τ

provided eval(σ)(b) = false .

σ \overline{while b do S_{n}} σ provided eval(σ)(b) = false .
[figure: σ; S; n; η; η; while b do S; n; τ]
σ \overline{while b do S_{n}} τ

provided eval(σ)(b) = true .

[figure: σ; S_{0}; n; τ; σ; call; n+1; τ]

p. 164 Lemma 3.10 (Non-strict Recursive Depth) Bounded Recursive Depth is not a strict

bound i.e., if σ  \overline{S  n} τthen for all m ≥ n, we may infer σ  \overline{S  m} τ.
Proof by induction on the derivation of σ  \overline{S  n} τ.  □

p. 164 Lemma 3.11 (Bounded Recursive Depth) 1. If a program terminates, the recursive depth must be finite i.e., if σ \overline{S ^{}}τ

then there exists a bound n such that σ  \overline{S  n} τ.

2. Bounded recursive depth is a mere annotation of operational semantics i.e., for

an arbitrary depth n, σ \overline{S n} τimplies σ \overline{S ^{}}τ. Proof

1. by induction on the derivation of σ \overline{S ^{}}τ. We need to specify a concrete recursive depth bound. If σ \overline{S ^{}}τ has been derived by one of the axioms (2.4), (2.5) or (2.9), we choose 0. The induction hypotheses for conditional and procedure invocation predetermine the overall recursive depth. Consider the case of sequential composition. From the induction hypotheses

∃n· σ  S_{1}  n τ and ∃n· τ  \overline{S_{2}  n} η

we want to conclude ∃n· σ \overline{^{S_{1}; S_{2}} n} η. Let n_{1} and n_{2} be recursive depth bounds such that

σ  \overline{S_{1}  n_{1}} τ and τ  \overline{S_{2}  n_{2}} η .

Resorting to Lemma 3.10, we weaken these to

σ  \overline{S_{1}  max(n_{1},n_{2})} τ and τ  \overline{S_{2}  max(n_{1},n_{2})} η .

Finally, the rule for sequential composition unites them to

σ \overline{^{S_{1}; S_{2}} max(n_{1},n_{2})} η .
2. by induction on the derivation of σ  \overline{S  n} τ
□

The MGF proof is conducted by induction on the structure of programs. We abbreviate

ψ(n)(Z,σ) =^{def} σ  \overline{call n+1} Z
ψ̂(n)(Z,σ) =^{def} ∃u: nat· ψ(u)(Z,σ) ∧ u < n
q(Z,τ)=^{def} Z = τ .

In the case of a procedure invocation, we cannot appeal to an induction hypothesis. Instead we derive the following pseudo-induction hypothesis which allows us to apply the rule for procedure calls (3.10). p. 165 Lemma 3.12 (Completeness Step) For an arbitrary recursive depth n, one may derive

{ψ̂(n)}call {q}⊢_{Hoare}{ψ(n)} S_{0} {q} .

Proof Appealing to the consequence rule and the definition of bounded recursive depth, we equivalently transform the proof obligation into

\{ψ̂(n)\}call \{q\}⊢_{Hoare}\{ λ(Z,σ) · σ  \overline{S_{0}  n} Z\} S_{0} \{q\} .

This presentation makes induction more amenable to the structure of the procedure body S_{0}. The cases where S_{0} is not a procedure invocation are isomorphic to the corresponding

proofs of the MGF property 2.10.3.3 on page 40. One only needs to ensure that recursive depth is preserved. In particular, the correctness formula in the context is ignored.

In the case of S_{0} = call, from being able to access the context

{ψ̂(n)}call {q}⊢_{Hoare}{ψ̂(n)}call {q} ,

we need to show

\{ψ̂(n)\}call \{q\}⊢_{Hoare}\{ λ(Z,σ) · σ  \overline{call  n} Z\} call \{q\} . (3.13)

To arrive at (3.13), we appeal to the rule of consequence. As a side-condition, given arbitrary states τ and σ such that σ \overline{call n} τ, we have to find a suitable recursive depth u < n such that σ \overline{call u+1} τ. We choose u = n − 1. Notice that n cannot have been 0 due to the definition of recursive depth.

□

It is now easy to prove the MGF Theorem: Proof of Theorem 3.8 by induction on the structure of S. In the case of a procedure invocation, we have to establish

⊢_{Hoare}\{ λ(Z,σ) · σ  \overline{call ^{}}Z\} call \{q\} .  (3.14)

Applying the procedure invocation rule to the pseudo-hypothesis of Lemma 3.12, we may derive

⊢_{Hoare}{λ(Z,τ)· ∃n: nat· ψ(n)(Z,σ)}call {q} .

The consequence rule mediates the differences in the preconditions and, given arbitrary states τ and σ such that σ \overline{call ^{}}τ, one has to find a depth u such that σ \overline{call u+1} τ. Applying Lemma 3.11.1 to the assumption, we may extract a depth n satisfying σ \overline{call n} τ. Again, we choose u = n − 1 as n cannot have been 0 due to the definition of recursive depth. □

For completeness, we are only interested in top-level correctness formulae in the empty context. Specifically, the presentation of Figure 3.1 on page 63 is not complete for arbitrary contexts. The rule for procedure calls has been tailored to only yield atomic correctness formulae.

p. 165 Corollary 3.13 (Completeness) Whenever ⊢_{Hoare}{p} S {q} is derivable from the axioms and rules of Figure 3.1 on page 63, ⊧_{Hoare}{p} S {q} holds.

Proof Completeness follows immediately from the MGF Theorem. See the proof of Corollary 2.31 on page 41 for further details. □

3.1.7 An Example Derivation

In this section, we employ the new set of rules to prove that an algorithm to compute y = x! leaves the input x invariant. In particular, we give an example of how to invoke the rule for recursive procedures for a suitable termination measure. The algorithm can be shown correct as easily with Sokołowski’s (1977) rule for procedures. For a more complex example, we refer to the case study of Chapter 4.

p. 165 Lemma 3.14 (Invariance Property of the Factorial Algorithm) For the declaration of the procedure in Example 3.2 on page 58, we may derive

⊢_{Hoare}{x = X} call {x = X} .

An interesting aspect of this proof is that our stronger rule of consequence is essential. In particular, Apt (1981) has proven that if one only uses Hoare’s (1969) consequence rule, Lemma 3.14 does not hold and thus one would have a seriously incomplete verification calculus. The proof also motivates that in practice it is convenient to be able to use a rule of adaptation to compute preconditions. It will turn out that such a rule is merely a trivial instance of our new rule of consequence.

Proof Termination is guaranteed because the recursive depth is bounded by the initial value of the input parameter x. Employing the consequence rule, we record the convergence measure

⊢_{Hoare}{∃n· p(n)}call {x = X} with p(n) =^{def} x = X ∧ n = x .

Thus the way has been smoothed to invoke the procedure invocation rule^{2} (3.10) leading to the proof obligation

Γ_{n} ⊢_{Hoare}{p(n)}S_{0} {x = X}

where Γ_{n} = {∃u· p(u)∧ u < t}call {x = X} for an arbitrary bound n. (3.15)

We may retrieve the information in the context by axiom (3.9)

Γ_{n} ⊢_{Hoare}{∃u· p(u)∧ u < t}call {x = X} .  (3.16)

The strategy is now to successively apply the Hoare Logic rules corresponding to the outermost constructor of the desired proof obligation (3.15). A proof obligation referring to a procedure invocation can be tackled by the assumption (3.16).

We commence with the rule for conditionals. The then branch is easily resolved. Otherwise, we have to show

Γ_{n} ⊢_{Hoare} {∃u· p(u)∧ u < t ∧ x ≠ 0} x:= x − 1; call; x:= x + 1; y:= y ∗ x {x = X} .

Since the assignment axiom is left-constructive (Dahl 1992) in that it accepts an arbitrary postcondition, a sound strategy^{3} is to unroll the program from behind. Thus, when the rule for sequential composition leads to branching in the proof obligation, one ought to concentrate on the second. Eventually, this approach leads to the two goals

Γ_{n} ⊢_{Hoare}{∃u· p(u)∧ u < t ∧ x ≠ 0} x:= x − 1 {?i}
Γ_{n} ⊢_{Hoare}{?i}call {x + 1 = X}  (3.17)

for some intermediate assertion ?i. Given a concrete instance, we can employ the rule of consequence to derive (3.17) from (3.16). Since ?i is a postcondition in a further proof obligation, it would be best if ?i would be as weak as the consequence rule admits. In other words, it would be pragmatically useful to also have a leftconstructive version of the rule of consequence. We discuss such rules, also called rules of adaptation, in more detail in the following section. A suitable assertion would be ?i =^{def}x + 1 = X . In the next section, we present a rule which automatically computes an equivalent version. □

2To successfully apply Sokołowski’s (1977) version, one has to ensure that p(0) does not hold. A suitable choice would be p(n) =^{def} x = X ∧ n = x + 1 , because all variables are declared as natural numbers.

3in particular, for Verification Condition Generators

Notice that regardless of the choice for ?i in the above proof, Hoare’s (1969) rule of consequence would fail at this point, because the postconditions of (3.16)and (3.17) are incompatible. We had already discussed this deficiency of the standard presentation of Hoare Logic in Sect. 2.10.3. It is an intrinsic problem for which an inadequate treatment of auxiliary variables is to blame.

Without procedures, completeness is not threatened because a mismatch of auxiliary variables such as (3.28) can always be reconciled by rebuilding the complete derivation. One may compensate by readjusting the auxiliary variables accordingly in the leaves of the proof tree. Whenever axiom (2.71) or (2.72) is encountered one instead chooses an equivalent version where auxiliary variables have been shifted. Such a patch cannot be applied in the presence of rule (3.8). It also introduces an axiom i.e., {∃u· p(u)∧ u < t}call {q} for some t : (W,<), but, unlike the axiom for the empty program or an assignment, the pre- and postcondition is bound by the specific assertions of a correctness formula in the derivation.

In particular, completeness could be achieved in the standard presentation by integrating our new consequence rule in the procedure invocation rule. We will discuss a similar rule in the following section. However, we feel that pragmatically, it is better to provide an improved consequence rule at all times.

Previously, a common approach has been to retain Hoare’s consequence rule and adopt a flurry of further rules to achieve completeness e.g.,

“ It will turn out that we need five additional [rules], before a completeness theorem becomes possible. ” (de Bakker 1980)

Perhaps even more worrying is that fact that four of these are actually employed in the verification of the invariance property of the factorial function. In such a setting, using Hoare Logic to derive properties of even simple programs appears to be unnecessarily complicated.

3.1.8 Rules of Adaptation

Among the additional rules to add in order to retain completeness for imperative programs dealing with recursive procedures, a rule of adaption has been considered by various authors e.g., (Hoare 1971, Morris n.d., London, Guttag, Horning, Lampson, Mitchell & Popek 1978, Gries & Levin 1980).

In general, rules of adaptation are of the form

\frac{Γ ⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{Γ ⊢_{Hoare}\{p\} S \{q\}}

for arbitrary assertions p_{1},q_{1},q, and particular proposals for the adapted precondition p. Ideally, the rule should be left-maximal (Dahl 1992) i.e., the precondition p should be the weakest possible so that

∀S· ⊧_{Hoare}{p_{1}} S {q_{1}} ⇒ ⊧_{Hoare}{p} S {q}  (3.18)

holds.

Not distinguishing between program and auxiliary variables, Hoare (1971) has proposed

p(σ) =^{def} ∃Z_{1}· p_{1}(σ) ∧ ∀τ· q_{1}(τ)⇒ q(τ)  (3.19)

where Z_{1} is a list of all variables^{4} free in p_{1}, q_{1}, but not in q.

However, while adding Hoare’s rule of adaptation leads to a complete verification calculus, Morris (n.d.) and Olderog (1983) have shown that, in general, (3.19) is not the weakest possible. Morris (n.d.) points out two^{5} instructive counter examples:

Example 3.15 Let

p_{1}(σ) =^{def} q_{1}(σ) =^{def}σ(Z) > 0 ∧ σ(x) > 0
q(σ) =^{def}σ(Z) ≥ 0 ∧ σ(x) > 0

where Z does not occur in the program under consideration. The weakest precondition is then given by λσ · σ(Z) ≥ 0 ∧ σ(x) > 0 . However, (3.19) requires the stronger λσ ·σ(Z) > 0 ∧σ(x) > 0 (modulo equivalence transformations), because the auxiliary variable Z occurs in both premiss and conclusion.

This problem can be solved by formally treating assertions as relations of auxiliary variables and states i.e.,

p(Z,σ) =^{def} ∃Z_{1}· p_{1}(Z_{1},σ) ∧ ∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ) .  (3.20)

A corresponding rule of adaptation (3.21) is obviously a trivial instance of our rule

\frac{⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}\{λ(Z,σ) · ∃Z_{1}· p_{1}(Z_{1},σ) ∧ ∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)\}S \{q\}}  (3.21)

Figure 3.3: Rule of Adaptation

of consequence (2.70). Conversely, if one replaces the new consequence rule by the rule of adaptation and Hoare’s (1969) rule of consequence, one retains a sound and complete verification calculus.

4In practice, this would be a list of auxiliary program variables.

5A third one only applies to partial correctness

Morris’ second example shows that the choice for the auxiliary variable Z_{1} may have to depend on the value of variables in the final state:

Example 3.16 Let

p_{1}(Z,σ) =^{def} Z = 0 ∨ Z = 1
q_{1}(Z,τ)=^{def} τ(x)≠ Z
q(Z,τ)=^{def} σ(x) ≠ 0 ∧ σ(x) ≠ 1

The weakest precondition is then given by true . However, both (3.19) and the weaker (3.20) are equivalent to false.

Relaxing the precondition p so that the witness Z_{1} can benefit from inspecting the final value of program variables according to τ leads to

p(Z,σ) =^{def} ∀τ· ∃Z_{1}· p_{1}(Z_{1})(σ) ∧ (q_{1}(Z_{1})(τ)⇒ q(Z)(τ)) .  (3.22)

We do not know if this is the weakest possible precondition for a rule of adaptation in the setting of total correctness. Thus, we can also not answer the question if its corresponding rule of consequence is optimal. We have used the weaker version (3.21) throughout this thesis. The problematic issue raised in the last example did not surface neither in any of the soundness and completeness proofs, nor in any of the examples.

It seems to be easier to design and verify an optimal rule of adaptation for partial correctness. In particular, Morris (n.d.) has been able to show that

p(Z,σ) =^{def} ∀τ· (∀Z_{1}· p_{1}(Z_{1},σ) ⇒ q_{1}(Z_{1},τ)) ⇒ q(Z,τ)

is the weakest possible precondition^{6} which satisfies (3.18). This version is however only sound when one considers partial correctness.

3.1.9 Mutually Recursive Procedures

Sokołowski (1977) sketched an extension to mutual recursion of which properties have to be derived simultaneously for all procedures with the same recursion-depth counter. Let Proc denote a (finite) set of procedure identifiers such that for each P : Proc, the program S_{P} stands for the body of the procedure P. Following Sokołowski (1977), one might then extend our well-founded variant (3.8) to

∀P : Proc· ∀t : W· Γ_{t} ⊢ {p(t)}S_{P} {q} ∀P : Proc· {∃t : W· p(t)}call P {q}

where (W,<) is well-founded

and, for all P ∈ Proc, ({∃u: W· p(u)∧ u < t}call P{q}) ∈ Γ_{t}.

6A more elaborate version has been published by Olderog (1983).

Pandya & Joseph (1986) have pointed out that this rule is difficult to apply, because it forces one to find termination measures across multiple procedure invocations. In their version, one may appeal to some correctness formulae in the context Γ_{t} without having to worry about decreasing the termination measure. Specifically, one may choose a subset of header procedures H ⊆ Prop such that every cycle in the procedure call graph must contain at least one header procedure. Then, a decrease of the termination measure needs only be established for header procedures i.e.,

Γ_{t} = {{∃u: W· p(u)∧ u < t}call P{q} | P ∈ H}

⋃ {{p(t)}call P {q} | P ∈ Proc \ H}

A further improvement would be to allow different termination measures for different procedures, where assumptions about the correctness of procedure calls can be gradually accumulated. As a new rule, we propose

∀t : W· Γ,{∃u: W· p(u)∧ u < t}call P{q}⊢ {p(t)}S_{P} {q}

Γ ⊢ {∃t : W· p(t)}call P {q}

where (W,<) is well-founded. (3.23)

Assume that P and Q are two mutually recursive procedures and we want to derive a property of call P(in the empty context). Applying the above rule (3.23), we consider deriving S_{P} relative to a context containing a correctness formula about call P. Unrolling the procedure body of S_{P}, a call to itself can be directly dealt with by appealing to the context. If S_{P} invokes the procedure Q, we again employ the rule for procedure invocations (3.23). Hence, with a context containing correctness formulae about call P and call Q, we may resolve the remaining proof obligation concerning the procedure body of Q.

A much more radical approach for dealing with recursive procedures has been advocated by Homeier (1995). Building on ideas of Pandya & Joseph (1986) to analyse the graph structure of recursive calls, he extends the notion of correctness formulae to e.g., also include Entrance Specifications {p} S → P {q} and Path Entrance Specifications {p} P_{1}− ps − P_{n} {q}. Intuitively

{p} S → P {q} holds if the program S is executed in a state satisfying p, then if at any point within S procedure P is called, then at the entry of P, q is satisfied.

{p} P_{1}− ps − P_{n} {q} holds if execution begins at the entry of the procedure P_{1} in a state satisfying p, and if in the execution of the body S_{1}, procedure calls are made successively deeper to the procedures listed in ps, and finally a call is made to the procedure P_{n}, then at that entry of P_{n}, q is satisfied.

Such an extension leads to a considerable blow up in the number of verification rules. Further investigations are needed to compare the suitability of a verification calculus with a rule such as (3.23) with Homeier’s (1995) system.

3.1.10 The Vienna Development Method

Extending VDM with recursive procedures follows exactly the same paradigm as for Hoare Logic. We introduce contexts along with an axiom for looking up correctness formulae in contexts and supply a rule for recursive procedure invocations. The new rules are identical to those for Hoare Logic, except that preconditions are unary predicates on the state space. Thus, the proofs of soundness and completeness can be carried out with the same techniques discussed for Hoare Logic.

p. 170 Theorem 3.17 (Soundness) The rules of Figure 3.4 on the next page are sound. Proof by induction on the structure of programs. □

p. 171 Theorem 3.18 (Completeness) The rules of Figure 3.4 on the following page are complete. Proof This is a straightforward consequence of the MGF property which can be established along the very same lines as in the setting of Hoare Logic. □

3.2 Local Program Variables

To simplify matters, we consider local program variables without procedures in this section. Section 3.3 will combine these two features.

To cope with run-time storage allocation in which, due to shadowing, program variables are no longer unique identifiers, one usually extends the notion of states and considers them either as stacks or introduces addresses. Exploiting an implicit stack mechanism (Sieber 1981, Olderog 1981), in our formalisation, we are able to work with essentially the same representation of states as in the case of simple imperative programs.

We start with an informal view of local program variables. We then go on to discuss the new requirements of the state space so that we may formally define syntax and semantics for imperative programs with local program variables. Focussing on Hoare Logic, in Section 3.2.4, we present a new rule for initialised local program variables and establish soundness and completeness. Finally, we sketch a similar extension for VDM.

{p} call {q}⊢ {p} call {q} Γ ⊢ {λσ · true }skip {λ(τ,σ) · τ = σ}

Γ ⊢ {λσ · true }x:= t {λ(τ,σ) · τ = σ[x ↦ eval(σ)(t)]}

\frac{Γ ⊢ \{p_{1}\} S_{1} \{λ(τ,σ) · p_{2}(τ) ∧ r_{1}(τ,σ)\} Γ ⊢ \{p_{2}\} S_{2} \{r_{2}\}}{Γ ⊢ \{p_{1}\} S_{1}; S_{2} \{r_{2}∘ r_{1}\}}

\frac{Γ ⊢ \{λσ · p(σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} Γ ⊢ \{λσ · p(σ) ∧ eval(σ)(b) = false \} S_{2} \{q\}}{Γ ⊢ \{p\} if b then S_{1} else S_{2} \{q\}}

\frac{Γ ⊢ \{λσ · p(σ) ∧ eval(σ)(b) = true \}S \{λ(τ,σ) · p(τ)∧ sofar(τ,σ)\}}{Γ ⊢ \{p\} while b do S \{q\}}

where sofar is any binary transitive relation on the state space Σ such that λ(τ,σ) · sofar(τ,σ)∧ p(τ) is well-founded and q(τ,σ) =^{def} p(τ)∧ eval(τ)(b)= false ∧ (sofar(τ,σ)∨ τ = σ ) .

∀t : W· {λσ · ∃u: W· p(u)(σ) ∧ u < t}call {q}⊢ {p(t)}S_{0} {q}

⊢ {λσ · ∃t : W· p(t)(σ)}call {q} where (W,<) is well-founded.

\frac{Γ ⊢ \{p_{1}\} S \{q_{1}\}}{Γ ⊢ \{p\} S \{q\}}

provided ∀σ · p(σ) ⇒ p_{1}(σ) and ∀(τ,σ) · p(σ) ⇒ q_{1}(τ,σ) ⇒ q(τ,σ).

Figure 3.4: VDM and Recursive Procedures

3.2.1 Initialisation

We introduce local program variables by the constructor new x:= t in S where x is a program variable, t is an expression with arbitrary type T and S is a program. Programs of the above form are also called blocks. Intuitively, the expression t is evaluated, its value assigned to the variable x and in this updated context, the program S is executed. At the end, the original value of x is restored.

Many real programming languages also allow blocks of the form new x in S where a suitable amount of memory is allocated, but x may or may not be initialised. The standard approach in program verification is to treat uninitialised variables as a special case of new x:= t in S where t denotes a new canonical value^{7} ω_{T} (Apt 1981). We only consider (well-formed) initialised local program variables.

“ After all, forgetting to initialize is one of the most common sources of programming error; and if there is no default initialization, debugging is made difficult by the fact that program behaviour is unpredictable in terms of the program text, and that it may even depend on the previous contents of the computer memory. Uninitialized pointer variables are especially troublesome. ” (Dahl (1992))

Surprisingly, some languages such as C which support initialised variables insist on first allocating the new variable x and only then evaluate the expression t (Schildt et al. 1990). Of course this also leads to undefined values if the variable x is contained in t.

Example 3.19 Employing the compiler gcc 2.7.2, the program

void main() {
  int c=103; {
     int c=c+1;
     printf ("%i\n",c);
  }}

prints out 5 instead of the expected 104. Java is conservative over C in that initialisations which refer to the value of a program variable to be defined are considered as not being well-formed. Perl is more flexible and the program

#!/usr/local/bin/perl
$c=103; {

7To avoid having to explicitly deal with such undefined values, de Bakker (1980) suggests to restrict the class of programs with uninitialised values to those where program variables may only be referenced after a suitable assignment has taken place.

  local($c) = $c+1;
  print "$c\n"; }

yields indeed 104.

3.2.2 States with Dynamic Types

With local program variables, representing the memory as a map from program variables to values of corresponding type is no longer adequate, because one may have multiple copies of program variables with identical names, but different values (and scopes). One usually refines the notion of states,

• either by introducing addresses (de Bakker 1980),

• or by explicitly modelling stacks (Clarke Jr. 1979).

3.2.2.1 Addresses

Addresses serve as unique handles for all program variables. Hence, instead of representing states as functions from program variables to values, states are functions from addresses to values. Scoping is controlled by an environment which is simply a map from program variables to addresses.

Let σ be an instance of the state and E an environment. To lookup the value of the program variable x, one needs to combine the two views of the memory i.e. σ(E(x)). Thus, evaluation has to additionally take scoping into account. To allocate a new local variable new x:= t in S, one needs to find a fresh address l to update the state space σ[l ↦ eval(E,σ)(t)]and the environment E [x ↦ l]. Afterward executing S, one reinstates the previous environment E.

3.2.2.2 Explicit Stacks

With this approach, the state space is a stack where entries are tuples of program variables and their values. To lookup the value of the program variable x, one needs to access the most recent entry in the stack where the first component is x and output the second component. Thus, scoping and evaluation of terms is determined by the order on the stack. To allocate a new local program variable new x:= t in S, one needs to evaluate the term t and push (x,eval(σ)(t)) onto the stack. After having executed the block, one needs to remove the last entry in the stack.

3.2.2.3 Implicit Stacks

Without procedures, one can work with a more naive notion of states which only records the value of all program variables in scope. Stack behaviour is achieved implicitly (Sieber 1981, Olderog 1981).

Let σ be such a state. When considering the execution behaviour of a block new x:= t in S commencing in state σ, it suffices to consider running the program S in the state σ[x ↦ eval(σ)(t)], because the old entry for x cannot be accessed by the program S. The final state of the block is identical to that of the program S, except that the entry for the program variable x needs to be updated by its original value σ(x).

When adding other language features, implicit stacks may no longer be adequate. In particular, when one considers procedures with call-by-name parameters, due to aliasing, one is forced to abandon the implicit stack encoding^{8}. However, restricting our attention to parameterless^{9} recursive procedures in this thesis, it suffices to work with minor variants of implicit stacks.

Previously, see Def. 2.2 on page 12, states were given relative to a fixed variable declaration sort: VAR → Type. However, in a block new x:= t in S, the sort of x is not determined by a previous variable declaration. Hence, sorts are no longer merely a parameter of states. Instead, states have to be treated as a family of functions over sorts.

p. 171 Definition 3.20 (State Space with Dynamic Types) Σ(sort) =^{def} ∏x: VAR· sort(x)

In particular, updating may change the sort i.e., for t : T and σ : Σ(sort) for some sort: VAR → Type, the updated state σ[x ↦ t]inhabits Σ(sort[x ↦ T]). We also need to revise our previous definitions of expressions and assertions:

p. 171 Definition 3.21 (Expressions)

expression(sort: VAR → Type)(T : Type) =^{def} (Σ(sort)) → T .

p. 172 Definition 3.22 (Assertions)

Assertion(sort: VAR → Type)(T : Type) =^{def} (T × Σ(sort)) → Prop

8Ah-kee (1990) has demonstrated that the address mechanism is suitable to handle aliasing.

9Call-by-value parameters would not cause any problems.

3.2.3 Programs with Dynamic Sorts

The BNF grammar

S::= skip | x:= t | S_{1}; S_{2} | if b then S_{1} else S_{2} | while b do S | new x:= t in S

does not do justice to the dynamic rôle of program variable declarations. The typetheoretic representation where every constructor is annotated by its type appears to be more informative. One may formulate the empty program and assignment for an arbitrary sort. Apart from the block constructor, all other language constructors simply preserve sorts.

p. 172 Definition 3.23 (Syntax) Imperative programs prog are defined as an inductive family over sort: VAR → Type by

skip : prog(sort)

x:= t : prog(sort) where t : expression(sort)(sort(x)) S_{1},S_{2} : prog(sort)

S_{1}; S_{2} : prog(sort)

S_{1},S_{2} : prog(sort)

if b then S_{1} else S_{2} : prog(sort)

where b: expression(sort)(bool)

S: prog(sort) while b do S: prog(sort)

where b: expression(sort)(bool)

S: prog(sort[x ↦ T]) new x:= t in S: prog(sort)

where t : expression(sort)(T) for an arbitrary type T

p. 173 Definition 3.24 (Structural Operational Semantics) We extend Def. 2.8 on page 16 with a rule for blocks

σ[x ↦ eval(σ)(t)]  \overline{S ^{}}τ
σ \overline{new x:= t in S_{}}τ[x ↦ σ(x)]

(3.24)

where for arbitrary type T and declaration sort: VAR → Type, we have

x: VAR  σ : Σ(sort)
t: expression(sort)(T)  τ: Σ(sort[x ↦ T])
S: prog(sort[x ↦ T])

In all other rules, the sort of states is identical in the premiss and the conclusion.

3.2.4 Hoare Logic

Building on ideas of Lauer (1971), Apt (1981) suggests the following rule

\frac{\{p[x ↦ y]∧ x = ω \} S \{q[x ↦ y]\}}{\{p\} new x in S \{q\}}  where y ∉ free(p,S,q)

where ω stands for a special constant denoting an undefined value.

Rather than substituting a fresh program variable y for the newly declared local program variable x, we preserve the original value of x in the assertions by updating both pre- and postcondition with a constant v which we introduce by universal quantification^{10} in the premiss. Scoping of the implicitly universally quantified p, S and q ensures that v ∉ free(p,S,q). Furthermore, we consider initialised blocks:

\frac{∀v· \{p[x ↦ v]∧ x = t [x ↦ v]\} S \{q[x ↦ v]\}}{\{p\} new x:= t in S \{q\}}  (3.25)

p. 178 Theorem 3.25 (Soundness) The set of rules in Figure 3.5 on the next page is sound. Proof by induction on the derivation of ⊢_{Hoare}{p} S {q}. In the new case of blocks, we fix arbitrary Z : T and σ : Σ(sort) such that p(Z,σ) holds. From the induction hypothesis

∀v· ⊧_{Hoare} \begin{matrix}\{λ(Z,σ) · p(Z,σ[x ↦ v]) ∧ σ(x) = eval(σ[x ↦ v])(t) \} \\ S \\ \{λ(Z,τ)· q(Z,τ[x ↦ v])\} \end{matrix}

we have to produce a final state τ such that σ \overline{new x:= t in S_{}}τ and q(Z,τ) holds. Instantiating the induction hypothesis with σ(x) for v and σ[x ↦ eval(σ)(t)] for the initial state yields a final state η such that

σ[x ↦ eval(σ)(t)]  \overline{S ^{}}η  and q(Z,η[x ↦ σ(x)])

holds. Hence, appealing to the operational semantics of blocks, we choose

τ =^{def} η[x ↦ σ(x)] .

□

10Alternatively, just like in the case of loops on page 38, one could have extended the domain of auxiliary variables in the premiss to avoid universal quantification.

⊢_{Hoare}{p} skip {p}

⊢_{Hoare}{λ(Z,σ) · p(Z,σ[x ↦ eval(σ)(t)])}x:= t {p}

\frac{⊢_{Hoare}\{p\} S_{1} \{r\} ⊢_{Hoare}\{r\} S_{2} \{q\}}{⊢_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

\frac{⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} ⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = false \}S_{2} \{q\}}{⊢_{Hoare}\{p\} if b then S_{1} else S_{2} \{q\}}

∀t : W· ⊢_{Hoare} \left\{{ \atop p̂}\right\} S \{λ(Z,τ)· p(Z,τ)∧ u(τ)< t \}

⊢_{Hoare}{p} while b do S {λ(Z,τ)· p(Z,τ)∧ eval(τ)(b)= false }

where p̂(Z,σ) =^{def} p(Z,σ) ∧ eval(σ)(b) = true ∧ u(σ) = t and (W,<) is well-founded.

\frac{∀v· ⊢_{Hoare} \{λ(Z,σ) · p(Z,σ[x ↦ v]) ∧ σ(x) = eval(σ[x ↦ v])(t) \} S \{λ(Z,τ)· q(Z,τ[x ↦ v])\}}{⊢_{Hoare}\{p\} new x:= t in S \{q\}}  (3.26)

\frac{⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{⊢_{Hoare}\{p\} S \{q\}}

where p_{1},q_{1} : Assertion(U) and p,q: Assertion(T) for arbitrary types T and U

and ∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) ) . (3.27)

Figure 3.5: Hoare Logic for Blocks – Total Correctness p. 178 Theorem 3.26 (MGF) ⊢_{Hoare}\{ λ(Z,σ) · σ \overline{S ^{}}Z\} S \{λ(Z,τ)· Z = τ\}

Proof by induction on the structure of S. In the case of a block, from the induction hypothesis

⊢_{Hoare}\{ λ(Z,σ) · σ \overline{S ^{}}Z\} S \{λ(Z,τ)· Z = τ\}

one has to establish

⊢_{Hoare}\{ λ(Z,σ) · σ \overline{new x:= t in S_{}}Z\} new x:= t in S \{λ(Z,τ)· Z = τ\} .

Given an arbitrary v : sort(x), we employ the stronger rule of consequence (3.27) to derive

⊢_{Hoare} \{ λ(Z,σ) · σ[x ↦ v] \overline{new x:= t in S_{}}Z∧ σ(x) = eval(σ[x ↦ v])(t) \}
S
{λ(Z,τ)· Z = τ[x ↦ v]}

from which the rule for blocks (3.26)renders the proof obligation. As a side-condition, given states Z and σ such that

σ[x ↦ v] \overline{new x:= t in S_{}}Z  (3.28)
σ(x) = eval(σ[x ↦ v])(t)  (3.29)

we have to find a state τ such that σ \overline{S ^{}}τ and Z = τ[x ↦ v]. Inverting the derivation of (3.28), there must be such a state τ which satisfies

σ[x ↦ v][x ↦ eval(σ[x ↦ v])(t)]  \overline{S ^{}}τ
Z = τ[x ↦ σ[x ↦ v](x)]

This completes the proof, because (3.29) ensures that the state σ is (extensionally) equal to σ[x ↦ v][x ↦ eval(σ[x ↦ v])(t)]. □

p. 178 Corollary 3.27 (Completeness) The set of rules in Figure 3.5 on the facing page is complete.

Proof Identical to the proof of Cor. 2.31 on page 41 for simple imperative programs.

□

In practice, is is convenient to additionally be able to employ a left-constructive rule for blocks. For the correctness proof of Quicksort in Chapter 4, we have also used the following version

\frac{∀v· \{p\} S \{q[x ↦ v]\}}{\{p[x ↦ t]\} new x:= t in S \{q\}}  (3.30)

which is to be understood as a pretty-printed version of the rule in Figure 3.6. It essentially assumes that the postcondition q does not mention x. Thus, one cannot expect to retain a complete system when replacing the Bottom-Up version (3.25) with this version. We are not aware of previous proposals of left-constructive rules for blocks.

∀v· ⊢_{Hoare}{p} S {λ(Z,τ)· q(Z,τ[x ↦ v])}
⊢_{Hoare}{λ(Z,σ) · p(Z,σ[x ↦ eval(σ)(t)])}new x:= t in S {q}

Figure 3.6: Admissible Left-Constructive Rule for Blocks in Hoare Logic

p. 178 Corollary 3.28 (Left-Constructive Rule for Blocks) The rule from Figure 3.6 is admissible. Proof By soundness and completeness it suffices to establish the semantic counterpart

∀v· ⊧_{Hoare}{p} S {λ(Z,τ)· q(Z,τ[x ↦ v])}
⊧_{Hoare}{λ(Z,σ) · p(Z,σ[x ↦ eval(σ)(t)])}new x:= t in S {q}

which follows immediately from the operational semantics for blocks. □

3.2.5 The Vienna Development Method

Jones (1990) suggests the right-constructive rule

\frac{\{p ∧ x = t\} S \{q\}}{\{p\} new x:= t in S \{∃v· q[x ↦ v]\}}  (3.31)

to deal with local program variables in VDM. It is, no doubt, useful in practice, but cannot handle shadowed variables. The postcondition of the conclusion cannot record any property of the value of the program variable x which is in scope before and after executing the block. Specifically, one cannot derive \{true \} new x:= t in S \{x =\begin{matrix}↼ − \\ x \end{matrix} \}. Thus, the set of rules given in Jones’s (1990) book on VDM is incomplete.

This deficiency has been overcome in the formalisation of Ah-kee (1990). He employs an address mechanism to represent states and assertions. Environments E map program variables to addresses. Assertions are predicates on addresses. Thus, one may selectively update addresses corresponding to the new local program variable x. For uninitialised local variables^{11}, he proposes

E [x ↦ l]| {p} S {q}

E | {p} new x in S {λ(τ,σ) · ∃v′,v· q(τ[l ↦ v],σ[l ↦ v′])} where l is a fresh address.

With the implicit stack mechanism, we discard information about intermediate local program variables. As a drawback, adding a variant of rule (3.31) does not lead to a complete verification calculus. Our solution has been to interpret the rule for Hoare Logic (3.25) in the VDM setting. This yields

\frac{∀v· \{p[x ↦ v]∧ x = t [x ↦ v]\} S \{q[x ↦ v]\}}{\{p\} new x:= t in S \{q\}} .

Formally, this corresponds to (3.32).

p. 181 Theorem 3.29 (Soundness) The rules of Figure 3.7 on the following page are sound. Proof by induction on the derivation of correctness formulae. □

p. 182 Theorem 3.30 (Completeness) The rules of Figure 3.7 on the next page are complete. Proof Similar to the corresponding proof in the setting of Hoare Logic. □

3.3 Recursive Procedures and Static Binding

Combining procedures with blocks requires some care. A procedure invocation may occur inside a block so that, at run-time, some local variables have been allocated which conflict with program variables of the procedure in scope at declaration time. Most programming languages, including Algol, C, Cobol, Fortran, Java and Pascal, adhere to static binding^{12} where a procedure invocation triggers an environmental change: A procedure may only refer to either global program variables, formal parameters, or its own local program variables. In particular, since we restrict our attention to parameterless procedures, information can only be passed to procedures via global program variables.

We deal with static binding. Some adjustments are necessary because a naïve combination of the rules of the previous two sections would lead to dynamic binding. A known remedy is to only consider programs where all defining occurrences of program

11restricted to the type int

12also referred to as lexicographical binding

⊢_{VDM}{λσ · true }skip {λ(τ,σ) · τ = σ}

⊢_{VDM}{λσ · true }x:= t {λ(τ,σ) · τ = σ[x ↦ eval(σ)(t)]}

\frac{⊢_{VDM}\{p_{1}\} S_{1} \{λ(τ,σ) · p_{2}(τ) ∧ r_{1}(τ,σ)\} ⊢_{VDM}\{p_{2}\} S_{2} \{r_{2}\}}{⊢_{VDM}\{p_{1}\} S_{1}; S_{2} \{r_{2}∘ r_{1}\}}

\frac{⊢_{VDM}\{λσ · p(σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} ⊢_{VDM}\{λσ · p(σ) ∧ eval(σ)(b) = false \} S_{2} \{q\}}{⊢_{VDM}\{p\} if b then S_{1} else S_{2} \{q\}}

⊢_{VDM}{λσ · p(σ) ∧ eval(σ)(b) = true }S {λ(τ,σ) · p(τ)∧ sofar(τ,σ)}

⊢_{VDM}{p} while b do S {q}

where sofar is any binary transitive relation on the state space Σ such that λ(τ,σ) · sofar(τ,σ)∧ p(τ) is well-founded and q(τ,σ) =^{def} p(τ)∧ eval(τ)(b)= false ∧ (sofar(τ,σ)∨ τ = σ ) .

\frac{∀v· ⊢_{VDM} \begin{matrix}\{λσ · p(σ[x ↦ v]) ∧ σ(x) = eval(σ[x ↦ v])(t) \} \\ S \\ \{λ(τ,σ) · q(τ[x ↦ v],σ[x ↦ v])\} \end{matrix}}{⊢_{VDM}\{p\} new x:= t in S \{q\}}  (3.32)

\frac{⊢_{VDM}\{p_{1}\} S \{q_{1}\}}{⊢_{VDM}\{p\} S \{q\}}

provided ∀σ · p(σ) ⇒ p_{1}(σ) and ∀(τ,σ) · p(σ) ⇒ q_{1}(τ,σ) ⇒ q(τ,σ).

Figure 3.7: VDM and Local Program Variables variables are denoted differently. In this case, static and dynamic binding coincide (Olderog 1981). We do not make such a simplification. In our treatment, when declaring new variables, one may choose an arbitrary program variable name and instantiate it with a value of arbitrary type. We cater for static binding by extending implicit stacks with a backup mechanism which records the value of global variables.

We start by annotating the operational semantics with an environment to keep track of the local program variables in scope. We then follow an idea by Ah-kee (1990) to also annotate correctness formulae with environments. Without environments, one effectively assumes that all variables in assertions correspond to values of program variables in scope. Adding environments leads to more flexibility when formulating verification rules. In particular, since this technique has previously only been applied to VDM, we are able to present an improved set of Hoare Logic rules for recursive procedures and static binding. More precisely, annotating the rules of the previous two sections with environments ensures that a procedure call must take place in the empty environment and the value of all local program variables must be frozen during a call. Without environments, one needs to replace the rule for blocks by a less elegant version to retain a sound verification calculus for static binding (Apt 1981).

We show that our set of rules is sound and complete. We sketch how to achieve similar results for VDM in Sect. 3.3.8.

3.3.1 Static versus Dynamic Binding

The difference between static and dynamic binding is perhaps best explained by means of an example.

p. 205 Example 3.31 (Scoping) Let

S_{0} =^{def} x:= z
S =^{def} z:= 1; new z:= 0 in call

What should be the final value of x? Invoking the procedure S_{0}, should z refer to the global program variable z which is in scope at declaration time, or the most recently allocated local program variable z? For static binding, the final value of x amounts to 1, whereas dynamic binding yields 0. We restrict our attention to static binding which is supported by most programming languages^{13}. With the set of rules we are about to introduce, we derive the correctness formula

{true } z:= 1; new z:= 0 in call {x = 1}

in Sect. 3.3.7.6 on page 109.

13Exceptions are e.g., Emacs Lisp, Perl (Wall, Christiansen & Schwartz 1996) and TEX(Knuth 1986).

3.3.2 States and Environments

Can we retain the simplicity of implicit stacks if we additionally have to cope with procedure calls? Consider the case where the program new x:= t in S invokes the procedure S_{0}. In refining

σ[x ↦ eval(σ)(t)] \overline{S ^{}}τ ,

one is confronted with justifying σ′ \overline{call ^{}}τ′. However, it would not be correct to investigate σ′ \overline{S_{0} ^{}}τ′at this point, because, due to static scoping, an environmental change is required. The program S_{0} must be able to access the global value of the program variable x which is no longer present in σ′. In particular, combining blocks and procedures, the operational justification employing the previous implementation of implicit stacks is not adequate.

To avoid shadowing, Clarke Jr. (1979) suggests to rename local program variable names ^{14}. Whenever one encounters a block of the form new x in S, one instead considers the semantics of new x′in S[x ↦ x′]where x′is fresh. We prefer a semantic account which avoids syntactic modifications. The key idea is to distinguish between global and local program variables, because, for top-level procedure declarations, the procedure body may only inspect global program variables, whereas the semantics for blocks only leads to updating of local program variables.

Both the address and the explicit stack mechanism would also introduce enough machinery to avoid this problem. But such an approach is redundant in that one does not actually need to worry about all intermediate local program variable allocations. It suffices to access the most recent local program variable as long as we still have access to all global program variables. We duplicate the state space in order to provide a backup of global program variables whenever an environmental change is required.

p. 182 Definition 3.32 (State Space) Relative to a parametrised declaration gsortfor global program variables, we index the state space by a declaration lsort

Σ(lsort) =^{def} (∏x· gsort(x)) × (∏x· lsort(x))

with both global and local declarations mapping program variables to types. Moreover, for σ: Σ(lsort), let σ.global: ∏x· gsort(x)and σ.local: ∏x· lsort(x)denote the global and local components of the state σ.

14Sieber’s (1981) version of this definition is flawed because he erroneously updates x in the declared procedure body S_{0}.

Scoping is controlled by an environment E : VAR → bool. Intuitively, E(x) holds if x is a local program variable in the current scope. For convenience we employ set notation. In particular,

x ∈ E =^{def} E(x)
∅ =^{def} λx· false
E ∪ {x} =^{def} λy· (x = y) ∨ (y ∈ E)

Looking up values of program variables in scope depends on both the current environment and state:

lookup(E,σ)(x) =^{def} if x ∈ E then σ.local(x) else σ.global(x) for σ: Σ(lsort).

Its type is given by

AccSort(lsort,E)(x) =^{def} if x ∈ E then lsort(x)else gsort(x)

Substitution has to take the environment into account. We define

σ[x ↦_{E} t]=^{def}

if x ∈ E then (σ.global,σ.local[x ↦ t]) else (σ.global[x ↦ t],σ.local)

and introduce

uL(σ,x,v) =^{def} (σ.global,σ.local[x ↦ v])  (3.33)

for updating the local entry of a program variable.

3.3.3 Expressions

We retain Def. 3.21 on page 79

expression(sort: VAR → Type)(T : Type) =^{def} (∏x· sort(x)) → T

where sortcorresponds to a particular instance of AccSort. A term can only be evaluated in a compatible setting i.e., for a given environment E and a state σ: Σ(lsort), an expression t must be of type expression(AccSort(lsort,E))(T) for some type T. Evaluation proceeds by plugging in the accessible fraction of the state space extracted by the lookupfunction: p. 183 Definition 3.33 (Evaluation of Expressions) Let lsortbe an arbitrary sort and E be

an arbitrary environment. Given a state σ: Σ(lsort) and an expression

t : expression(AccSort(lsort,E))(T) ,

we define evaluation by

eval(E,σ)(t)=^{def} t(lookup(E,σ))

3.3.4 Assertions

The state space keeps track of all global variables and, for each program variable name, the value of the most recently allocated local variable. We provide unrestricted access to the state space in assertions.

p. 185 Definition 3.34 (Assertions) Assertion(lsort)(T)=^{def} (T × Σ(lsort)) → Prop

In particular, we can explicitly refer to the value of local and global variables. To support such a view of assertions, one needs to also change the syntactic representation. It is no longer adequate to merely consider standard first-order logic where a (free) variable corresponds to a value of a program variable as this approach does not distinguish between global and local program variables.

Example 3.35 (Syntax of Assertions) One could restrict the domain of auxiliary variables to the state space and retain the convention that auxiliary variables are represented by upper-case letter. To disambiguate global from local program variables, one could decorate local program variables e.g., with a dot. Then

[[A = ċ∧ a = Ȧ]](Z,σ)

corresponds to

Z.global(a) = σ.local(c) ∧ σ.global(a) = Z.local(a) .

As a side-effect of such a richer notion of assertion, it is conceivable that some of the perceived short-comings of verification calculi can be avoided e.g., Cousot (1990), in an attempt to explain the intuition behind Clarke Jr.’s (1979) incompleteness results, writes

“ But when considering Algol- or Pascal-like languages, the naming conventions in [assertions] P, Q and [programs] C are totally different. For example, objects deeply buried in the runtime stack cannot be accessed by their name although they can be modified using procedure calls! ”

3.3.5 The Programming Language

p. 186 Definition 3.36 (Syntax) We extend Def. 3.23 on page 80 by a constructor for proce-

dure invocations call : prog(sort)

which can be invoked in an arbitrary environment.

Notice that sortis expected to be a mapping of all program variables in scope to types. In particular, in the definitions of the operational semantics in the following section, sortmust be a particular instance of AccSort(lsort,E) for some lsort: VAR → Type and environment E. Alternatively, one could directly parametrise the class of programs by a local sort and an environment.

3.3.6 Structural Operational Semantics

Operational semantics has to also take environments into account to ensure that expressions are well-formed. The environment and the sort of local program variables in the final state is always identical to that in the initial state. They may only change for intermediate states, when encountering blocks or procedure invocations.

p. 189 Definition 3.37 (Structural Operational Semantics) The inductive definition of the

operational semantics

(lsort,E) | σ \overline{S ^{}}τ

depends now explicitly on a sort of local program variables lsortand an environment E, where σ,τ: Σ(lsort) and S: prog(AccSort(lsort,E)). We have collected the set of all rules in Figure 3.8 on the next page.

3.3.6.1 Procedures with Static Binding

Procedure invocations are legitimate in any environment

call : prog(AccSort(lsort,E)) ,

but local program variables cease to be accessible i.e., for S_{0} : prog(gsort), we have

\frac{(lsort,∅) | σ  \overline{S_{0}  ^{}}τ}{(lsort,E) | σ  \overline{call ^{}}τ} .  (3.34)

Notice that gsortis (extensionally) equal to AccSort(lsort,∅). The proposition

(lsort,∅) | σ  \overline{S_{0}  ^{}}τ

(lsort,E) | σ \overline{skip ^{}}σ

(lsort,E) | σ x:= t ^{}σ[x ↦_{E} eval(E,σ)(t)]

where t : expression(AccSort(lsort,E))(AccSort(lsort,E)(x)). (lsort,E) | σ \overline{S_{1} ^{}}η (lsort,E) | η \overline{S_{2} ^{}}τ

(lsort,E) | σ \overline{S_{1}; S_{2} ^{}}τ

(lsort,E) | σ \overline{S_{1} ^{}}τ (lsort,E) | σ \overline{if b then S_{1} else S_{2}_{}}τ

provided eval(E,σ)(b)= true .

(lsort,E) | σ \overline{S_{2} ^{}}τ (lsort,E) | σ \overline{if b then S_{1} else S_{2}_{}}τ

provided eval(E,σ)(b)= false .

(lsort,E) | σ \overline{while b do S_{}}σ provided eval(E,σ)(b)= false . (lsort,E) | σ \overline{S ^{}}η (lsort,E) | η \overline{while b do S_{}}τ

(lsort,E) | σ \overline{while b do S_{}}τ

provided eval(E,σ)(b)= true .

(lsort,∅) | σ \overline{S_{0} ^{}}τ (lsort,E) | σ \overline{call ^{}}τ

(lsort[x ↦ T],E ∪ {x}) | uL(σ,x,eval(E,σ)(t)) S ^{}τ

(lsort,E) | σ \overline{new x:= t in S_{}}uL(τ,x,σ.local(x)) Figure 3.8: Structural Operational Semantics for Static Binding is not well-formed in (Lego’s) intensional type theory. Instead, one needs to explicitly introduce type coercions i.e., σ \overline{coerce(c)(S_{0})_{}}τ for a suitable^{15} function coerce, where c is a proof of ∀x· gsort(x)= AccSort(lsort,∅)(x). For convenience, we will omit such type coercions in the sequel.

3.3.6.2 Blocks with Backup for Global Program Variables

We extend rule 3.24 on page 80 with a backup mechanism for blocks

(lsort[x ↦ T],E ∪ \{x\}) | uL(σ,x,eval(E,σ)(t)) \overline{S ^{}}τ
(lsort,E) | σ \overline{new x:= t in S_{}}uL(τ,x,σ.local(x))

(3.35)

where, for an arbitrary environment E, an arbitrary type T and a declaration lsort, we have

x: VAR  σ : Σ(lsort)
t: expression(AccSort(lsort,E))(T)  τ: Σ(lsort[x ↦ T])
S: prog(AccSort(lsort,E ∪ {x})[x ↦ T]) .

By distinguishing between global and local program variables, when commencing execution of S, the initial state still contains the global entry for x which may be required if S contains a procedure call. Moreover, observe that

AccSort(lsort,E ∪ {x})[x ↦ T]

is (extensionally) equal to

AccSort(lsort[x ↦ T],E ∪ {x}) .

3.3.6.3 Recursive Depth

To guarantee termination of recursive calls, similar to Def. 3.9 on page 66, we annotate the operational semantics with a recursive depth n which is an upper bound on the recursive depth required in an execution. We refer to Figure 3.9 on the following page for further details.

p. 194 Lemma 3.38 (Bounded Recursive Depth) 1. If a program terminates, the recursive depth must be finite i.e., if

(lsort,E) | σ \overline{S ^{}}τ
then there exists a bound n such that (lsort,E) | σ  \overline{S  n} τ.

15This is best defined by induction on the structure of programs and exploits substituting terms in types. See Appendix A.

(lsort,E) | σ  \overline{skip  n} σ

(lsort,E) | σ \overline{x:= t n} σ[x ↦_{E} eval(E,σ)(t)]

where t : expression(AccSort(lsort,E))(AccSort(lsort,E)(x)). (3.36)

(lsort,E) | σ \overline{S_{1} n} η (lsort,E) | η \overline{S_{2} n} τ

(lsort,E) | σ \overline{^{S_{1}; S_{2}} n} τ

(lsort,E) | σ \overline{S_{1} n} τ (lsort,E) | σ \overline{if b then S_{1} else S_{2}_{n}} τ

provided eval(E,σ)(b)= true .

(lsort,E) | σ \overline{S_{2} n} τ (lsort,E) | σ \overline{if b then S_{1} else S_{2}_{n}} τ

provided eval(E,σ)(b)= false .

(lsort,E) | σ \overline{while b do S_{n}} σ provided eval(E,σ)(b)= false . (lsort,E) | σ \overline{S n} η (lsort,E) | η \overline{while b do S_{n}} τ

(lsort,E) | σ \overline{while b do S_{n}} τ

provided eval(E,σ)(b)= true .

(lsort,∅) | σ \overline{S_{0} n} τ (lsort,E) | σ \overline{call n+1} τ

(lsort[x ↦ T],E ∪ \{x\}) | uL(σ,x,eval(E,σ)(t)) \overline{S n} τ (lsort,E) | σ \overline{new x:= t in S_{n}}uL(τ,x,σ.local(x))

(3.37)

Figure 3.9: Recursive Depth for Procedures and Blocks 2. Bounded recursive depth is a mere annotation of operational semantics i.e., for

an arbitrary depth n, (lsort,E) | σ  \overline{S  n} τimplies
(lsort,E) | σ  S ^{}τ .

Proof Similar to the proof of Lemma 3.11 on page 67 in the setting without local program variables. □

3.3.6.4 Adequacy

The rule for blocks (3.35) may be comprehensible, but the interaction with procedures is certainly somewhat subtle. Since we axiomatise operational semantics, we cannot argue about its soundness and completeness with respect to a more easily comprehensible semantic notion. Rather than taking its adequacy on good faith, one should investigate a number of properties for clarification. One could for example establish that the operational semantics is conservative over the presentations in the previous two sections where we have dealt with procedures and blocks in isolation. Furthermore, one ought to examine how local program variables interact with procedure calls.

For the soundness and completeness proofs, we have found it useful to establish the following properties.

p. 195 Lemma 3.39 (Determinism) The operational semantics of Figure 3.8 and its annotated version of Figure 3.9 is deterministic. Proof by induction on the derivation. □

p. 196 Lemma 3.40 (Procedure Activation and Environments) Let E and E′ be arbitrary

environments.

(lsort,E) | σ  \overline{call  n} τ
(lsort,E′) | σ  \overline{call  n} τ
Proof Inverting the derivation of (lsort,E) | σ  \overline{call  n} τyields
(lsort,∅) | σ \overline{S_{0} n−1} τ .
Thus, applying rule (3.34) leads to the desired (lsort,E′) | σ  \overline{call  n} τ.  □

The soundness and completeness proofs are more challenging when combining recursive procedure calls with local program variables because one has to relate the behaviour of procedure activations commencing in states which differ in the value of local program variables. However, if these local program variables are not in scope, their value is preserved. p. 196 Theorem 3.41 (Preserving Local Program Variables) Let U be any type, y any pro-

gram variable which is not local i.e., y ∉ E and v: U. Whenever

(lsort,E) | σ  \overline{S  n} τ

then

(lsort[y ↦ U],E) | uL(σ,y,v)  \overline{S  n} uL(τ,y,v)

holds.

Notice that the type AccSort(lsort,E) is (extensionally) equal to

AccSort(lsort[y ↦ U],E) for y ∉ E.

Due to Lemma 3.38, such results also hold when one omits the recursive depth annotations. But notice that, with such annotations, the above theorem also establishes that the recursive depth is preserved.

Proof by induction on the derivation of (lsort,E) | σ  \overline{S  n} τ. The cases

where S amounts to an assignment or a block require some attention, because one needs to show that updating is commutative in these circumstances.

Assignment By the axiom for assignments (3.36), it suffices to establish that

uL(σ[x ↦_{E} eval(E,σ)(t)],y,v)

is (extensionally) equal to

uL(σ,y,v)[x ↦_{E} eval(E,uL(σ,y,v))(t)] .

Since y is not in scope, evaluation of the term t in the environment E cannot depend on the local entry of y in the state σ. Thus, eval(E,uL(σ,y,v))(t) yields the same result as eval(E,σ)(t). We proceed by case analysis on x ∈ E.

x ∈ E The program variables x and y must be different. Hence, updating of x

and y is interchangeable. x ∉ E The order of updating is irrelevant because one updates the local entry

for y and the global entry for x.

Block We abbreviate

lsort_{x} =^{def} lsort[x ↦ T]
lsort_{x,y} =^{def} \begin{matrix}lsort & [x ↦ T] \\ [y ↦ U] \end{matrix}
lsort_{y} =^{def} lsort[y ↦ U] .

From the assumption

(lsort_{x},E ∪ \{x\}) | uL(σ,x,eval(E,σ)(t))  \overline{S  n} τ  (3.38)

and the induction hypothesis

y ∉ E ∪ {x} ⇒

(lsort_{x,y},E ∪ \{x\}) | uL( uL(x,eval(E,σ)(t),σ),y,v)  \overline{S  n} uL(τ,y,v) ,

one needs to show

(lsort_{y},E) | uL(σ,y,v) \overline{new x:= t in S_{n}}uL( uL(τ,x,σ.local(x)),y,v) . (3.39)

The induction hypothesis is only applicable if x and y are different program variables. If they are identical, we need to appeal to (3.38).

x = y Since y ∉ E, evaluation of the term t relative to (E,σ) yields the same result as evaluation with respect to (E,uL(σ,y,v)). Hence, the initial state of (3.38) is (extensionally) equal to

uL(uL(σ,y,v),x,eval(E,uL(σ,y,v))(t)) .

By the rule for blocks (3.37), we may conclude

(lsort,E) | uL(σ,y,v) \overline{new x:= t in S_{n}}uL(τ,x,v) .  (3.40)

This concludes the case x = y because the final states of (3.40) and (3.39) are (extensionally) equal.

x ≠ y Since y ∉ E, we also have y ∉ E ∪ {x} and may exploit the induction hypothesis. For distinct program variables x and y, we may commute the updating operations in the initial state. Thus, the rule for blocks (3.37) leads to

(lsort_{y},E) | uL(σ,y,v) \overline{new x:= t in S_{n}}uL(uL(τ,y,v),x,σ.local(x))

Finally, we commute the updating operations in the final state.

□

p. 197 Corollary 3.42 (Preserving Local Program Variables) 1. ((lsort,E) | σ \overline{S ^{}}τ∧ y ∉ E) ⇒ σ.local(y) = τ.local(y)

2. Let U be any type and v : U be an arbitrary value. Given any environments

E and E′, whenever (lsort,E) | σ  call  n τ holds, one may also derive
(lsort[y ↦ U],E′) | uL(σ,y,v)  \overline{call  n} uL(τ,y,v).

Proof

1. We apply Theorem 3.41 and instantiate v with σ.local(y). Thus, by determinism, τ = uL(τ,y,σ.local(y))from which one may deduce that τ.local(y)and σ.local(y) are identical.

2. By Lemma 3.40, we may reduce the problem to a setting without any local pro-

gram variables. Given (lsort,∅) | σ  \overline{call  n} τ, Theorem 3.41 yields
(lsort[y ↦ U],∅) | uL(σ,y,v)  \overline{call  n} uL(τ,y,v) .
□

3.3.7 Hoare Logic

We need to revise Hoare Logic to integrate environments. One could either introduce them at the level of atomic correctness formulae

((lsort_{1},E_{1}) | {p_{1}} call {q_{1}}) ⊢_{Hoare}((lsort_{2},E_{2}) | {p_{2}} S {q_{2}})

or across a sequent

(lsort,E)| ({p_{1}} call {q_{1}}⊢_{Hoare}{p_{2}} S {q_{2}})

It turned out that it is easier to design a complete verification calculus for sequents with a single environment. Intuitively, it obviates the requirement for a structural rule to mediate between different environments with different sorts of local program variables. However, since the rule for blocks needs to modify the environment in the premiss, both the context and the succedent of the premiss needs to be adjusted. Alternatively, one could consider sequents in which the local sort is shared, whereas the set of local program variable names in scope is attached to atomic correctness formulae.

p. 199 Definition 3.43 (Semantics of Hoare Logic) Similar to Def. 3.5 on page 62, we define (lsort,E) | Γ ⊧_{Hoare}{p} S {q}by case analysis on the structure of the context Γ.

• In the empty context, we have

(lsort,E) | ∅ ⊧_{Hoare}{p} S {q} =^{def}

∀Z· ∀σ · p(Z,σ) ⇒ ∃τ· ((lsort,E) | σ \overline{S ^{}}τ)∧ q(Z,τ)

• For Γ = {p′} call {q′}, we define validity relative to the empty context i.e.,

(lsort,E) | Γ ⊧_{Hoare}{p} S {q} =^{def}
((lsort,E) | ∅ ⊧_{Hoare}{p′} call {q′}) ⇒
((lsort,E) | ∅ ⊧_{Hoare}{p} S {q})

3.3.7.1 Procedure Invocation

Turning to procedure calls, in resolving a correctness formula which invokes a procedure in an environment (lsort,E), we may assume the correctness of this formulae while proving the correctness of the unfolded procedure body. Since procedure calls cause context switching, we investigate the triggered proof obligation relative to the empty environment. Assertions may inspect all entries of the state space irrespective of scoping restrictions. We may therefore reuse the same assertions as in the rule (3.8) for recursive procedures invocations without local program variables on page 62.

\frac{∀t : W· (lsort,∅) | \{p̂(t)\}call \{q\}⊢_{Hoare}\{p(t)\}S_{0} \{q\}}{(lsort,E) | ∅ ⊢_{Hoare}\{λ(Z,σ) · ∃t : W· p(t)(Z,σ)\}call \{q\}}

where p̂(t)(Z,σ)=^{def} ∃u: W· p(u)(Z,σ)∧ u < t and (W,<) is well-founded. (3.41)

3.3.7.2 Blocks

If one were to insist on assertions being simply first-order logical formulae where some free variables are interpreted as the value of program variables, one would effectively limit scoping in assertions to dynamic binding. Specifically, the rule (3.25) from page 81 would be unsound with respect to static binding (Apt 1981). One can avoid scoping intrusion by substituting program variables in programs instead of assertions. For uninitialised variables, Gorelick (1975) proposes

\frac{\{p ∧ y = ω \} S[x ↦ y]\{q\}}{\{p\} new x in S \{q\}}  where y ∉ free(p,S,q).  (3.42)

However, modifying the program text during the process of verification may not be appreciated, because software engineers may have chosen variable names carefully to reflect their intended usage. Furthermore, Apt (1981) admits that rule (3.25) is easier to use and handle than rule (3.42).

We follow Ah-kee’s (1990) technique of annotating correctness formulae with environments and allow assertions to inspect the complete state space and not only the value of variables in scope. This has previously only been applied to VDM. In assertions we may thus explicitly refer to the value of a local and a global program variable^{16}. We propose the rule

\frac{∀v· (lsort[x ↦ T],E ∪ \{x\}) | Pr(Γ,x,v) \begin{matrix}⊢_{Hoare} & \{In(p,x,v,t,E)\} \\ & S \\ \{ & uL & (q,x,v) & \} \end{matrix}}{(lsort,E) | Γ ⊢_{Hoare}\{p\} new x:= t in S \{q\}}

with Pr(Γ,x,v) =^{def} \{ \begin{matrix}∅ & for Γ = ∅, \\ \{Pr(p′,x,v)\}call \{Pr(q′,x,v)\} & for Γ = \{p′\} call \{q′\} \end{matrix}

Pr(p,x,v)((Z,V),σ) =^{def} uL(p,x,v)(Z,σ)∧ σ.local(x) = V

In(p,x,v,t,E)(Z,σ) =^{def} uL(p,x,v)(Z,σ)∧ σ.local(x) = eval(E,uL(σ,x,v))(t)

(3.43)

In the premiss of the rule, we reduce the complexity by considering the derivation of a correctness formula concerning the program S in the updated environment. We have to freeze all references to the local variable x in all assertions of the premiss. Let v: lsort(x)be a (fresh) constant^{17}. We overload notation and exploit our previous definition (3.33) for updating the local entry in the state space. Employing

uL(p,x,v)(Z,σ) =^{def} p(Z,uL(σ,x,v))

we replace all references to the previous local value of x in all assertions of the premiss by the same v. Since v is fresh, it corresponds to the previous value of the local program variable x when reaching the block. In particular, in the initial state of the succedent in the premiss, for σ: Σ(lsort[x ↦ T]), the state uL(σ,x,v) amounts to the initial state of the succedent in the conclusion. Thus, we may add σ.local(x) = eval(E,uL(σ,x,v))(t) in the precondition for S to ensure that the local value of x is equal to the value of the expression t when evaluated in the original environment E and state σ.

However, due to static scoping, the value of the local program variable x must be preserved across a procedure call in the context Pr(Γ,x,v). The auxiliary variable V : T records the value of the current local program variable x at the time of invoking a procedure. In the context of the rule’s premiss, we have extended the domain of (lsort,E) | {p} call {q}⊢_{Hoare}{p} call {q}

16A less powerful extension to the notion of assertions to merely add propositions of the form x ∈ E to check if x is a local program variable (Trakhtenbrot et al. 1984) does not seem to suffice.

17In the completeness proof, we will see that, due to our choice of permitting (local) program variables with arbitrary sorts, it is useful to share the same reference v across the sequent. Thus, v cannot be an auxiliary variable, because they range only over atomic correctness formulae. For a sound and complete calculus where v is represented as an auxiliary variable, one would need to restrict the sort of all (local)

program variables to types SORT⊊ Type in which, for every type in SORT, one can effectively extract an element of that type.

(lsort,E) | Γ ⊢_{Hoare}{p} skip {p}

(lsort,E) | Γ ⊢_{Hoare}{λ(Z,σ) · p(Z,σ[x ↦_{E} eval(E,σ)(t)])}x:= t {p}

\frac{(lsort,E) | Γ ⊢_{Hoare}\{p\} S_{1} \{r\} (lsort,E) | Γ ⊢_{Hoare}\{r\} S_{2} \{q\}}{(lsort,E) | Γ ⊢_{Hoare}\{p\} S_{1}; S_{2} \{q\}}

\frac{(lsort,E) | Γ ⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} (lsort,E) | Γ ⊢_{Hoare}\{λ(Z,σ) · p(Z,σ) ∧ eval(σ)(b) = false \}S_{2} \{q\}}{(lsort,E) | Γ ⊢_{Hoare}\{p\} if b then S_{1} else S_{2} \{q\}}

∀t : W· (lsort,E) | Γ ⊢_{Hoare} \left\{{ \atop p̂}\right\} S \{λ(Z,τ)· p(Z,τ)∧ u(τ)< t \}

(lsort,E) | Γ ⊢_{Hoare}{p} while b do S {λ(Z,τ)· p(Z,τ)∧ eval(τ)(b)= false }

where p̂(Z,σ) =^{def} p(Z,σ) ∧ eval(σ)(b) = true ∧ u(σ) = t

and (W,<) is well-founded. (3.44)

\frac{∀t : W· (lsort,∅) | \{p̂(t)\}call \{q\}⊢_{Hoare}\{p(t)\}S_{0} \{q\}}{(lsort,E) | ∅ ⊢_{Hoare}\{λ(Z,σ) · ∃t : W· p(t)(Z,σ)\}call \{q\}}

where p̂(t)(Z,σ)=^{def} ∃u: W· p(u)(Z,σ)∧ u < t and (W,<) is well-founded.

\frac{∀v· (lsort[x ↦ T],E ∪ \{x\}) | Pr(Γ,x,v) \begin{matrix}⊢_{Hoare} & \{In(p,x,v,t,E)\} \\ & S \\ \{ & uL & (q,x,v) & \} \end{matrix}}{(lsort,E) | Γ ⊢_{Hoare}\{p\} new x:= t in S \{q\}}

with Pr(Γ,x,v) =^{def} \{ \begin{matrix}∅ & for Γ = ∅, \\ \{Pr(p′,x,v)\}call \{Pr(q′,x,v)\} & for Γ = \{p′\} call \{q′\} \end{matrix}

Pr(p,x,v)((Z,V),σ) =^{def} uL(p,x,v)(Z,σ)∧ σ.local(x) = V

In(p,x,v,t,E)(Z,σ) =^{def} uL(p,x,v)(Z,σ)∧ σ.local(x) = eval(E,uL(σ,x,v))(t)

\frac{(lsort,E) | Γ ⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{(lsort,E) | Γ ⊢_{Hoare}\{p\} S \{q\}}

where p_{1},q_{1} : Assertion(U) and p,q: Assertion(T) for arbitrary types T and U

and ∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) ) . (3.45)

Figure 3.10: Hoare Logic and Static Binding – Total Correctness auxiliary variables to generate a fresh constant V : T. Therefore, in general, one may no longer a priori fix the domain of auxiliary variables as the state space.

An alternative solution would be to restrict the class of assertions for procedure specifications. As Tarlecki (1985) has pointed out, one usually wants to use a procedure in several different contexts. Therefore, auxiliary variables, which play the rôle of a local reference point, ought to coincide with the value of the variables in the initial state. In other words, one may consider restricting the specification for a procedure activation to the VDM format where the precondition p′(Z,σ) is of the form σ = Z ∧ p_{VDM}(σ) for some predicate p_{VDM} ⊆ Σ(lsort) and the precondition q′ is of the form Assertion(lsort)(Σ(lsort)). Then, for Z : Σ(lsort[x ↦ T]), one may refer to Z.local(x) instead of V. Hence, the context in the premiss could be simplified to

Pr(Γ,x,v)= \{ \begin{matrix}∅ & for Γ = ∅, \\ \{uL(p′,x,v)\}call \{Pr(q′,x,v)\} & for Γ = \{p′\} call \{q′\} \end{matrix}

wherePr(q′,x,v)(Z,τ)=^{def} q(uL(Z,x,v),uL(τ,x,v))∧ τ.local(x)= Z.local(x)

In a setting where the scope of local program variables is provided relative to atomic correctness formulae, one may leave the context (and its environment) invariant in the rule for blocks. When looking up correctness formulae in the context, the difference in environments may then be reconciled in a single step e.g.,

((lsort_{,}∅) | {p} call {q}) ⊢_{Hoare}( ( lsort[x⃗ ↦ T⃗]_{,} x⃗) | {Pr(p)} S {Pr(q)})

where Pr(p)((Z,v⃗,V⃗),σ) =^{def} uL(p,x⃗,v⃗)(Z,σ)∧ ⋀_{i∈I} σ.local(x_{i}) = V_{i}.

3.3.7.3 Soundness

p. 204 Theorem 3.44 (Soundness) The verification calculus defined as the least relation satisfying the rules summarised in Figure 3.10 on the previous page is sound. Proof by induction on the derivation of (lsort,E) | Γ ⊢_{Hoare}{p} S {q}. Consider the case where we have applied the rule for blocks as the last step of the derivation. We abbreviate lsort_{x} =^{def} lsort[x ↦ T]. From the induction hypothesis

∀v: lsort(x)· (lsort_{x},E ∪ {x}) | Pr(Γ,x,v) ⊧_{Hoare}{In(p,x,v,t,E)} S {uL(q,x,v)} (3.46)

we have to establish

(lsort,E) | Γ ⊧_{Hoare}{p} new x:= t in S {q} .

Let Z be an arbitrary auxiliary variable and σ : Σ(lsort) be an arbitrary initial state such that p(Z,σ) holds. In the case where the context Γ contains a correctness formula {p′} call {q′}, the induction hypothesis is no longer directly applicable^{18}. As a new aspect in the proof, given

(lsort,E) | ∅ ⊧_{Hoare}{p′} call {q′} ,  (3.47)

one needs to establish

(lsort_{x},E ∪ {x}) | ∅ ⊢_{Hoare}{Pr(p′,x,v)}call {Pr(q′,x′v)} .  (3.48)

Let Z′,V be arbitrary auxiliary variables and σ′: Σ(lsort_{x}) be an arbitrary initial state such that p′(Z′,uL(σ′,x,v))and σ′.local(x) = V hold. We need to find a final state τ′: Σ(lsort_{x}) such that one has

(lsort_{x},E ∪ \{x\}) | σ′  \overline{call ^{}}τ′  (3.49)
q′(Z′,uL(τ′,x,v))  (3.50)
τ′.local(x)= V  (3.51)

Appealing to (3.47) we may extract a final state τ which satisfies

(lsort,E) | uL(σ′,x,v)  \overline{call ^{}}τ  (3.52)

and q′(Z′,τ). By Cor. 3.42.2, the derivation (3.52) can be lifted to the updated environment (3.49) with τ′=^{def} uL(x,σ′.local(x),τ), because σ′is (extensionally) equal to uL(x,σ′.local(x),uL(σ′,x,v)). Cor. 3.42.1 guarantees that in the derivation of (3.52), τ = uL(τ,x,v). Thus, uL(τ′,x,v)= τ and we may conclude (3.50). Finally, in the light of σ′.local(x) = V, applying Cor. 3.42.1 to the derivation (3.49) ensures the desired (3.51). □

3.3.7.4 Completeness

The proof of completeness follows the structure in the setting with recursive procedures without local program variables in Section 3.1.6 on page 65. We abbreviate

p_{0}(S)(lsort,E)(n)(Z,σ)=^{def} (lsort,E) | σ  \overline{S  n} Z
ψ(lsort,E)(n)(Z,σ) =^{def} p_{0}(call)(n + 1)(lsort,E)(Z,σ)
ψ̂(lsort,E)(n)(Z,σ) =^{def} ∃u: nat· ψ(u)(lsort,E)(Z,σ) ∧ u < n
q(Z,τ)=^{def} Z = τ .

18for v = σ.local(x)

The subtle interaction between procedures and local variables is dealt with in the following theorem. It established the pseudo induction hypothesis of the MGF theorem in the case of a recursive procedure call.

p. 204 Theorem 3.45 (Completeness Step)

(lsort,∅) | {ψ̂(lsort,∅)(n)}call {q}⊢_{Hoare}{ψ(lsort,∅)(n)} S_{0} {q} .

Proof Just like the proof of the corresponding Lemma 3.12 on page 67, appealing to the consequence rule and the definition of bounded recursive depth, we equivalently transform the proof obligation into

(lsort,∅) | {ψ̂(lsort,∅)(n)}call {q}⊢_{Hoare}{p_{0}(S_{0})(lsort,∅)(n)}S_{0} {q} . (3.53)

However, a direct proof by induction on the structure of the procedure body S_{0} would not go through, because

• the context would be unaffected and

• programs would not be able to refer to local program variables.

Thus, the new rule for blocks (3.43) would not be applicable. Instead, we have to consider arbitrary environments E. Moreover, we strengthen the proof obligation and replace ψ̂(lsort,E)(n) and q in the context by arbitrary assertions p_{1},q_{1} such that, according to the rule of consequence,

\frac{(lsort,E) | ∅ ⊢_{Hoare}\{p_{1}\} call \{q_{1}\}}{(lsort,E) | ∅ ⊢_{Hoare}\{ψ̂(lsort,E)(n)\}call \{q\}} .

Hence, we pursue

∀p_{1},q_{1} · KSC(ψ̂(lsort,E)(n),q,p_{1},q_{1})⇒ (lsort,E) | {p_{1}} call {q_{1}}⊢_{Hoare}{p_{0}(S)(lsort,E)(n)}S {q}

where

KSC(p,q,p_{1},q_{1}) =^{def}
∀Z· ∀σ · p(Z,σ) ⇒ ( ∃Z_{1}· p_{1}(Z_{1},σ) ∧ (∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ)) )

by induction on the structure of S. In comparison to the corresponding proof without blocks, one needs to additionally appeal to the rule of consequence in each case to deal with the arbitrary assertions p_{1},q_{1}. As an example, we spell out the case of a procedure invocation. The challenging part is the new case when we encounter a block.

Procedure Call Let p_{1},q_{1} be arbitrary assertions. Given the assumption

KSC(ψ̂(lsort,E)(n),q,p_{1},q_{1}) ,

we need to show

(lsort,E) | {p_{1}} call {q_{1}}⊢_{Hoare}{p_{0}(call)(lsort,E)(n)}call {q} . (3.54)

From being able to access the context, we may derive

(lsort,E) | {p_{1}} call {q_{1}}⊢_{Hoare}{p_{1}} call {q_{1}} .

An appeal to the rule of consequence yields

(lsort,E) | {p_{1}} call {q_{1}}⊢_{Hoare}{ψ̂(lsort,E)(n)}call {q} .

Applying the consequence rule once more leads to (3.54). For a justification of the side-condition, see the corresponding proof in the scenario without blocks on page 67.

Blocks We abbreviate lsort_{x} =^{def} lsort[x ↦ T]. From the induction hypothesis

∀p_{1},q_{1} · KSC(ψ̂(lsort,E)(n),q,p_{1},q_{1})⇒ (lsort_{x},E ∪ {x}) | {p_{1}} call {q_{1}}⊢_{Hoare}{p_{0}(S)(lsort,E)(n)}S {q}

and arbitrary assertions p_{1},q_{1} satisfying

KSC(ψ̂(lsort,E)(n),q,p_{1},q_{1})  (3.55)

we have to establish

(lsort,E) | Γ ⊢_{Hoare}{p_{0}(new x:= t in S)(lsort,E)(n)}new x:= t in S {q}

where Γ = {p_{1}} call {q_{1}}. Appealing to the rule for blocks (3.43), it suffices to show

(lsort_{x},E ∪ {x}) | Pr(Γ,x,v) ⊢_{Hoare}{In(p′,x,v,t,E)} S {uL(q,x,v)}

where p′ =^{def} p_{0}(new x:= t in S)(lsort,E)(n) (3.56)

for an arbitrary constant v : lsort(x). From now on in the proof, we may refer to v because we had chosen to universally quantify v at the level of a sequent. We now exploit the induction hypothesis and instantiate the precondition with Pr(p_{1},x,v) and the postcondition with Pr(q_{1},x,v). This yields

(lsort_{x},E ∪ {x}) | Pr(Γ,x,v) ⊢_{Hoare}{p_{0}(S)(lsort,E)(n)}S {q}  (3.57)

provided the side-condition

KSC(ψ̂(lsort,E)(n),q,Pr(p_{1},x,v),Pr(p_{2},x,v))  (3.58)

is satisfied. The rule of consequence mediates between (3.57) and (3.56). Thus, in addition to (3.58), we are left with having to establish

KSC(In(p′,x,v,t,E),uL(q,x,v),p_{0}(S)(lsort,E)(n),q) .  (3.59)

(3.59) Expanding the definitions of KSC, p′, In, p_{0} and uL, after some straightforward simplifications, this side-condition is similar to the corresponding one in the proof of the MGF theorem on page 83.

(3.58) Given states Z,σ,τ: Σ(lsort_{x}) such that for some u < n, we have

(lsort_{x},E ∪ \{x\}) | σ  \overline{call u+1} Z ,  (3.60)

we have to extract auxiliary variables Z′and v′such that

p_{1}(Z′,uL(σ,x,v′))  (3.61)

and

( q_{1}(Z′,uL(τ,x,v′))∧ τ.local(x)= σ.local(x) ) ⇒ Z = τ .  (3.62)

Having universally quantified v in the premiss of the rule for blocks, we still have a suitable candidate for v′in scope. Otherwise, one would need to restrict the class of sorts so that one can effectively generate an element for every data type. In order to utilise (3.55), we have to lift the assumption (3.60) to

(lsort,E) | uL(σ,x,v)  \overline{call  n} uL(τ,x,v)

by appealing to Cor. 3.42.2. From (3.55), we extract the desired auxiliary variable Z′. It satisfies (3.61) and

q_{1}(Z′,uL(τ,x,v))⇒ uL(Z,x,v)= uL(τ,x,v) .  (3.63)

Dealing with the remaining proof obligation (3.62), assume

q_{1}(Z′,uL(τ,x,v)) and τ.local(x)= σ.local(x) .

By (3.63), it suffices to show that the local entry of x in the state spaces Z and σ are identical. This can be established by considering the derivation (3.60). According to Lemma 3.40, the same derivation holds in the empty environment. Thus, one may conclude the proof with the help of Cor. 3.42.1.

□

p. 204 Theorem 3.46 (MGF)

(lsort,E) | ∅ ⊢_{Hoare}\{ λ(Z,σ) · (lsort,E) | σ \overline{S ^{}}Z\}S \{q\}

Proof by induction on the structure of S.

Procedure Call We need to show

(lsort,E) | ∅ ⊢_{Hoare}\{ λ(Z,σ) · (lsort,E) | σ  \overline{call ^{}}Z\}call \{q\} (3.64)

Applying the rule for procedure invocations (3.41) to Theorem 3.45 yields

(lsort,E) | ∅ ⊢_{Hoare}\{ λ(Z,σ) · ∃n· (lsort,E) | σ \overline{call n+1} Z\}call \{q\} .

(3.65)

We employ the rule of consequence to mediate between (3.65) and (3.64). For a justification of the side-condition, see the corresponding proof without blocks on page 68.

Blocks see the corresponding proof in the scenario without procedures on page 83. □

p. 204 Corollary 3.47 (Completeness) Whenever (lsort,E) | ∅ ⊢_{Hoare}{p} S {q} is derivable from the axioms and rules of Figure 3.10 on page 101, (lsort,E) | ∅ ⊧_{Hoare}{p} S {q} holds. Proof Completeness follows immediately from the MGF Theorem. See the proof of Corollary 2.31 on page 41 for further details. □

3.3.7.5 Admissible Rules

In examples, due to the left-constructive nature of the assignment axiom, it is useful to be able to appeal to admissible left-constructive versions of the consequence rule, see Figure 3.1.8 on page 72, and the rule for blocks, see Figure 3.6 on page 84. Moreover, we derive a rule for non-recursive procedure calls. When combining recursive procedures with local program variables, we need to annotate the rules with contexts and environments.

\frac{(lsort,∅) | ∅ ⊢_{Hoare}\{p\} S_{0} \{q\}}{(lsort,E) | ∅ ⊢_{Hoare}\{p\} call \{q\}}  (3.66)

\frac{(lsort,E) | Γ ⊢_{Hoare}\{p_{1}\} S \{q_{1}\}}{(lsort,E) | Γ ⊢_{Hoare} \left\{{ \atop p̂}\right\} S \{q\}}

with p̂(Z,σ) =^{def} ∃Z_{1}· p_{1}(Z_{1},σ) ∧ ∀τ· q_{1}(Z_{1},τ)⇒ q(Z,τ). (3.67)

∀v· (lsort[x ↦ T],E ∪ {x}) | Pr(Γ,x,v)⊢_{Hoare}{p} S {uL(q,x,v)} (lsort,E) | Γ ⊢_{Hoare}{λ(Z,σ) · uL(p,x,eval(E,σ)(t))}new x:= t in S {q}

(3.68)

Figure 3.11: Admissible Rules for Static Binding in Hoare Logic

p. 205 Corollary 3.48 (Soundness of Admissible Rules for Static Binding) The rules from Figure 3.11 are admissible. Proof Procedure Call By soundness and completeness it suffices to show that

(lsort,∅) | σ \overline{S_{0} ^{}}τ

implies

(lsort,E) | σ \overline{call ^{}}τ .

This follows immediately from the operational semantics. Adaptation The rule (3.67) is a trivial instance of the consequence rule (3.45). Blocks Given

∀v· (lsort[x ↦ T],E ∪ {x}) | Pr(Γ,x,v)⊢_{Hoare}{p} S {uL(q,x,v)} we need to establish

(lsort,E) | Γ ⊢_{Hoare}{λ(Z,σ) · uL(p,x,eval(E,σ)(t))}new x:= t in S {q}

(3.69)

By the rule of consequence, we may replace p by

λ(Z,σ) · In( uL(p,x,eval(E,σ)(t)),x,v,t,E) .

Then, the rule for blocks (3.43) is applicable and yields the desired (3.69).

□

3.3.7.6 Example

We return to Example 3.31 on page 87 to illustrate how the new set of rules disambiguates global and local program variables.

p. 206 Lemma 3.49 (Static Binding) Let E be an arbitrary environment (lsort,E) such that E ∩ {x,z} = ∅ i.e., x and z are global program variables. Given the procedure declaration S_{0} =^{def} x:= z, one may derive (lsort,E) | ∅ ⊢_{Hoare}{true } z:= 1; new z:= 0 in call {x = 1}.

Proof Semantically, the postcondition [[x = 1]](Z,τ) corresponds to

lookup(E,τ)(x)= 1

which expands to τ.global(x)= 1. Thus, updating the value of local program variables has no effect. We follow the convention introduced in Example 3.35 on page 90 that [[x]](Z,σ)= σ.global(x) and [[ẋ]](Z,σ)= σ.local(x).

Appealing to the rule of sequential composition, we split the proof obligation into

(lsort,E) | ∅ ⊢_{Hoare}{true } z:= 1 {?i_{1}}  (3.70)
(lsort,E) | ∅ ⊢_{Hoare}{?i_{1}}new z:= 0 in call {x = 1}  (3.71)

where ?i_{1} is an intermediate proof obligation which can be synthesised courtesy of the left-constructive version of the rule for blocks. Refining the second correctness formula (3.71) by the rule (3.68) yields

(lsort[z ↦ nat],E ∪ {z}) | ∅ ⊢_{Hoare}{?i_{2}}call {x = 1}

where

?i_{1} = uL(?i_{2},z,0) .  (3.72)

We then use rule (3.66) to cater for the (non-recursive) procedure invocation. This gives us the new subgoal (lsort[z ↦ nat],∅) | ∅ ⊢_{Hoare} {?i_{2}}x:= z {x = 1}. By the axiom of assignments, the precondition ?i_{2} gets bound to z = 1 where, due to the environmental change caused by the procedure call, z refers to the value of the global program variable. Hence, synthesising ?i_{1} according to (3.72) leads to z = 1 (and not 0 = 1). With this instantiation, it is straight-forward to derive the remaining proof obligation (3.70). □

3.3.8 The Vienna Development Method

We extend the rules of Figure 3.4 on page 76 with environments and a rule for blocks. In the procedure invocation rule, the premiss is restricted to the empty environment to enforce static scoping. The conclusion holds for an arbitrary number of local variables. In the rule for blocks, we need to also adjust the environment of the context. We had already discussed the required modifications for VDM in Sect. 3.3.7.2 on page 99.

p. 213 Theorem 3.50 (Soundness) The rules of Figure 3.12 on the next page are sound. Proof by induction on the derivation of correctness formulae. □

p. 214 Theorem 3.51 (Completeness) The rules of Figure 3.12 on the facing page are complete. Proof Similar to the corresponding proof in the setting of Hoare Logic. □

3.4 Parameter Passing

For practical applications, it is clearly important to support procedures with parameters. In this section we discuss call-by-value and call-by-name parameters.

Let S_{0}(T val a,U var x) be a procedure declaration in which the program S_{0} may refer to the program variables a and x with sorts T and U. One refers to a as a formal value parameter and to x as a formal variable parameter. The procedure is invoked by call (t,v)where t is an expression and v a program variable. One refers to t as the actual value parameter and to v as the actual variable parameter (Dahl 1992).

On procedure entry, the expression t is evaluated in the current environment and its value is assigned to the variable a. With call-by-name passing, the variable x is aliased to v i.e., in executing S_{0}, all occurrences of x are effectively replaced by v. To adequately deal with call-by-name parameters, it seems advisable to abandon the implicit stack encoding of states. Ah-kee (1990) develops VDM proof rules for parameter passing based on the address mechanism.

Call-by-value parameters are easier to deal with because this mechanism can be simulated in the presence of local program variables (Morgan 1988). One allocates a new variable a, initialises it with the value of t and proceeds with executing S_{0}:

(lsort[a ↦ T],\{a\}) | uL(σ,a,eval(E,σ)(t))  \overline{S_{0}  ^{}}τ
(lsort,E) | σ \overline{call t ^{}}uL(τ,a,σ.local(a))

However, one needs to take care to evaluate the expression t in the correct environment E. Let us first consider the case where all procedure invocations employ the

(lsort,E) | {p} call {q}⊢ {p} call {q} (lsort,E) | Γ ⊢ {λσ · true }skip {λ(τ,σ) · τ = σ}

(lsort,E) | Γ ⊢ {λσ · true }x:= t {λ(τ,σ) · τ = σ[x ↦ eval(σ)(t)]}

\frac{(lsort,E) | Γ ⊢ \{p_{1}\} S_{1} \{λ(τ,σ) · p_{2}(τ) ∧ r_{1}(τ,σ)\} (lsort,E) | Γ ⊢ \{p_{2}\} S_{2} \{r_{2}\}}{(lsort,E) | Γ ⊢ \{p_{1}\} S_{1}; S_{2} \{r_{2}∘ r_{1}\}}

\frac{(lsort,E) | Γ ⊢ \{λσ · p(σ) ∧ eval(σ)(b) = true \}S_{1} \{q\} (lsort,E) | Γ ⊢ \{λσ · p(σ) ∧ eval(σ)(b) = false \} S_{2} \{q\}}{(lsort,E) | Γ ⊢ \{p\} if b then S_{1} else S_{2} \{q\}}

\frac{(lsort,E) | Γ ⊢ \{λσ · p(σ) ∧ eval(σ)(b) = true \}S \{λ(τ,σ) · p(τ)∧ sofar(τ,σ)\}}{(lsort,E) | Γ ⊢ \{p\} while b do S \{q\}}

where sofar is any binary transitive relation on the state space Σ such that λ(τ,σ) · sofar(τ,σ)∧ p(τ) is well-founded and q(τ,σ) =^{def} p(τ)∧ eval(τ)(b)= false ∧ (sofar(τ,σ)∨ τ = σ ) .

∀t : W· (lsort,∅) | {λσ · ∃u· p(u)(σ) ∧ u < t}call {q}⊢ {p(t)}S_{0} {q}

(lsort,E) | ∅ ⊢ {λσ · ∃t : W· p(t)(σ)}call {q} where (W,<) is well-founded.

\frac{∀v· (lsort[x ↦ T],E ∪ \{x\}) | Pr(Γ,x,v) ⊢_{VDM} \begin{matrix}\{ & In & (p,x,v,t,E) & \} \\ S \\ \{ & uL & (q,x,v) & \} \end{matrix}}{(lsort,E) | Γ ⊢ \{p\} new x:= t in S \{q\}}

with Pr(Γ,x,v) =^{def} \{ \begin{matrix}∅ & for Γ = ∅, \\ \{uL(p′,x,v)\}call \{Pr(q′,x,v)\} & for Γ = \{p′\} call \{q′\} \end{matrix}

Pr(q′,x,v)(τ,σ) =^{def} uL(q′,x,v)(τ,σ)∧ τ.local(x)= σ.local(x) In(p,x,v,t,E)(σ) =^{def} uL(p,x,v)(σ)∧ σ.local(x) = eval(E,uL(σ,x,v))(t) .

\frac{(lsort,E) | Γ ⊢ \{p_{1}\} S \{q_{1}\}}{(lsort,E) | Γ ⊢ \{p\} S \{q\}}

provided ∀σ · p(σ) ⇒ p_{1}(σ) and ∀(τ,σ) · p(σ) ⇒ q_{1}(τ,σ) ⇒ q(τ,σ).

Figure 3.12: VDM and Static Binding same call-by-value parameter t. One may simulate all occurrences of call tby the procedure new a:= t in S_{0} if the actual value parameter t refers only to global program variables. In particular, restricting our attention to non-recursive procedures and partial correctness, in the case where the expression t amounts to a global program variable a′, call-by-value parameter passing can be dealt with by the rule

∀v· (lsort[a ↦ T],{a}) | {In(p,a,v,a′,E)}S_{0} {uL(q,a,v)}

(lsort,E)| {p} call a′{q} for a′∉ E.

Dahl (1992) proposes dealing with the general case of arbitrary expressions t as actual value parameters by considering the translation

S_{0} ⇝ new a:= a′in S_{0}
call t⇝ a′:= t; call a′

(3.73)

This leads to the rule

∀v· (lsort[a ↦ T],{a}) | {In(p,a,v,a′,E)}S_{0} {uL(q,a,v)}

(lsort,E) | {λ(Z,σ) · p(Z,(σ.global[a′↦ t],σ.local))}call t{q} for a′∉ E.

It would be an interesting further research contribution to mechanically establish soundness and completeness for verification calculi dealing with parameter passing. In the following chapter, in verifying Quicksort, we avoid call-by-value parameter passing by directly translating all procedure calls and procedure bodies according to (3.73). Moreover, we replace call-by-name parameters by global program variables.

Case Study: Quicksort

In the previous chapters, we have demonstrated that it is feasible to conduct soundness and completeness proofs for verification calculi on a machine. As a by-product, we have gained a new set of verification rules for imperative programs dealing with recursive (parameterless) procedures and local variables. Soundness and completeness are important meta-theoretical properties. However, it remains to be shown that the new verification calculi are suitable in practice. Clearly, we cannot really expect to verify industrial-size programs at this stage. Our results would need to be extended to better support modular program development. In particular, we cannot directly deal with multiple procedures or parameter passing.

In this chapter, using Hoare’s (1961) Quicksort algorithm as an example, we show that our Hoare Logic rules lend themselves to proving the correctness of a concrete recursive procedure with local and global variables.

Verification systems such as Hoare Logic themselves do not actually impose a strict methodology on how to verify programs. At one end of the spectrum, one can derive a Verification Condition Generator (VCG) from the set of rules. Given a program annotated with loop invariants and specifications for recursive procedures, a VCG automatically produces a set of propositions, which, if proven correct, guarantee the correctness of the desired program. Using the B method (Abrial 1996) as the underlying verification system, this approach has for example been employed by GEC Alsthom to verify the speed- and switching-control software used in all of France’s 6000 electric trains. On the other end of the spectrum, Dijkstra (1990), Gries (1981) and others advocate developing the program hand-in-hand^{1} with its proof of correctness.

Our specification follows Reynolds’s (1981) presentation. The arguments in the correctness proof are well-known. Foley & Hoare had already published a proof of partial correctness for Quicksort in 1971. In our presentation, besides also considering termination, we highlight the rôle of the strengthened consequence rule and the new rules required in the presence of recursive procedures and local variables.

1Formally, such an approach may be described by first-order deliverables (Burstall & McKinna 1993).

The formal rigour of the correctness proof is an orthogonal issue. Current industrial applications seem to be aided by some tool support, but purely logical propositions (arising from a consequence rule) are mostly dealt with on paper only. We have conducted the complete verification using the Lego system.

The outline of this chapter is as follows. We first sketch the required background theory of sorted arrays. We then present Reynolds’s (1981) specification and discuss the modifications required so that the program can be expressed in our toy language framework. At the heart of the chapter lies the verification proof.

4.1 A Theory of Sorted Arrays

In his thesis, McKinna (1992) investigates correctness proofs of functional programs in Lego. We chose to reuse a substantial amount of his theory of sorted lists, which McKinna formalised to justify the correctness of Insert Sort.

In an imperative scenario, we have to deal with arrays. These are quite different from lists. There is no recursive structure and any element can be accessed in constant time. For efficiency, the algorithms rely on call-by-name passing (or global variables) of arrays. An imperative sorting algorithm requires three arguments: the array, the lower and the upper bound. The verification requires a little more care, because one has to ensure that a recursive call only manipulates the array within the specified bounds. We decided to represent arrays as (total) function spaces. We chose nat → nat as the sort for the program variable x storing the array. Any entry in an array x[c] corresponds to x(c). An assignment to a particular field in an array can then be represented by updating, see Section 2.6.1 on page 15 for details.

As a disadvantage of reusing McKinna’s (1992) scripts, we have to convert functions to lists relative to some array bounds. Thus, we can no longer directly exploit the structure of arrays. It might have been more elegant to develop a theory of sorting based directly on arrays. However, we believe this would have taken more time and decided it would not be worth the effort. We will keep the presentation short and informal.

4.1.1 Ordered Lists

Given an ordering relation such as < on natural numbers, one can lift this relation pointwise to lists. We overload notation e.g., if k and l are lists of natural numbers, k < l denotes that every element in the list k is less than every element in the list l.

Lifting is also useful to define the notion of an ordered list. For every element x in the list, every element appearing later than x in the list must be greater than or equal to x.

Definition 4.1 (Ordered Lists) Let R ⊆ T × T be an arbitrary relation.

ord(R)([])= true
ord(R)(a:: l)= [a]R l∧ ord(R)(l)

4.1.2 Subarrays

In sorting algorithms such as Quicksort, one needs to assert that a subarray, characterised by array boundaries, is sorted. Representing arrays as functions, we have implemented a function natl(x)(a,b), which, given a function x with domain nat and boundaries a,b : nat, returns the list of all elements between a and b, intuitively, [x(a),... ,x(b)].

The main idea behind Quicksort is to recursively partition the array into two subarrays such that all elements in the first are less than or equal to those in the second. This involves swapping of elements. Due to the assignment axiom, on the level of assertions, one needs to update array elements. One therefore has to investigate how updating interacts with the translation from functions to lists. Either updating has no effect, because it affects elements outside the array bounds, or it changes an intermediate value and one may factorise the array accordingly.

p. 217 Lemma 4.2 (External Update) Let a, b and c be natural number such that either c < a or b < c, then natl(x[c ↦ t])(a,b)= natl(x)(a,b). Proof by induction on the array bounds a and b for a fixed element t. □

p. 217 Lemma 4.3 (Factorisation) Let a, b and c be natural numbers such that a ≤ b ≤ c, then

natl(x[b ↦ t])(a,c)= (if b = 0 then [] else natl(x)(a,b − 1)) @ (t :: (natl(x)(b + 1,c))) .

Notice that the conditional is necessary, because, for a = b = 0, we have by definition that b−1 = 0 : nat. Thus, natl(x)(a,b−1) reduces to x(0) and not the required empty list. Proof by induction on the array bounds a and c. The proof has an interesting twist in that one needs to generalise over an arbitrary term t. □

Having explored the effect of updating, we can show that swapping two elements via updating preserves the permutation property.

Definition 4.4 (Permutation) McKinna defines ∼ as the smallest equivalence relation which identifies lists up to the commutativity of the append function, and is closed under adding a new element at the front:

l ∼ l  \frac{k ∼ l}{l ∼ k}  \frac{k ∼ l l ∼ m}{k ∼ m}  k @ l ∼ l @ k  \frac{k ∼ l}{(a:: k)∼ (a:: l)}

p. 217 Lemma 4.5 (Swapping Elements) Let a, b, c and d be natural numbers with a ≤ c ≤ b and a ≤ d ≤ b. Then natl(x[d ↦ x(c)][c ↦ x(d)])(a,b)∼ natl(x)(a,b). Proof by case analysis on whether c < d, c = d or c > d. □ New in the imperative scenario is the desired property that elements outside the boundaries of the array are not affected by the sorting algorithm. Hence, we need to express that two arrays X and x are permutations within a specified region and identical otherwise.

p. 219 Definition 4.6 (Permutation on Subarrays)

X _{a}∼ ^{b} x=^{def}
(natl(X)(a,b)∼ natl(x)(a,b)) ∧ (∀c· (c < a ∨ b < c) ⇒ X(c)= x(c))

4.1.3 High-Level Correctness Argument

The array to be sorted undergoes a series of transformations during the process of Quicksort. First, it is reordered to form a partition such that elements in the first are smaller than or equal to all elements in the second. Then, the algorithm is applied recursively to the two partitions.

Starting with the original array X_{0}, let X_{1} and X_{2} denote the two intermediate arrays, and X_{3} the final array. From the progress achieved in the individual steps, one needs to establish that X_{3} is ordered, a permutation of X_{0} and that none of the elements outside of the array boundaries have been modified.

p. 219 Definition 4.7 (Sorted Arrays) X _{a}Sorted^{b} x=^{def} ord(≤ )(natl(x)(a,b))∧ X _{a}∼ ^{b} x p. 219 Theorem 4.8 (Quicksort) Let a and b be the array boundaries, and c the intermediate

position computed by the partitioning process such that a < c ≤ b. Given

X_{0} _{a}∼ ^{b} X_{1}  (4.1)
natl(X_{1})(a,c − 1) ≤ natl(X_{1})(c,b)  (4.2)
X_{1} _{a}Sorted^{c−1} X_{2}  (4.3)
X_{2} _{c}Sorted^{b} X_{3}  (4.4)

one can justify X_{0} _{a}Sorted^{b} X_{3}. Proof Combining the stability conditions outside the array boundaries of (4.3) and (4.4), we may exploit

natl(X_{1})(c,b)= natl(X_{2})(c,b)  (4.5)
natl(X_{2})(a,c − 1) = natl(X_{3})(a,c − 1)  (4.6)

We show how to establish ord(≤ )(natl(X_{3})(a,b)) in more detail. Appealing to the permutation properties of (4.3) and (4.4), as well as the equalities (4.5) and (4.6), from the partitioning (4.2), we may conclude

natl(X_{3})(a,c − 1) ≤ natl(X_{3})(c,b) .  (4.7)

Moreover, rewriting (4.3) with the help of (4.6) yields

ord(≤ )(natl(X_{3})(a,c − 1))  (4.8)

Hence, combining (4.7), (4.8) and the order property of (4.4) established that the list natl(X_{3})(a,b) is sorted. □

4.2 Specification of Quicksort

We follow Reynolds’s (1981) top-down presentation. Relative to a program Prepare which partitions the array into two fragments k and l with k ≤ l, we recursively sort k and l. A Hoare triple which characterises the behaviour of Prepare is

{a < b ∧ a = A ∧ b = B ∧ x = X} Prepare {A < c ≤ B ∧ natl(X)(A,c − 1)≤ natl(X)(c,B)∧ x _{A}∼ ^{B}X∧ a = A ∧ b = B}

Lemma 4.9 (Rigorous Correctness of Quicksort) We pass the boundaries a,b as callby-values and the array x as call-by-name. procedure quicksort(nat val a,b; nat array var x);

{a = A ∧ b = B ∧ x = X}

if a < b then begin nat c; Prepare; quicksort(a,c − 1,x); quicksort(c,b,x)

end

{X _{A}Sorted^{B}x}

The Preparefragment builds on a general partition procedure

procedure partition(nat val a,b,r; nat var c; nat array var x); {a = A ∧ b = B ∧ x = X} begin nat d;

c:= a; d:= b

while c ≤ d do

if x[c] ≤ r then c:= c + 1 else

if x[d] > r then d:= d − 1 else

begin begin nat t; t:= x[c]; x[c] := x[d] ; x[d] := tend;

c:= c + 1 ; d:= d − 1

end

end

{A ≤ c ≤ d + 1 ∧ natl(x)(A,c − 1) ≤ r ∧ r < natl(x)(c,B) ∧ x _{A}∼ ^{B}X∧ a = A ∧ b = B}

If Preparewere to simply call partition(a,b,r,c,x), there would be no guarantee that both partitions are strictly smaller. Without this additional property, the above Quicksort procedure may not terminate. Reynolds (1981) overcomes this problem by analysing the outermost elements x[a] and x[b] and swaps them in case x[a]> x[b]. By narrowing the partitioning algorithm to the subarray natl(x)(a + 1,b − 1) for some intermediate pivot element r which lies between x[a] and x[b], one can guarantee that Preparefinds a value c such that a < c ≤ b:

Definition 4.10 (Prepare)

{a < b ∧ a = A ∧ b = B ∧ x = X} begin nat r;

if x[a] > x[b] then begin nat t; t:= x[a]; x[a] := x[b] ; x[b] := tend; r:= (x[a]+ x[b]) ÷ 2; partition(a + 1,b − 1,r,c,x) end {A < c ≤ B ∧ natl(X)(A,c − 1)≤ natl(X)(c,B)∧ x _{A}∼ ^{B}X∧ a = A ∧ b = B}

4.2.1 Mechanisation

Being primarily interested in the meta-theory of verification calculi, we have chosen a shallow embedding for assertions. Unfortunately, this renders examples illegible. For the purpose of this chapter, we present assertions at the syntactic level. These have been translated by hand from the actual Lego scripts.

All local program variables have sort nat. We therefore annotate correctness formulae only with a set of local program variables in scope E instead of an environment (lsort,E).

We only consider assertions where the domain of auxiliary variables coincides with the state space. In assertions, lowercase (free) variable names correspond to the value of program variables in the current state, whereas uppercase variable names correspond to auxiliary variables. Intuitively, auxiliary variables capture the initial value of program variables.

Example 4.11 In an environment E recording all local variables, the assertion

a = A ∧ b = B ∧ a < b

is to be parsed as

λ(Z,σ) · lookup(E,σ)(a) = lookup(E,Z)(a) ∧ lookup(E,σ)(b)= lookup(E,Z)(b) ∧ lookup(E,σ)(a) < lookup(E,σ)(b) .

Our toy language does not support multiple procedures, nor parameter passing. Therefore, we need to reformulate the algorithm in more primitive terms. To circumvent parameter passing in Def. 4.9, we replace the call-by-name parameter x by a global program variable.

We eliminate call-by-value parameters by following the recipe of Sect. 3.4 on page 110. We declare the parameters a′ and b′ as global program variables. At the beginning of the procedure, we allocate two local variables a and b and initialise them with the values of a′and b′at procedure invocation time. Moreover, we have to take care of uninitialised local program variables, either by rearranging code, or, by choosing some arbitrary initial value. p. 221 Definition 4.12 (Quicksort)

S_{0} =^{def} new a:= a′in new b:= b′in

if a < b then new c:= 0 in

begin

{Prepare \atop ;}

a′:= a; b′:= c − 1
call;
a′:= c; b′:= b;

call end

As the partitioning algorithm is non-recursive, we macro-expand its invocation in Def. 4.10. As a further simplification, we set the pivot element to x[a] instead of (x[a]+ x[b]) ÷ 2, because, given x[a]≤ x[b], it it easier to show x[a]≤ x[a]≤ x[b]than x[a]≤ (x[a]+ x[b]) ÷ 2 ≤ x[b].

p. 220 Definition 4.13 (Prepare)

Prepare=^{def} if x[a]> x[b]then new t:= x[a]

in begin x[a] := x[b] ; x[b] := tend;

new r:= x[a] in begin

c:= a + 1;

new d:= b − 1 in

while c ≤ d do
if x[c] ≤ r then c:= c + 1 else

if x[d] > r then d:= d − 1 else

begin

new t:= x[c]

in begin
x[c] := x[d] ; x[d] := t
end;
c:= c + 1 ; d:= d − 1

end

end

The specification for the Quicksort algorithm remains the same. However, due to our encoding of parameter passing, we have to ensure that the program variables a′, b′ and x are global. p. 235 Lemma 4.14 (Correctness of Quicksort) Let E be an arbitrary environment such that

E ∩ {a′,b′,x} = ∅. We can then derive

E | ∅ ⊢_{Hoare}{a′= A′∧ b′= B′∧ x = X} call {X _{A′}Sorted^{B′}x}

In order to prove this, we establish the intermediate result:

p. 234 Lemma 4.15 (Correctness of Prepare) Let E be an arbitrary environment^{2} and Γ be an arbitrary context.

E | Γ ⊢_{Hoare}{a < b ∧ a = A ∧ b = B ∧ x = X } Prepare {variant_{Prepare}(x)(c)∧ a = A ∧ b = B }

where

p. 222 Definition 4.16 (Variant for PrepareFragment)

variant_{Prepare}(x)(c) =^{def}

A < c ≤ B ∧ natl(X)(A,c − 1)≤ natl(X)(c,B)∧ x _{A}∼ ^{B}X

An important and challenging aspect of the specification is to provide a suitable loop invariant. Based on Reynolds’s (1981) version for the partition algorithm, we have employed the following assertion:

p. 222 Definition 4.17 (Invariant for PrepareFragment)

inv_{Prepare}(c,d) =^{def} A < c ≤ d + 1 ∧ d < B ∧ natl(x)(A,c − 1) ≤ r ∧ r < natl(x)(d + 1,B − 1) ∧ r ≤ x[B]∧

x _{A}∼ ^{B}X∧ a = A ∧ b = B

4.3 A Guided Tour of the Verification Proof

Our formalised verification calculus does not directly permit multiple procedures. The declaration of the procedure in Def. 4.12 contains only procedure activations of itself. We have used Lego’s support for definitions to emphasise that the Preparefragment corresponds to a procedure invocation. In the correctness proof, we exploit Lego’s support for structuring proofs to independently verify the correctness of Prepareand Quicksort.

2For simplicity, in the mechanised proof, we only consider the particular environment E = {a,b,c} arising in the proof of Quicksort.

We commence by assuming Lemma 4.15 and show Lemma 4.14 i.e., that the skeleton of the Quicksort algorithm is correct. Afterwards, we complete the proof by verifying the Prepareimplementation.

The main purpose of this section is to clarify how to use our set of Hoare Logic rules in practice. It is well-known that the whole process can be automated with a Verification Condition Generator.

4.3.1 Verification of Quicksort Skeleton

Our proof strategy is to employ the rule for well-founded recursion (3.41) on page 99. To use this we need to plant a termination measure in the precondition. For Quicksort, every call works on a smaller subarray. Its length is determined by the expression (b′− a′) + 1. We therefore employ the consequence rule (3.45) to equivalently transform the precondition to ∃n·n = (b′− a′) + 1 ∧a′= A′∧ b′= B′∧ x = X . Specialising rule (3.41) to the usual well-founded order < on nat results in the subgoal

∅ | Γ ⊢_{Hoare}{n = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X } S_{0}

{X _{A′}Sorted^{B′}x}

where n: nat is an arbitrary reference and

Γ =^{def} {∃m· m = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧ m < n }

call

{X _{A′}Sorted^{B′}x}

The outermost constructors of the program S_{0} successively determine which verification rules to apply. We commence by compensating for the local variable a. Since the pre- and postcondition do not mention another (local) program variable a, the rule for blocks (3.43) on page 100 only adds a = a′in the precondition and a = A in both assertions of the context. In particular, we have to derive

{a} | Pr(Γ,a,v_{a}) ⊢_{Hoare}{n = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧ a = a′}

new b:= b′in

if a < b then new c:= 0 in

begin

{Prepare \atop ;}

a′:= a; b′:= c − 1
call;

a′:= c; b′:= b;

call end {X _{A′}Sorted^{B′}x}

Similarly, we remove the other local variable declarations and split the conditional. The case where the array to be sorted contains at most one element can be resolved easily. Otherwise, we have to show

{a,b,c} | Γ_{a,b,c} ⊢_{Hoare}{n = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧

a = a′ ∧ b = b′∧ a < b ∧ c = 0 }

{Prepare \atop ;}

a′:= a; b′:= c − 1
call;
a′:= c; b′:= b;

call

{X _{A′}Sorted^{B′}x}

where for

p_{1}(A,A′,B,B′,C,X)(a,a′,b,b′,c,x) =^{def}
∃m· m = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧ m < n ∧
a = A ∧ b = B ∧ c = C

and

q_{1}(A,A′,B,B′,C,X)(a,a′,b,b′,c,x) =^{def}
X _{A′}Sorted^{B′}x∧ a = A ∧ b = B ∧ c = C

we define

Γ_{a,b,c} =^{def} Pr( Pr(Pr(Γ,a,v_{a}),b,v_{b}),c,v_{c})

= \begin{matrix}\{p_{1}(A,A′,B,B′,C,X)(a,a′,b,b′,c,x)\} \\ call \\ \{q_{1}(A,A′,B,B′,C,X)(a,a′,b,b′,c,x)\} \end{matrix}.

The assignment axiom can cope with an arbitrary postcondition. Hence, it is advisable, to break down the sequence of statements in reverse order. Splitting off the last procedure invocation yields

{a,b,c} | Γ_{a,b,c} ⊢_{Hoare}{n = (b − a) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧

a′= a ∧ b′= b ∧ a′< b′ ∧ c = 0 }

{Prepare \atop ;}

a′:= a; b′:= c − 1

call

{?i[b ↦ b′][a ↦ c]}

and

{a,b,c} | Γ_{a,b,c} ⊢_{Hoare} {?i}call{X _{A′}Sorted^{B′}x}  (4.9)

where ?i is an intermediate assertion to be synthesised.

Ideally, ?i should be as weak as possible. It can be computed with the help of the rule of adaptation (3.67) applied to (4.9) and the correctness formula in the context Γ_{a,b,c}. This yields the precondition

∃A_{1},A′_{1},B_{1},B′_{1},C_{1},X_{1} · p_{1}(A_{1},A′_{1},B_{1},B′_{1},C_{1},X_{1})(a,a′,b,b′,c,x) ∧

∀a_{1},a′_{1},b_{1},b′_{1},c_{1},x_{1}· (q_{1}(A_{1},A′_{1},B_{1},B′_{1},C_{1},X_{1})(a_{1},a′_{1},b_{1},b′_{1},c_{1},x_{1}) ⇒

X _{A′}Sorted^{B′}x_{1})

The existential quantifiers are uniquely determined by the assertion p_{1}. Furthermore, we can simplify q_{1} because the variables a_{1}, a′_{1}, b_{1}, b′_{1} and c_{1} do not occur in the assertion X _{A′}Sorted^{B′}x_{1}. Thus, we may instantiate

?i= (b′− a′) + 1 < n ∧ (∀X_{3}· x _{A′}Sorted^{B′}X_{3}⇒ X _{A′}Sorted^{B′}X_{3})

Adopting the same strategy, we remove the first recursive procedure invocation and are left with having to show

{a,b,c} | Γ_{a,b,c} ⊢_{Hoare}{n = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧

a = a′ ∧ b = b′∧ a < b ∧ c = 0 }

Prepare

{((c − 1) − a′) + 1 < n ∧ (b′− c) + 1 < n ∧ (∀X_{2}· x _{a′}Sorted^{c−1} X_{2}⇒

∀X_{3}· X_{2} _{c}Sorted^{b′}X_{3}⇒ X _{A′}Sorted^{B′}X_{3})}

One could continue this strategy and push the cumulative postcondition through the Preparefragment. Instead, we follow Reynolds’s specification in which Prepareis a procedure and proceed by stepwise-refinement i.e., we employ the consequence rule (3.45) to resolve the open goal with the help of the postulated correctness of the Preparefragment according to Lemma 4.15. We then independently establish the correctness of the partitioning for a concrete implementation.

Notice that we require the strengthened consequence rule (3.45) to exploit n = (b′− a′) + 1, a property exclusively recorded in the precondition when resolving the termination guarantees ((c−1)−a′)+1 < n and (b′−c)+1 < n of the postcondition. More precisely, modulo some trivial simplifications, our strengthened consequence rule generates the following side-condition:

n = (b′− a′) + 1 ∧ a′= A′∧ b′= B′∧ x = X ∧ a = A′∧ b = b′∧ a < b ⇒

∀c,X_{1}· variant_{Prepare}(X_{1})(c)⇒ ((c − 1) − a′) + 1 < n ∧ (b′− c) + 1 < n ∧ (∀X_{2}· x _{a′}Sorted^{c−1} X_{2}⇒∀X_{3}· X_{2} _{c}Sorted^{b′}X_{3}⇒ X _{A′}Sorted^{B′}X_{3}))

The inequalities are easily resolved. The last part of the conjunction corresponds to Theorem 4.8 on page 116 in the presence of variant_{Prepare}(X_{1})(c).

4.3.2 Verification of the Preparefragment

The only interesting aspect in this proof revolves around the loop. Using the sequential rule, we first split the proof obligation into

E | Γ ⊢_{Hoare}{a < b ∧ a = A ∧ b = B ∧ x = X }

if x[a]> x[b]then new t:= x[a]

in begin x[a] := x[b] ; x[b] := tend
{?i_{1}}

and

E | Γ ⊢_{Hoare}{?i_{1}}

new r:= x[a] in begin

c:= a + 1;

new d:= b − 1 in

while c ≤ d do
if x[c] ≤ r then c:= c + 1 else

if x[d] > r then d:= d − 1 else

begin

new t:= x[c]

in begin
x[c] := x[d] ; x[d] := t
end;
c:= c + 1 ; d:= d − 1

end

end {variant_{Prepare}(x)(c)∧ a = A ∧ b = B }

where the intermediate assertion ?i_{1} is eventually uniquely determined by the loop invariant and can be synthesised, provided we employ the left-constructive version of the rule for dismantling blocks (3.68) which unifies

?i_{1} = uL(?i_{2},r,x[a])

and yields the proof obligation

E ∪ {r} | Pr(Γ,r,v_{r}) ⊢_{Hoare}{?i_{2}} c:= a + 1;

new d:= b − 1 in
while c ≤ d do
if x[c] ≤ r then c:= c + 1 else

if x[d] > r then d:= d − 1 else

begin

new t:= x[c]

in begin
x[c] := x[d] ; x[d] := t
end;
c:= c + 1 ; d:= d − 1

end

{variant_{Prepare}(x)(c)∧ a = A ∧ b = B }

We employ the sequential, assignment and left-constructive rule for blocks to successively decompose the program fragment until we reach the loop. This process yields another intermediate assertion ?i_{3} such that

?i_{2} = ?i_{3}[c ↦ a + 1 ]
?i_{3} = uL(inv_{Prepare}(c,d),d,b − 1 )

and

E ∪ {r,d} | Γ_{r,d} ⊢_{Hoare}while c ≤ d do

if x[c] ≤ r then c:= c + 1 else if x[d] > r then d:= d − 1 else

begin

new t:= x[c]

in begin

x[c] := x[d] ; x[d] := t
end;
c:= c + 1 ; d:= d − 1

end

{variant_{Prepare}(x)(c)∧ a = A ∧ b = B }

with

Γ_{r,d} =^{def} Pr(Pr(Γ,r,v_{r}),d,v_{d}) .

Dealing with the loop, we apply the consequence rule to strengthen the postcondition to inv_{Prepare}(c,d)∧ ¬(c ≤ d) . Progress is achieved by extending the first segment, the second segment, or both. A suitable termination measure keeps track of the length of the intermediate segment (d + 1) − c. Thus, applying the rule for well-founded loops (3.44), for a constant w: (nat,<), we are left with having to establish

E ∪ {r,d} | Γ_{r,d} ⊢_{Hoare}{inv_{Prepare}(c,d)∧ c ≤ d ∧ (d + 1) − c = w }

if x[c] ≤ r then c:= c + 1 else if x[d] > r then d:= d − 1 else

begin

new t:= x[c]

in begin
x[c] := x[d] ; x[d] := t
end;
c:= c + 1 ; d:= d − 1

end {inv_{Prepare}(c,d)∧ (d + 1) − c < w }

The cases where either x[c]≤ r or x[d]> r are straightforward. Otherwise, we have to show

E ∪ {r,d} | Γ_{r,d} ⊢_{Hoare}{inv_{Prepare}(c,d)∧ c ≤ d ∧ (d + 1) − c = w ∧ ¬(x[c]≤ r) ∧ ¬(x[d]> r) }

new t:= x[c]
in begin
x[c] := x[d] ; x[d] := t

end {inv_{Prepare}(c + 1)(d − 1) ∧ ((d− 1) + 1) − (c + 1) < w }

To complete the proof, we need to appeal once more to the rule of consequence. We could either 1. commence by applying the top-down version of the rule for blocks (3.43) and

appeal to the consequence rule when dealing with the assignment x[c] := x[d].

2. first apply the rule of consequence so that the left-constructive version of the rule for blocks (3.11) becomes applicable. One can then immediately resolve x[c] := x[d] by the axiom for assignments.

We have pursued the first option. Given

inv_{Prepare}(c,d)
c ≤ d ∧ ¬(x[c]≤ r) ∧ ¬(x[d]> r)  (4.10)
(d + 1) − c = w
t = x[c]

we have to show

inv_{Prepare}(c + 1)(d − 1) [x ↦ x[d ↦ t]][x ↦ x[c ↦ x[d]]]  (Invariant)
((d− 1) + 1) − (c + 1) < w  (Termination)

Invariant Having represented arrays as functions, we need to remove redundant updates by appealing to the Lemmata 4.2 and 4.5.

Termination The premiss (4.10) is equivalent to c < d.

4.4 Conclusions

Much of the discussion in the above proofs has been focussed on choosing appropriate verification rules at strategic points and explaining the resulting proof obligations. Our main motivation has been to clarify the effect of the new set of rules by way of an example.

The only challenging aspect is to devise suitable invariants for loops. Furthermore, one needs to have a succinct specification of the recursive procedure. In an early proof attempt, we had forgotten to specify that array elements outside the array bounds remain invariant.

Given such invariants and specifications of recursive procedures, it is well-known that the complete verification can be automated by a VCG. This produces the same set of purely logical obligations arising from side-conditions of the consequence rule which we had to deal with after manually applying the axioms and rules of Hoare Logic.

Our proof has been unnecessarily complicated, because we have no support for parameter passing at the level of Hoare Logic. We had to introduce further global and local program variables. In particular, it caused more proof obligations involving values of the additional program variables.

Lego’s implementation of existential variables (such as ?i) has been very helpful when applying verification rules. It allowed us to apply them without having to specify the assertions. Later in the correctness proof, Lego synthesised these automatically.

Unfortunately, it turned out to be time consuming to prove the arising (purely logical) verification conditions in Lego. Due to our choice of a shallow embedding of assertions, the proof tool has no syntactic guidance to reduce the resulting excessively large proof obligations^{3}.

To check the correctness of the Quicksort algorithm, Lego had to run for more than 37 hours requiring more than 80MB on a SUN SPARC station 20 with sufficient physical memory to avoid swapping^{4}. To speed up the process, one could either employ a deep embedding of assertions, or directly guide the system to simplify proof obligations.

3See pages 222–234.

4In comparison, on the same architecture, the completeness proof for Hoare Logic dealing with recursive procedures and local variables could be dealt with in less than 15 minutes requiring less than 25MB. In both cases, we started Lego in the empty environment.

Conclusions and Future Work

If a verification calculus is unsound, one may derive properties of a program which do not hold. If it is incomplete, one may not be able to derive a desired property of a program despite it being satisfied. It is well known that establishing soundness and completeness for verification calculi is a challenging task. Many incorrect results based on doing proofs by hand have been published in the past.

We have taken advantage of the Lego system to interactively derive machinechecked soundness and completeness proofs for Hoare Logic and the operation decomposition rules of the Vienna Development Method (VDM). We have dealt with parameterless recursive procedures and local variables in the context of total correctness. Previously, machine-checked completeness results had only been established for simple imperative programs without recursive procedures in a scenario of partial correctness.

With computer-aided proof systems, one attains a high-level of confidence concerning the correctness of machine-checked proofs e.g., in Lego, less than 200 lines of SML code are responsible for checking that a given derivation is correct. Today’s proof tools are sufficiently advanced to be of significant help in investigating metatheory of verification calculi. In our opinion, soundness and completeness are difficult concepts which have to be tackled by appealing to intricate induction principles. A computer-aided proof system is very good at managing such tasks.

5.1 Syntax of Assertions

To prove completeness, one needs to be able to construct assertions which express semantic properties of the programming language. One usually simply assumes that the assertion language is sufficiently expressive. Both soundness and completeness proofs can be simplified if one does not worry about the actual syntactic representation of assertions.

A disadvantage of this approach is that examples are more difficult to read. Moreover, verifying the Quicksort algorithm, we found that the resulting proof obligations arising from the side-condition of the rule of consequence become too large for the Lego system to efficiently process. Without syntactic structure, the proof tool has little guidance on how to reduce such proof obligations.

5.2 The Rôle of Auxiliary Variables in Hoare Logic

The required formal detail in a completely machine-checked proof encourages one to more critically analyse the theory under investigation. Often, simplifications are vital for a machine-checked development to be feasible. As our main contribution, we have illuminated the rôle of auxiliary variables in Hoare Logic. They are required to relate the value of program variables in different states. We felt that, in previous Hoare Logic calculi, some of the encountered difficulties arose because the rôle of auxiliary variables had not been adequately reflected in the rules.

We have followed a proposal by Apt & Meertens (1980) to interpret assertions as relations on the state space and a domain of auxiliary variables. Moreover, we have proposed a new consequence rule to adjust auxiliary variables while strengthening preconditions and weakening postconditions. This rule is stronger than all previously suggested structural rules, including Hoare’s (1969) rule of consequence and rules of adaptation. As a direct consequence

• we were able to show that, contrary to common belief, VDM is more restrictive than Hoare Logic in that every derivation in VDM can be naturally embedded in Hoare Logic.

• We have clarified how to uniformly establish completeness as a corollary of Gorelick’s (1975) Most General Formula (MGF) theorem which focusses on deriving a specific correctness formula. One may appeal to our new consequence rule to derive completeness in a single step from the MGF.

• No further structural rules are required to cater for recursive procedures.

The new approach has already been taught by Hofmann (1997) to undergraduate students as part of a course on semantics and verification.

5.3 Future Work

Following Apt (1981) and America & de Boer (1990), we have restricted our attention to a single procedure declaration without parameters. We have however proposed a new rule to handle mutually recursive procedures which may conceivably be easier to apply than previous suggestions. Furthermore, we have sketched extensions to procedures with call-by-value parameters.

We were able to work with a simple notion of states which maps program variables to values. To extend this work to call-by-name parameters, and thus deal with aliasing, it seems advisable to consider a more elaborate mechanism for representing the state space such as an address mechanism. It would be interesting and rewarding to employ a computer-aided proof tool to investigate soundness and completeness for such verification calculi.

Appendix A

Some Remarks on Working with the Lego System

The Lego system was developed at the University of Edinburgh by Pollack (1994) with contributions by Claire Jones and McBride (1998). We recommend Underwood’s (1997) lecture notes as an introduction to the system. Lego implements Luo’s (1994) Unifying Theory of dependent Types (UTT) which has been established as being consistent by Goguen (1994). UTT combines an extension of the Calculus of Constructions (Luo 1990) with (intensional) Martin-Löf type theory (Coquand, Nordström, Smith & von Sydow 1994). UTT has a well-established theory which consists of less than 100 rules. The algorithm which checks proof terms is relatively small. It has been programmed in SML in less than 200 lines. Thus confidence in the system is high (Pollack 1998).

In this chapter, we describe in more detail some of the peculiarities of working in type theory, with particular emphasis on the Lego implementation. We commence with introducing the notation for term structure to clarify the outline of the scripts in the following chapter. In Sect. A.2, we introduce the system’s facility for defining recursive functions. In Sect A.3, we present the encoded well-founded induction principle. Next, we explain in detail how we have encoded an equality such that one may replace terms in any type. Thus we may implement substitution on dependent functions.

A.1 Notation

Functional abstraction λx: T· U is represented by [x:T]U. Its type ∏x: T· U is rendered as {x:T}U. If one replaces the colon with a vertical bar, Lego attempts to synthesise the value of such implicit variables in an application (Pollack 1992).

Example A.1 (Implicit Arguments) The seq constructor for sequential composition of imperative programs inhabits the type

{sort|SORT(VAR)}{S1,S2:prog(sort)}prog(sort)

It expects three elements, a sort and two imperative programs where program variables adhere to this sort. It returns a program with the same sort. However, the implicit argument sort of the compound program can be synthesised from the other arguments. In particular, the term seq(S1)(S2), where S1 and S2 are programs of type prog(sort), is well-formed.

A.2 Recursion

Recursion is restricted to higher-order primitive recursion. Every inductively defined type is equipped with an elimination principle which enforces that the new type is closed by its constructors.

Example A.2 Polymorphic Lists are defined by

     Inductive     [list:Type->Type]
     Constructors [nil :{T:Type}list(T)]
                   [cons:{T|Type}T->list(T)->list(T)]

From such a definition, Lego generates the elimination principle

\frac{∏T· C(nil(T))  ∏T· ∏x· ∏xs· C(xs)→ C(cons(x)(xs))}{∏T· ∏z· C(z)} (list_{elim})

and the two computation rules

list_{elim}(C)(f)(g)(nil(T))⊳_{δ}f(T)
list_{elim}(C)(f)(g)(cons(x)(xs))⊳_{δ}g(x)(xs)(list_{elim}(C)(f)(g)(xs))

where C is a dependent function inhabiting ∏T | Type· list(T)→ Type. In particular, the constant list_{elim} expects as arguments a function C and descriptions of the behaviour in the base and step cases. It returns a function mapping an arbitrary list z to C(z).

Consider the definition of the append function in the programming language SML (Paulson 1990)

fun append (nil)(y) = y
|append (x::xs)(y) = x::(append (xs)(y))

where :: denotes the uncurried version of cons. It follows a primitive-recursive pattern and one may immediately reflect its definition in Lego by applying list_{elim} to the three arguments 1. λT· λ : list(T)· list(T)→ list(T)

2. λT· λy· y

3. λT· λx· λ : list(T)· λappend xs: list(T)→ list(T) · λy· cons(x)(append xs(y))

A.3 Well-Founded Relations

A relation R ⊆ T × T is well-founded if, and only if, there are no infinite decreasing sequences x_{0} R^{−1} x_{1} R^{−1} ···e.g., < ⊆ nat × nat is well-founded, whereas < ⊆ int × int is not. Well-founded relations play an important rôle in specifying a termination measure required in proofs of termination (Dershowitz & Manna 1979). If a verification rule refers to a well-founded relation R to cater for termination, its soundness proof has to appeal to well-founded induction:

\frac{∀n· (∀x· x R n⇒ P(x)) ⇒ P(n)}{∀z· P(z)}  (WF)

One may consider the induction scheme as a constructive version of the definition of well-foundedness.

A.4 Equality

Representing equality in type theory is a delicate subject. In this section, we show in detail how one can set up a powerful notion of extensional equality which supports dependent substitutions. Thus, we may implement updating of dependent functions which is required to represent a model of the state space.

Lego’s tactics can be configured to work for an arbitrary equality, provided it is reflexive and substitutive. It uses these ingredients to derive, amongst other properties, a proof that the equality is also symmetric. We chose Hofmann’s (1995) encoding of an extensional equality. It is based on Leibniz equality which equates two terms if they fulfil the same property:

p. 235 Definition A.3 (Leibniz Equality) x = y =^{def} ∀P : T → Prop · P(x)⇒ P(y)

Leibniz equality is obviously reflexive and substitutive. Let Eq refl(x) be an abbreviation for the proof of reflexivity λP· λp: P(x)· p.

Substitutivity is somewhat limited. One may only replace two equal terms within a proposition. We therefore axiomatise^{1} p. 235 Axiom 1 (Substitutivity)

1Notice that we cannot employ Eq subst to define equality, because Eq subst(x,y) is a type and not a proposition.

∏x,y: T· x = y → ∏C : T → Type · C(x)→ C(y)  (Eq subst)
∀C : T → Type · ∀a: T· ∀b: C(a)· Eq subst(a,a)(Eq refl(a))(C)(b)= b

(Eq_subst_comp)

The axiom Eq subst allows one to substitute within a type. The second axiom permits removing redundant substitutions such as substituting a for a, provided that the equality a = a has been established by the canonical proof of reflexivity. In practice, we also want to remove redundant substitutions if the equality has been established by other means, in particular, by appeal to an assumption. In such a scenario, it would help to assume that there is only one canonical proof of two terms being equal. We go further and adopt the stronger proof irrelevance principle stating that regarding any proposition, proofs are unique.

p. 236 Axiom 2 (Proof Irrelevance) ∀P : Prop· ∀x,y: P· x = y

With proof irrelevance, we can strengthen substitutivity to replace terms in a dependent context:

p. 237 Lemma A.4 (Dependent Substitution) Let A be any type, B a type depending on elements of the type A, and C inhabiting ∏a: A·B(a) → Type. Then, for any two elements a and â in A, given a proof p that they are propositionally equal, for any dependent term b: B(a), we may replace C(a)(b) by C(â)(Eq subst(a,â)(p)(B)(b)).

Proof Let p be a proof of a = â. We show

∏b: B(a)· C(â)(Eq subst(a,â)(p)(B)(b))→ C(a)(b)

by applying Eq subst to the proof p and

λa· ∏p: a = â · ∏b: B(a)· C(â)(Eq subst(a,â)(p)(B)(b))→ C(a)(b) .

This leaves us with having to establish

∏b: B(â)· C(â)(Eq subst(â,â)(p′)(B)(b))→ C(â)(b)

where p′is a witness for â= â. Due to proof irrelevance, we may replace p′with the canonical proof Eq refl(â). Appealing to Axiom 1, the substitution collapses. □

Finally, we assume that two (dependent) functions are equal if their input/output behaviour is identical. This is required in the soundness proof for the verification rule for blocks on page 102 to replace a function f by f [x ↦ t][x ↦ f(x)].

p. 236 Axiom 3 (Extensionality) Let T be a type and C : T → Type be an indexed type. Consider any dependent functions f,g: ∏x: T·C(x). If, for all inputs x: T, f(x) = g(x) holds, one may conclude f = g.

In his thesis, Hofmann (1995) has shown that it is consistent to assume all of these axioms.

A.5 Updating Dependent Functions

We use dependent functions to model the state space. An assignment to a variable then corresponds to updating the function. Unlike non-dependent functions, updating requires some care and we have to resort to the first two axioms to achieve the goal.

In a constructive framework, we can only effectively update a (dependent) function if equality on its domain is decidable. For the purpose of this section, let T be such a domain. We characterise that equality is decidability by relying on a concrete implementation eq: T × T → bool such that the function eq reflects equality i.e.,

∀x,y: T· x = y ⇔ eq(x,y)= true .  (DecSetoid_{char})

Assume we have a suitable candidate for eq and let f : ∏y: T·C(y) be the function we want to update at position x : T with v : C(x) i.e. f [x ↦ v]. Let y be an arbitrary element of the domain. Our task is to extract the appropriate element with type C(y). We proceed by case analysis on whether x coincides with y. More precisely, we derive

∀c: bool· (eq(x,y)= c) → C(y)

by induction on c with the help of the elimination principle

C : bool → Type C(false) C(true )
∀c: bool· C(c)

                                                         (bool_{elim})

Consider the case that c = false. We ignore the additional input eq(x,y)= false and retrieve the original value f(y).

The case c = true is more subtle, because the naïve solution v does not inhabit C(y). But, in the presence of a proof of eq(x,y)= true , eq’s characterisation lemma (DecSetoid_{char}) yields a proof that x = y and we can use the axiom of substitutivity to coerce the type C(y) to C(x).

Thus, the recursion corresponds to the following elimination on booleans^{2}:

bool_{elim} (\overbrace{λc: bool· (eq(x,y) = c)}^{need to be cut}→ C(y))  (A.1)

( λH : eq(x,y) = true · \underbrace{Eq subst(x,y)(snd(DecSetoid_{char}(x,y))(H))}_{coercing v: C(x)to C(y)}(v))

(A.2)

(λ : eq(x,y) = false · f(y))  (A.3)
(eq(x,y))  (A.4)
(Eq refl(eq(x,y)))  (A.5)

Expressions (A.2) and (A.3) correspond to the cases eq(x,y) and ¬eq(x,y), respectively. The terms (A.4) and (A.5) serve as a witness in order to cut the assumptions in term (A.1)

We can now establish the correctness of this implementation and show that both f [x ↦ v](x) = v and f [x ↦ v](y) = f(y) hold where x ≠ y. Both proofs are easy to conduct by appealing to dependent substitution, replacing eq(x,x) by true , and eq(x,y) by false. We focus on the first property to clarify why ordinary substitution fails. The term f [x ↦ v](x) is obtained by replacing y with x yielding

bool_{elim} (\overbrace{λc: bool· (eq(x,x) = c)}^{need to be cut}→ C(x))  (A.6)

( λH : eq(x,x) = true · \underbrace{Eq subst(x,y)(snd(DecSetoid_{char}(x,x))(H))}_{coercing v: C(x)to C(x)}(v))

(A.7)

(λ : eq(x,x) = false · f(x))  (A.8)
(eq(x,x))  (A.9)
(Eq refl(eq(x,x)))  (A.10)

The substitutivity axioms are not strong enough to replace true for eq(x,x) because, in case (A.7), the term Eq subst(x,y)(snd(DecSetoid_{char}(x,x))(H)) requires an argument inhabiting the type (eq(x,x))= true . Replacing eq(x,x) by true would lead to an illegal term. As a consequence, we cannot replace eq(x,x) in the term (A.6) either, for otherwise, the type of the argument (A.7) would violate the typing discipline of the bool_{elim} rule. Furthermore, if we were to replace eq(x,x) with true in (A.9), the last argument must be a proof that eq(x,x) = true , which cannot be fulfilled by a proof of reflexivity. We need dependent substitution to perform the desired substitution for the term (A.9) modifying the type of the term (A.6)–(A.8) accordingly.

2Notice that in extensional type theory (Martin-Löf 1984), we may omit the coercion function Eq subst(x,y)(snd(DecSetoid_{char}(x,y))(H)) because the equality reflection rule forces the identity of propositional and definitional equality. As a drawback, assuming this rule renders type-checking undecidable. We work in an intensional type theory. Type checking is decidable, but we cannot avoid equality bookkeeping in the constructed terms. Unfortunately, the current implementation of Lego offers no help with this.

Appendix B

Outline of LEGO Scripts

This appendix contains outlines of the actual Lego scripts in which most of the proofs have been omitted. The full scripts and further modules from Lego’s library are available on-line (Lego 1998).

B.1 Simple Imperative Programs

Module state Import DepUpdate;
[VAR|DecSetoid][sort|SORT VAR];
(*  Corresponds to Definition 2.2 on page 12. *)
[STATE = FS|VAR sort];
[lookupSTATE [s:STATE] = s];
(*  Corresponds to Definition 2.5 on page 14. *)
[expression [T:Type(0)] = STATE -> T]
[eval [sigma:STATE][T|Type(0)][t:expression T] = t sigma];
[STATE_update = FS_update];
[STATE_lookup_update = FS_update_lemma];

Module syntax Import lib_bool state;
(*  Corresponds to Definition 2.6 on page 15. *)
Inductive [prog:Type]
   Theorems
Constructors
  [skip:prog]
  [assign:{x:VAR.DecSetoid_carrier}{t:expression (sort x)}prog]
  [seq:{S1,S2:prog}prog]
  [ifthenelse:{b:expression bool}{S1,S2:prog}prog]
  [while:{b:expression bool}{body:prog}prog];
Goal ifloop : {T|Type}{S:prog}{then:T}{else:T}T;

...

Goal ifassign : {T|Type}{S:prog}{then:T}{else:T}T;

...

Goal prog_domain : {S:prog}Type;

...

[P : {S:prog}Prop];

Goal prog_predicate : {S:prog}Prop;

...

Discharge P;
Goal assign_update :
{T|Type}
{S:prog}{x:VAR.DecSetoid_carrier}{t:expression (sort x)}
{f:{x:VAR.DecSetoid_carrier}{t:expression (sort x)}T}
T;

...

Goal prog_assign_injective' :
{ix0,iy0|VAR.DecSetoid_carrier}
{ix1|expression (sort ix0)}{iy1|expression (sort iy0)}
(Eq  (assign iy0 iy1) (assign ix0 ix1))->
{P:{ix0:VAR.DecSetoid_carrier}{ix1:expression (sort ix0)}Prop}
(P ix0 ix1) -> P iy0 iy1;

...

Goal loop_extract_b : {default:bool.expression}{S:prog}bool.expression;

...

Goal loop_extract_body : {S:prog}prog;

...

Module operate Import syntax;
(*  Corresponds to Definition 2.8 on page 16. *)
Inductive [prog_operate : {sigma:STATE}{S:prog}{tau:STATE}Prop] Relation
Inversion NoReductions
Constructors
[skip_operate
: {sigma:STATE}prog_operate sigma skip sigma]
[assign_operate
: {x:VAR.DecSetoid_carrier}{t:x.sort.expression}{sigma:STATE}
prog_operate sigma (assign x t) (STATE_update x (t sigma) sigma)]
[seq_operate
: {S1,S2:prog}{sigma,tau,eta:STATE}
(prog_operate sigma S1 tau) -> (prog_operate tau S2 eta)->
(*------------------------------------------------------*)
prog_operate sigma (seq S1 S2) eta]
[ifthen_operate
: {b:bool.expression}{S1,S2:prog}{sigma,tau:STATE}
(is_true (b (lookupSTATE sigma))) ->
(prog_operate sigma S1 tau) ->
prog_operate sigma (ifthenelse b S1 S2) tau]
[ifelse_operate
: {b:bool.expression}{S1,S2:prog}{sigma,tau:STATE}
(is_false (b (lookupSTATE sigma))) ->
(prog_operate sigma S2 tau) ->
prog_operate sigma (ifthenelse b S1 S2) tau]
[while_exit_operate
: {b:bool.expression}{body:prog}{sigma:STATE}
(is_false (b (lookupSTATE sigma))) ->
prog_operate sigma (while b body) sigma]
[while_body_operate
: {b:bool.expression}{body:prog}{sigma,tau,eta:STATE}
(is_true (b (lookupSTATE sigma))) ->
(prog_operate sigma body eta) ->
(prog_operate eta (while b body) tau) ->
prog_operate sigma (while b body) tau];

Module operate_thms Import operate lib_bool_thms;
Goal oper_Inversion : {S:prog}{sigma,tau:STATE}Prop;
Induction S;
  Intros; Refine Eq sigma tau; (* skip *)
  (* assign *)
  Intros; Refine Eq (STATE_update x (t sigma) sigma) tau;
  (* seq *)
  Intros s1 s2 ____;
  Refine Ex [rho:STATE]and (prog_operate sigma s1 rho)
                               (prog_operate rho s2 tau);
  (* conditional *)
  Intros b s1 s2 ____;
     Refine prog_operate sigma (if (b sigma) s1 s2) tau;
  (* loop *)
  Intros b s ___;
     Refine if (b sigma);
       Refine Ex [rho:STATE]and (prog_operate sigma s rho)
                          (prog_operate rho (while b s) tau);
Refine Eq sigma tau;
Save oper_Inversion;
Goal oper_inversion : {sigma|STATE}{S|prog}{tau|STATE}
{D:prog_operate sigma S tau}(oper_Inversion S sigma tau);

...

Goal oper_inversion_ifthenelse :
  {b|expression bool}{S1,S2|prog}{sigma,tau|STATE}{c|bool}
  {eq:Eq (b sigma) c}(prog_operate sigma (ifthenelse b S1 S2) tau) ->
  prog_operate sigma (if c S1 S2) tau;

...

Goal oper_inversion_while :
  {c|bool}{b|expression bool}{S|prog}{sigma,tau|STATE}
  (Eq (b sigma) c)->(prog_operate sigma (while b S) tau) ->
  if c
      (Ex [rho:STATE]and (prog_operate sigma S rho)
                           (prog_operate rho (while b S) tau))
      (Eq sigma tau);

...

(*  Corresponds to Lemma 2.9 on page 16. *)
Goal oper_determ :
  {sigma|STATE}{S|prog}{tau|STATE}
  {Prem_tau|prog_operate sigma S tau}{C:STATE->Prop}{eta:STATE}
  {Prem_eta:prog_operate sigma S eta}
  {C_eta:C eta}C tau;

...

Module DECLexp Import lib_int_basics decsetoid;
Inductive [VARexp:Type]
Constructors [x,y,r:VARexp];
[sortexp = [_:VARexp]zed];
Goal VARexp_eq : {a,b:VARexp}bool;

...

Goal VARexp_eq_refl: {a:VARexp}is_true (VARexp_eq a a);

...

Freeze Eq;
Goal VARexp_char : Eqchar VARexp_eq;

...

Goal VARexp_setoid : DecSetoid;

...

Module exp Import DECLexp lib_bool_funs lib_int_bool operate
                           lib_nat_Le;
(** belongs in lib_nat_Le **)
Goal Le_zero_Eq_zero : {a|nat}(Le a zero) -> Eq a zero;

...

Goal Le_resp_pred_left : {a,b|nat}(Le (suc a) b) -> Le a (pred b);

...

(**************************)
(** belongs to lib_int_funs **)
Goal Eq_zed_suc : {a,b:nat}Eq_zed ((suc a,suc b)) (a,b);

...

Goal Le_zed_resp_suc_right :
  {a|zed}{b,c|nat}(Le_zed a (b,c)) -> Le_zed a (suc b,suc c);

...

Goal Le_zed_resp_pred_right :
  {a|zed}{b,c|nat}(Le_zed a (suc b,suc c)) -> Le_zed a (b,c);

...

Goal abs_zed : zed -> nat;
intros input;
Refine nat_elim [m:nat]{n:nat}nat; Refine -0 input.2; Refine -0 input.1;
  Refine Id; (* |(0,n)| = n *)
  intros m abs_zed_m;
  Induction n;
     Refine suc m; (* |(suc m,0)| = suc m *)
     intros n _;
     Refine abs_zed_m n; (* |(suc m,suc n)| = |(m,n)| *)
Save abs_zed;
(Normal abs_zed (four,two));
(Normal abs_zed (two,four));
Goal abs_zed_zero : {a|zed}(Eq (abs_zed a) zero) -> Eq_zed a zero_zed;

...

Freeze Eq;
Goal abs_zed_suc : {a|zed}(is_suc (abs_zed a)) -> not (Eq_zed a zero_zed);

...

Goal exp_zed : {a:zed}{b:nat}zed;
Induction b;
  intros; Refine one_zed;
  intros b exp_zed_b a; Refine times_zed a (exp_zed_b a);
Save exp_zed;
Configure Infix * left 6; [op* = times_zed];
Configure Infix - left 5; [op- = minus_zed];
Goal Le_zed_pos_pred :
  {a|zed}(Le_zed zero_zed a) -> (not (Eq_zed a zero_zed)) ->
  Le_zed zero_zed (a - one_zed);

...

Goal abs_zed_pos_can : {a:nat}Eq (abs_zed (a,zero)) a;

...

Goal Le_zed_Lt_zed_pred : {a,b|zed}(Le_zed a (b-one_zed)) -> Lt_zed a b;

...

Goal abs_pos_suc_pred :
  {a|zed}(Lt_zed zero_zed a) -> Eq (suc (abs_zed (a-one_zed))) (abs_zed a);

...

Goal exp_zed_pred :
  {a:zed}{b:nat}(is_suc b) -> Eq (exp_zed a b) (a * exp_zed a (pred b));

...

Goal Le_is_suc : {a,b:nat}(Le (suc a) b) -> is_suc (minus b a);

...

Goal Eq_Eq_zed : {a,b|zed}(Eq a b)->Eq_zed a b;

...

Cut [VAR=VARexp_setoid];
Cut [sort=sortexp];
Goal prog_loop_exp : prog;
Refine while [sigma:STATE]inv (int_eq_bool (sigma y) zero_zed);
     Refine seq;
       Refine assign r [sigma:STATE](sigma r) * (sigma x);
       Refine assign y [sigma:STATE]minus_zed (sigma y) one_zed;
Save prog_loop_exp;
Goal prog_exp : prog;
Refine seq;
  Refine assign r [sigma:STATE]one_zed;
  Refine prog_loop_exp;
Save prog_exp;
[sofar [sigma,tau:STATE] =
  [R=sigma r][r=tau r]
  [X=sigma x][x=tau x]
  [Y=sigma y][y=tau y]
  Eq_zed r (R * (exp_zed X (abs_zed Y)))];
Goal induction_exp :
  {n:nat}{sigma:STATE}
  {pre:(Le_zed zero_zed (sigma y)) ∧ (Eq (abs_zed (sigma y)) n)}
  Ex [tau:STATE] (prog_operate sigma prog_loop_exp tau) ∧
                     ((Eq_zed (tau y) zero_zed) ∧ sofar sigma tau);

...

(*  Corresponds to Lemma 2.10 on page 18. *)
Goal correct_exp :
  {sigma:STATE}{pre:(Le_zed zero_zed (sigma(y)))}
  Ex [tau:STATE](prog_operate sigma prog_exp tau) ∧
                  (Eq_zed (tau(r)) (exp_zed (sigma x) (abs_zed ((sigma y)))));

...

Unfreeze Eq;

Module VDMsemantics Import lib_rel operate;
(*  Corresponds to Definition 2.12 on page 22. *)
[VDMSem [p:STATE.Pred][S:prog][r:Rel STATE STATE]
  = {sigma:STATE}
     (p sigma) -> Ex [tau:STATE]and (prog_operate sigma S tau) (r tau sigma)];

Module VDM Import lib_rel syntax;
[VDM_Pred_to_Rel [P:STATE.Pred] = [tau,_:STATE]P tau : Rel STATE STATE];

[reflclose [R:Rel STATE STATE] =
[tau,sigma:STATE](R tau sigma) ∨ (Eq tau sigma)];
(*  Corresponds to Definition 2.13 on page 23. *)
Inductive
[VDMder : {P:Pred STATE}{S:prog}{Q:Rel STATE STATE}Prop]
Relation NoReductions
Constructors
[VDMSkip :
VDMder  ([_:STATE]trueProp) skip  ([tau,sigma:STATE]Eq tau sigma)]
[VDMAssign :
{x:VAR.DecSetoid_carrier}{t:x.sort.expression}
VDMder ([_:STATE]trueProp)
        (assign x t)
([tau,sigma:STATE]Eq tau (STATE_update x (t(sigma)) sigma))]
[VDMSeq :
{p1,p2:STATE.Pred}{r1,r2:Rel STATE STATE}{S1,S2:prog}
(VDMder p1 S1 [tau,sigma:STATE]and (p2 tau) (r1 tau sigma)) ->
(VDMder p2 S2 r2) ->
VDMder p1 (seq S1 S2) (composeRel r2 r1)]
[VDMIfthenelse :
{pre:STATE.Pred}{post:Rel STATE STATE}
{b:bool.expression}{TH,EL:prog}
(VDMder ([sigma:STATE](pre sigma) ∧ (is_true (b sigma.lookupSTATE)))
         TH post) ->
(VDMder ([sigma:STATE](pre sigma) ∧ (is_false (b sigma.lookupSTATE)))
         EL post) ->
VDMder pre (ifthenelse b TH EL) post]
[VDMWhile :
{P:STATE.Pred}
{sofar:Rel STATE STATE}
 {sofar_trans: trans sofar}
 {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
{b:bool.expression}{body:prog}
{Prem:VDMder
              ([sigma:STATE](P sigma) ∧ (is_true (b sigma.lookupSTATE)))
              body
     ([tau,sigma:STATE](P tau) ∧ (sofar tau sigma))}
VDMder P (while b body)
       ([tau,sigma:STATE](P tau) ∧ (is_false (b tau.lookupSTATE)) ∧
                          (reflclose sofar tau sigma))]
[VDMConsequence :
{p,p1:Pred STATE}{q,q1:Rel STATE STATE}
{S:prog}
{ConsPre:{sigma|STATE}(p sigma) -> p1 sigma}
{ConsPost:{sigma|STATE}{tau:STATE}(p sigma) ->
           (q1 tau sigma) -> q tau sigma}
{ConsPrem:VDMder p1 S q1}
VDMder p S q];

Module VDMtheorems Import VDM;
Goal VDMPre :
{pre|STATE.Pred}{S|prog}{post|Rel STATE STATE}
{Prem:VDMder pre S post}

  VDMder pre S [tau,sigma:STATE]and (pre sigma) (post tau sigma);

...

Goal VDMConsequenceLeft :
              {pre_s,pre : STATE.Pred}{post:Rel STATE STATE}{S:prog}
              {strengthen : {sigma:STATE}(pre_s sigma) -> pre sigma}
              {prem : VDMder pre S post}
     VDMder pre_s S post;

...

Goal VDMConsequenceRight:
  {pre : STATE.Pred}{post,post_w:Rel STATE STATE}{S:prog}
  {weaken : {tau,sigma:STATE}(post tau sigma) -> post_w tau sigma}
  {prem : VDMder pre S post}
  VDMder pre S post_w;

...

Goal VDMSkipBU :
  {p:Rel STATE STATE}{p':STATE.Pred}
  {p'p:{sigma:STATE}(p' sigma) -> p sigma sigma}
  VDMder p' skip p;

...

Goal VDMAssignBU :
  {x:VAR.DecSetoid_carrier}{t:x.sort.expression}
  {p:Rel STATE STATE}{p':STATE.Pred}
  {p'p:{sigma:STATE}
        (p' sigma) ->  p (STATE_update x (eval sigma t) sigma) sigma}
  VDMder p' (assign x t) p;

...

Goal VDMSeqBU :
  {p1,p2:STATE.Pred}{r,r1,r2:Rel STATE STATE}{S1,S2:prog}
  {weakening: SubRel (composeRel r2 r1) r}
  (VDMder p1 S1 [tau,sigma:STATE]and (p2 tau) (r1 tau sigma)) ->
  (VDMder p2 S2 r2) ->
  VDMder p1 (seq S1 S2) r;

...

Goal VDMLoopBU :
   {P,I:STATE.Pred}
   {Q,sofar:Rel STATE STATE}
      {sofar_trans: trans sofar}
      {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel I))}
   {b:bool.expression}{body:prog}
   {ScPre:SubPred P I}
   {ScPost:{tau,sigma:STATE}(I sigma) -> (I tau) ->
             (is_false (b tau.lookupSTATE)) ->
   (reflclose sofar tau sigma) -> Q tau sigma}
   {Prem:VDMder
                  ([sigma:STATE](I sigma) ∧ (is_true (b sigma.lookupSTATE)))
                  body
         ([tau,sigma:STATE](I tau) ∧ (sofar tau sigma))}
   VDMder P (while b body) Q;

...

Module VDMexp Import lib_nat_Le lib_bool_funs lib_int_bool DECLexp VDM;

(** belongs in lib_nat_Le **)
Goal Le_zero_Eq_zero : {a|nat}(Le a zero) -> Eq a zero;

...

Goal Le_resp_pred_left : {a,b|nat}(Le (suc a) b) -> Le a (pred b);

...

Goal Lt_zed_Le_pred : {a,b|zed}(Lt_zed a b) -> Le_zed a (minus_zed b one_zed);

...

(**************************)
Goal Lt_imp_Le_zed : {a,b|zed}(Lt_zed a b) -> Le_zed a b;

...

[pred_zed [a:zed] = minus_zed a one_zed];
Goal pred_zed_resp : {a,b:zed}(Eq_zed a b) -> Eq_zed (pred_zed a) (pred_zed b);

...

Goal Lt_zed_pred : {a:zed}Lt_zed (pred_zed a) a;

...

(** belongs to lib_int_funs **)
Goal Eq_zed_suc : {a,b:nat}Eq_zed ((suc a,suc b)) (a,b);

...

Goal Le_zed_resp_suc_right :
  {a|zed}{b,c|nat}(Le_zed a (b,c)) -> Le_zed a (suc b,suc c);

...

Goal Le_zed_resp_pred_right :
  {a|zed}{b,c|nat}(Le_zed a (suc b,suc c)) -> Le_zed a (b,c);

...

Goal Le_zed_resp_pred_left :
  {a|zed}{b,c|nat}(Le_zed (suc b,suc c) a) -> Le_zed (b,c) a;

...

Goal Lt_zed_resp_pred_right :
  {a|zed}{b,c|nat}(Lt_zed a (suc b,suc c)) -> Lt_zed a (b,c);

...

Goal abs_zed : zed -> nat;

...

Goal abs_zed_pos_can : {a:nat}Eq (abs_zed (a,zero)) a;

...

Goal abs_zed_resp : {a,b:zed}(Eq_zed a b) -> Eq (abs_zed a) (abs_zed b);

...

Goal abs_zed_zero : {a|zed}iff (Eq (abs_zed a) zero) (Eq_zed a zero_zed);

...

Goal abs_zed_suc : {a:zed}iff  (is_suc (abs_zed a)) (not (Eq_zed a zero_zed));

...

Goal abs_zed_pred :
  {a|zed}(Lt_zed zero_zed a) ->
  Lt (abs_zed (minus_zed a one_zed)) (abs_zed a);

...

Goal Lt_resp_abs_zed_pos :
  {a,b:zed}(Le_zed zero_zed a) -> (Lt_zed a b) -> Lt (abs_zed a) (abs_zed b);

...

Goal exp_zed : {a:zed}{b:nat}zed;

...

Configure Infix * left 6; [op* = times_zed];
Configure Infix - left 5; [op- = minus_zed];
Goal Eq_Eq_zed : {a,b|zed}(Eq a b)->Eq_zed a b;

...

Goal exp_zed_times :

  {a:zed}{b,c:nat}Eq_zed (exp_zed a b * exp_zed a c) (exp_zed a (plus b c));

...

Goal Le_zed_pos_pred :
  {a|zed}(Le_zed zero_zed a) -> (not (Eq_zed a zero_zed)) ->
  Le_zed zero_zed (a - one_zed);

...

Goal Le_zed_Lt_zed_pred : {a,b|zed}(Le_zed a (b-one_zed)) -> Lt_zed a b;

...

Goal abs_pos_suc_pred :
  {a|zed}(Lt_zed zero_zed a) -> Eq (suc (abs_zed (a-one_zed))) (abs_zed a);

...

Goal abs_zed_minus :
  {a,b:zed}(Le_zed zero_zed a) -> (Le_zed a b) ->
             Eq (abs_zed (minus_zed b a)) (minus (abs_zed b) (abs_zed a));

...

Goal abs_zed_pred' :
  {a|zed}(Lt_zed zero_zed a) -> Eq (abs_zed (pred_zed a)) (pred (abs_zed a));

...

Goal exp_zed_pred :
  {a:zed}{b:nat}(is_suc b) -> Eq (exp_zed a b) (exp_zed a (pred b) * a);

...

Goal Le_is_suc : {a,b:nat}(Le (suc a) b) -> is_suc (minus b a);

...

Goal int_eq_bool_character :
  {a,b:zed}iff (is_true (int_eq_bool a b)) (Eq_zed a b);

...

Goal int_eq_bool_character' :
  {a,b:zed}iff (is_false (int_eq_bool a b)) (not (Eq_zed a b));

...

Cut [VAR=VARexp_setoid];
Cut [sort=sortexp];
Goal minus_zed_zero : {a:zed}Eq_zed (a-zero_zed) a;

...

Goal prog_loop_exp : prog;
Refine while [sigma:STATE]inv (int_eq_bool (sigma y) zero_zed);
     Refine seq;
       Refine assign r [sigma:STATE](sigma r) * (sigma x);
       Refine assign y [sigma:STATE]minus_zed (sigma y) one_zed;
Save prog_loop_exp;
Goal prog_exp : prog;
Refine seq;
  Refine assign r [sigma:STATE]one_zed;
  Refine prog_loop_exp;
Save prog_exp;
[pre = [sigma:STATE]Le_zed zero_zed (sigma(y))];
[invariant_term [sigma:STATE] =
  sigma(r) * exp_zed (sigma(x)) (abs_zed (sigma(y)))];
[sofar [tau,sigma:STATE] =
  (Eq_zed (invariant_term sigma) (invariant_term tau)) ∧
  (Lt_zed (tau(y)) (sigma(y)))];
(*  Corresponds to Lemma 2.14 on page 23. *)
Goal correct_exp :

VDMder pre
        prog_exp
([tau,sigma:STATE]
Eq_zed (tau(r)) (exp_zed (sigma x) (abs_zed ((sigma y)))));

...

Module VDMsound Import VDMsemantics VDM operate_thms;
(*  Corresponds to Theorem 2.15 on page 25. *)
Goal VDMsoundness :
{P|Pred STATE}{S|prog}{Q|Rel STATE STATE}
{der:VDMder P S Q}VDMSem P S Q;

...

Module termination Import operate_thms lib_rel;
(*  Corresponds to Definition 2.16 on page 27. *)
[Termin [S:prog] =
[sigma:STATE]Ex [tau:STATE]prog_operate sigma S tau : STATE.Pred];
Goal seq_termin1 :
{S1,S2|prog}{sigma|STATE}(Termin (seq S1 S2) sigma) -> Termin S1 sigma;

...

Goal seq_termin2 :
{S1,S2|prog}{sigma,tau|STATE}
(Termin (seq S1 S2) sigma) -> (prog_operate sigma S1 tau) ->
Termin S2 tau;

...

Goal ifthenelse_termin :
{b|expression bool}{S1,S2|prog}{sigma|STATE}{c|bool}
{eq:Eq (b sigma) c}
(Termin (ifthenelse b S1 S2) sigma) ->
if c (Termin S1 sigma) (Termin S2 sigma);

...

Goal while_termin :
{b|expression bool}{S|prog}{sigma,tau|STATE}
(Termin (while b S) sigma) ->
(is_true (b sigma)) ->
(prog_operate sigma S tau) ->
Termin (while b S) tau;

...

Goal while_termin_body :
{b|expression bool}{S|prog}{sigma|STATE}
(is_true (b sigma)) -> (Termin (while b S) sigma) -> Termin S sigma;

...

Module VDMcomplete Import VDMsemantics VDMtheorems termination WF;
(** pre(S,q) is equivalent to Termin(S) **)
[post [S:prog][p:STATE.Pred] =
[tau,sigma:STATE](p(sigma)) ∧ (prog_operate sigma S tau) : Rel STATE STATE];
(*  Corresponds to Definition 2.17 on page 27. *)
[VDM_Most_General [S:prog] =
VDMder (Termin S) S ([tau,sigma:STATE]prog_operate sigma S tau)];

Goal VDM_most_general_seq :
  {S1,S2:prog}{S1_ih:VDM_Most_General S1}{S2_ih:VDM_Most_General S2}
                (VDM_Most_General (seq S1 S2));

...

Goal VDM_most_general_conditional :
  {b:expression bool}{S1,S2:prog}
  {S1_ih:VDM_Most_General S1}{S2_ih:VDM_Most_General S2}
  VDM_Most_General (ifthenelse b S1 S2);

...

[b:expression bool][body:prog];
$Goal VDMWFsofar :
  WF (andRel (sofar b body) (VDM_Pred_to_Rel (Termin (while b body))));

...

Freeze Eq;
Goal VDM_most_general_loop :
  {ih:VDM_Most_General body}VDM_Most_General (while b body);

...

Discharge b;
(*  Corresponds to Theorem 2.18 on page 27. *)
Goal VDM_most_general : {S:prog}VDM_Most_General S;

...

(*  Corresponds to Corollary 2.19 on page 28. *)
Goal VDM_complete :
  {S|prog}{p|STATE.Pred}{q|Rel STATE STATE}
  {semant:VDMSem p S q}VDMder p S q;

...

Module semantics Import syntax assertion;
(*  Corresponds to Definition 2.20 on page 30. *)
[semantics [p:Assertion][S:prog][q:Assertion]
  = {sigma|STATE}(p sigma) ->
     Ex [tau:STATE] (prog_operate sigma S tau) ∧ (q tau)];

Module hoare Import syntax assertion lib_nat_Lt lib_rel;
(*  Corresponds to Definition 2.21 on page 31. *)
Inductive [HD : {p:Assertion}{S:prog}{q:Assertion}Prop]
Relation Inversion
Constructors
(* skip *)
[Inv : {p:Assertion}HD p skip p]
(* := *)
[Assign : {x:VAR.DecSetoid_carrier}{t:x.sort.expression}{p:Assertion}
           HD ([sigma:STATE][sigma' = STATE_update x (t sigma) sigma]
        p sigma') (assign x t) p]
(* ; *)
[Seq : {p,q,r:Assertion}{S1,S2:prog}
        (HD p S1 r) -> (HD r S2 q) -> HD p (seq S1 S2) q]
(* ifthenelse *)
[Ifthenelse : {p,q:Assertion}{b:STATE->bool}{S1,S2:prog}
                (HD ([sigma:STATE](p sigma) ∧ (is_true (b sigma))) S1 q) ->
                (HD ([sigma:STATE](p sigma) ∧ (is_false (b sigma))) S2 q) ->

                HD p (ifthenelse b S1 S2) q]
(* while *)
[While :
   {T|Type}{Lt':Rel T T}{is_wf : WF Lt'}
   {u:expression(T)} (* convergence function *)
   {p:Assertion}{b:STATE->bool}{body:prog}
   {While_step:{n:T}
      HD ([sigma:STATE](p(sigma)) ∧ (is_true(b sigma)) ∧ (Eq (u sigma) n))
         body ([tau:STATE](p(tau)) ∧ (Lt' (u tau) n))}
   HD p (while b body) ([tau:STATE](p(tau)) ∧ (is_false(b tau)))]
[Con: {p,p1,q,q1:Assertion}{S:prog}
       {ConSC : {sigma,tau:STATE}((p sigma) -> p1 sigma) ∧ ((q1 tau) -> q tau)}
       (HD p1 S q1)->HD p S q]
NoReductions;
Goal Conl : {p,p1,q:Assertion}{S:prog}
       (p .Assertion_implies p1) ->
       (HD p1 S q)->HD p S q;

...

Goal Conr:{p,q,q1:Assertion}{S:prog}
       (q1 .Assertion_implies q) ->
       (HD p S q1)->HD p S q;

...

Goal Inv_lemma : {p,p':Assertion}
                   (p' .Assertion_implies p) -> HD p' (skip) p;

...

Goal Assign_lemma :
  {x:VAR.DecSetoid_carrier}{t:x.sort.expression}{p,p':Assertion}
  (p' .Assertion_implies
   (compose p ([sigma:STATE]STATE_update x (t sigma) sigma))) ->
  HD p' (assign x t) p;

...

Module soundness Import operate semantics hoare;
(* The consequence rule preserves soundness *)
$Goal soundness_consequence :
  {p,p1,q,q1:Assertion}{S:prog}
  {ConSC : {sigma,tau:STATE}((p sigma) -> p1 sigma) ∧ ((q1 tau) -> q tau)}
  (semantics p1 S q1)->semantics p S q;

...

(*  Corresponds to Theorem 2.22 on page 32. *)
Goal soundness :
  {P:Assertion}{S:prog}{Q:Assertion}(HD P S Q) ->
  (semantics P S Q);

...

Module completeness Import operate_thms hoare semantics WF;
[pre [S:prog][q:Assertion]
  = [rho:STATE]Ex[tau:STATE]and (prog_operate rho S tau) (q tau)
  : Assertion]
[post [p:Assertion][S:prog]
  = [tau:STATE]Ex [rho:STATE](p rho) ∧ (prog_operate rho S tau)
  : Assertion];
(*  Corresponds to Definition 2.23 on page 32. *)
Goal completeness :

  {P|Assertion}{S|prog}{Q|Assertion}
  (semantics P S Q) -> HD P S Q;

...

(* Hoare Logic Assertions over extended state space *)
Module HAuxAssertion Import state;
(*  Corresponds to Definition 2.24 on page 36. *)
[Assertion [T:Type] = T -> STATE -> Prop];
[T,U|Type]
[UpdateAssertion
  [x:VAR.DecSetoid_carrier][t:expression (sort x)][p:Assertion T]
  = [z:T][sigma:STATE]
     [sigma' = STATE_update x (t sigma) sigma]p z sigma']
[ExAssertion [P:U->Assertion T]
  = [z:T][sigma:STATE]Ex ([u:U]P u z sigma)];
[KSC [p,q:Assertion T][p1,q1:Assertion U]
  = {Z|T}{sigma|STATE}(p Z sigma) ->
      Ex [Z1:U](p1 Z1 sigma ∧
 ({tau:STATE}(q1 Z1 tau) -> q Z tau))];
Discharge T;

(* Semantics of Hoare Logic *)
Module HAuxSem Import operate HAuxAssertion;
[T|Type];
(*  Corresponds to Definition 2.26 on page 36. *)
[HAuxSem [p:Assertion(T)][S:prog][q:Assertion(T)] =
  {Z|T}{sigma|STATE}(p Z sigma) ->
  Ex [tau:STATE](prog_operate sigma S tau) ∧ (q Z tau)];
Discharge T;

Module HAux Import syntax HAuxSem lib_nat_Lt lib_rel;
(*  Corresponds to Definition 2.27 on page 37. *)
Inductive [HD : {T|Type}{p:Assertion(T)}{S:prog}{q:Assertion(T)}Prop]
Relation
Constructors
(* skip *)
[Inv : {T|Type}{p:Assertion(T)}HD p skip p]
(* := *)
[Assign :
  {T|Type}{x:VAR.DecSetoid_carrier}{t:x.sort.expression}
  {p:Assertion(T)}
  HD ([Z:T][sigma:STATE][sigma' = STATE_update x (t sigma) sigma]
        p Z sigma') (assign x t) p]
(* ; *)
[Seq : {T|Type}{p,q,r:Assertion(T)}{S1,S2:prog}
        (HD p S1 r) -> (HD r S2 q) -> HD p (seq S1 S2) q]
[Ifthenelse :
  {T|Type}{p,q:Assertion(T)}{b:STATE->bool}{S1,S2:prog}
  (HD ([Z:T][sigma:STATE](p Z sigma) ∧ (is_true (b sigma))) S1 q) ->

(HD ([Z:T][sigma:STATE](p Z sigma) ∧ (is_false (b sigma))) S2 q) ->
HD p (ifthenelse b S1 S2) q]
(* while *)
[While :
 {T,W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
 {u:expression(W)} (* convergence function *)
 {p:Assertion(T)}{b:STATE->bool}{body:prog}
 {While_step:{t:W}
   HD ([Z:T][sigma:STATE]
        (p Z sigma) ∧ (is_true(b sigma)) ∧ (Eq (u sigma) t))
       body ([Z:T][tau:STATE](p Z tau) ∧ (Lt' (u tau) t))}
 HD p (while b body) ([Z:T][tau:STATE](p Z tau) ∧ (is_false(b tau)))]
[Con: {T,U|Type}{p1,q1:Assertion(T)}{p,q:Assertion(U)}{S:prog}
     {ConSC : {Z|U}{sigma:STATE}(p Z sigma) ->
     Ex [Z1:T](p1 Z1 sigma) ∧ ({tau:STATE}(q1 Z1 tau) -> q Z tau)}
     (HD p1 S q1)->HD p S q]
NoReductions;
[T|Type];
Goal Conl :
{p,p1,q:Assertion(T)}{S:prog}
{SC:{Z|T}{sigma|STATE}(p Z sigma)->p1 Z sigma}
(HD p1 S q)->HD p S q;

...

Goal Conr:
{p,q,q1:Assertion(T)}{S:prog}
{SC:{Z|T}{tau|STATE}(q1 Z tau)->q Z tau}
     (HD p S q1)->HD p S q;

...

Goal Inv_lemma :
{p,p':Assertion(T)}
{SC:{Z|T}{sigma|STATE}(p' Z sigma)->p Z sigma}HD p' (skip) p;

...

Goal Assign_lemma :
{x:VAR.DecSetoid_carrier}{t:x.sort.expression}
{p,p':Assertion(T)}
{SC:{Z|T}{sigma|STATE}
  (p' Z sigma)->p Z (STATE_update x (t sigma) sigma)}
HD p' (assign x t) p;

...

Discharge T;

Module HAuxsound Import HAuxSem HAux operate_thms;
(** Our new consequence rule preserves soundness **)
$Goal HDConsequencesoundness :
{T|Type}{p,q:Assertion T}
{T1|Type}{p1,q1:Assertion T1}
{S:prog}
{ConsSC:{z|T}{sigma|STATE}
         (p z sigma) ->
  Ex [z1:T1](p1 z1 sigma ∧ ({tau:STATE}(q1 z1 tau) -> q z tau))}
{ConsPrem:HAuxSem p1 S q1}HAuxSem p S q;
Expand HAuxSem;
(* Given an auxiliary variable Z and an initial state σ
     satisfying p(Z,σ), *)
intros _________Z sigma pZsigma;

  (* we need to find a final state τ such that σ  \overline{^{S}} τ
       and q(Z,τ). Combining p(Z,σ) and ConsSC, *)
  Refine ConsSC pZsigma;
  (* we can extract a witness Z_{1} such that p_{1}(Z_{1},σ) holds, and for
      any state τ, we may reduce the task of showing q(Z,τ) to

q_{1}(Z_{1},τ) *)

  intros Z1 Z1_details; Refine Z1_details; intros p1Z1sigma q1q;
  (* Appealing to ConsPrem, from p_{1}(Z_{1},σ) *)
  Refine ConsPrem p1Z1sigma;
  (* we may infer that there is a state τ satisfying both

σ \overline{^{S}} τ and q_{1}(Z_{1},τ). *)

  intros tau tau_details; Refine tau_details; intros _q1Z1tau;
  Refine ExIn; Refine -0 pair;
  Refine -0 q1q; Immed;
$Save HDConsequencesoundness;
(*  Corresponds to Theorem 2.28 on page 38. *)
Goal Hsoundness :
  {T|Type}
  {P|Assertion T}{S|prog}{Q|Assertion T}
  {der:HD P S Q}HAuxSem P S Q;

...

Module WF Import TransClosure termination syntax;
[b:expression(bool)][body:prog];
[sofar [tau,sigma:STATE]
  = TransClosure
         ([sigma,tau:STATE](is_true (b sigma)) ∧
                       (prog_operate sigma body tau)) sigma tau];
Goal WF_sofar :
  WF ([tau,sigma:STATE]
        (Termin (while b body) sigma ∧ sofar tau sigma));

...

Discharge b;

Module HAuxComplete Import operate_thms HAux HAuxSem WF;
(*  Corresponds to Definition 2.29 on page 40. *)
[MGF [S:prog]
  = HD ([Z,sigma:STATE]prog_operate sigma S Z) S (Eq|(STATE))];
(*  Corresponds to Theorem 2.30 on page 40. *)
Goal HL_most_general : {S:prog}MGF S;

...

(*  Corresponds to Corollary 2.31 on page 41. *)
Goal HL_complete :
  {S|prog}{T|Type}{p,q|Assertion T}
  {semant:HAuxSem p S q}HD p S q;
intros -0;
Refine Con; Refine -0 HL_most_general;
Dnf; intros;
Refine semant; Immed;
intros eta eta_details; Refine eta_details; intros;
Refine ExIn;

Refine -0 pair; Immed;
qnify; Immed;
Save HL_complete;

(* Embedding VDM Correctness Formulae in Hoare Logic
Proof-Theoretic Argument *)
Module VDMToH Import VDM HAux lib_bool_thms;
[Translate [p:Pred STATE][S:prog][q:Rel STATE STATE] =
HD ([Z,sigma:STATE](Eq sigma Z) ∧ (p sigma)) S ([Z,tau:STATE]q tau Z)];
(*  Corresponds to Theorem 2.32 on page 43. *)
Goal VDMToH :
{p|Pred STATE}{S|prog}{q|Rel STATE STATE}
(VDMder p S q) -> Translate p S q;
Induction 1;
(** Skip **)
Refine Inv_lemma; intros; Refine H.fst;
(** Assign **)
intros; Refine Assign_lemma; intros;
Qrepl H.fst; Refine Eq_refl;
(** Sequential Composition **)
intros; Refine Seq; Refine -0 Con; Assumption -0; Assumption +1;
Dnf; intros; andE H; Refine ExIn; Refine +1 pair; Refine +1 pair;
Refine +1 Eq_refl;
Assumption;
Intros; Refine ex_y; Immed;
(** Conditional **)
intros; Refine Ifthenelse;
Refine Conl; Assumption +2; intros; andE H; andE H1;
  Refine pair; Refine -0 pair; Immed;
Refine Conl; Assumption +2; intros; andE H; andE H1;
  Refine pair; Refine -0 pair; Immed;
(** Loop **)
intros;
[invariant [Z,sigma:STATE] = (reflclose sofar sigma Z) ∧ (P sigma)];
Refine Con; Refine -0 While; Refine +1 invariant;
(** Resolving side-condition of consequence rule **)
Dnf; intros; andE H; Refine ExIn; Refine -0 pair;
Refine +1 pair; Refine +1 inr;
Assumption +1; Assumption;
intros; andE H3; andE H4; Refine pair; Refine pair;
Immed;
Refine +3 Id; Assumption +1;
intros -0; Refine Con; Assumption -0;
(** Resolving side-condition of consequence rule **)
Expand Id; Expand andRel VDM_Pred_to_Rel;
 intros -0; andE H; andE H1; Qrepl Eq_sym H2;
andE H3; Refine ExIn;
Refine -0 pair; Refine +1 pair; Refine +2 pair;
Assumption +1; Assumption; Assumption;
intros; andE H7; Refine pair; Refine pair; Refine -0 pair;
Refine H5;
  intros is_lt; Refine inl; Refine sofar_trans; Assumption +1;
  Qrepl Eq_sym H2; Immed;

       intros is_eq; Refine inl; Qrepl Eq_sym is_eq; Qrepl H2; Immed;
     Assumption;
     Qrepl H2;
     Immed;
  (** The Rule of Consequence **)
  intros; Refine Con; Assumption -0; Dnf;
  intros; andE H; Refine ExIn; Refine -0 pair; Refine +1 pair;
  Assumption +1;
  Refine  ConsPre; Assumption;
  intros _; Refine ConsPost; Qrepl Eq_sym H1; Immed;
Save VDMToH;

B.2 Recursive Procedures

Module syntax Import lib_bool state;
(*****************************************)
(*** The syntax of imperative programs ***)
(*****************************************)
(*  Corresponds to Definition 3.1 on page 58. *)
Inductive [prog:Type] Theorems
Constructors
  [skip:prog]
  [assign:{x:VAR.DecSetoid_carrier}{t:x.sort.expression}prog]
  [seq:{S1,S2:prog}prog]
  [ifthenelse:{b:bool.expression}{S1,S2:prog}prog]
  [while:{b:bool.expression}{body:prog}prog]
  [call:prog]; (* without parameters *)
Goal ifloop : {T|Type}{S:prog}{then:T}{else:T}T;

...

Goal loop_extract_b : {default:bool.expression}{S:prog}bool.expression;

...

Goal loop_extract_body : {S:prog}prog;

...

[P : {S:prog}Prop];
Goal prog_predicate : {S:prog}Prop;

...

Discharge P;
Goal assign_update :
  {T|Type}
  {S:prog}{x:VAR.DecSetoid_carrier}{t:expression (sort x)}
  {f:{x:VAR.DecSetoid_carrier}{t:expression (sort x)}T}
  T;

...

Goal prog_assign_injective :
   {ix0,iy0|VAR.DecSetoid_carrier}
   {ix1|expression (sort ix0)}{iy1|expression (sort iy0)}
   (Eq  (assign iy0 iy1) (assign ix0 ix1))->
   {P:{ix0:VAR.DecSetoid_carrier}{ix1:expression (sort ix0)}Prop}
   (P ix0 ix1) -> P iy0 iy1;

...

(* A recursive procedure declaration for computing the factorial function *)

Module fac Import lib_nat_bool_rels lib_nat_Lt lib_nat_times_thms syntax;
(** belongs in library **)
Goal bool_eq_refl : {x:bool}is_true (bool_eq x x);

...

Freeze Eq;
Goal bool_char : Eqchar bool_eq;

...

Goal bool_DecSetoid : DecSetoid;
Refine make_DecSetoid; Refine -0 bool_char;
Save bool_DecSetoid;

(*** (global) variable declaration ***)
Cut [VAR = bool_DecSetoid];
Cut [sort = [_:bool_DecSetoid.DecSetoid_carrier]nat];
$[x = false : VAR.DecSetoid_carrier][y = true : VAR.DecSetoid_carrier];
(*  Corresponds to Example 3.2 on page 58. *)
$Goal FactorialProcedure : prog;
Refine ifthenelse;
  Intros sigma; Refine nat_eq (sigma x) zero;
  Refine assign; Refine y; Intros; Refine one; (* then branch *)
  (** else branch **)
  Refine seq;
     Refine assign; Refine x; Intros sigma; Refine pred (sigma x);
  Refine seq;
     Refine call;
  Refine seq;
     Refine assign; Refine x; Intros sigma; Refine suc (sigma x);
     Refine assign; Refine y; Intros sigma; Refine times (sigma y) (sigma x);
Save FactorialProcedure;

Module operate Import lib_bool_thms lib_nat syntax;
(*  Corresponds to Definition 3.3 on page 59. *)
Inductive [prog_operate : {sigma:STATE}{S:prog}{tau:STATE}Prop]
Relation Inversion NoReductions
Parameters [S0:prog]
Constructors
  [skip_operate
     : {sigma:STATE}prog_operate sigma skip sigma]
  [assign_operate
     : {x:VAR.DecSetoid_carrier}{t:x.sort.expression}{sigma:STATE}
       prog_operate sigma (assign x t) (STATE_update x (t sigma) sigma)]
  [seq_operate
     : {S1,S2:prog}{sigma,tau,eta:STATE}
       (prog_operate sigma S1 tau) -> (prog_operate tau S2 eta)->
       prog_operate sigma (seq S1 S2) eta]
  [ifthen_operate
     : {b:bool.expression}{S1,S2:prog}{sigma,tau:STATE}
       (is_true (b (lookupSTATE sigma))) ->
       (prog_operate sigma S1 tau) ->
       prog_operate sigma (ifthenelse b S1 S2) tau]
  [ifelse_operate
     : {b:bool.expression}{S1,S2:prog}{sigma,tau:STATE}
       (is_false (b (lookupSTATE sigma))) ->
       (prog_operate sigma S2 tau) ->
       prog_operate sigma (ifthenelse b S1 S2) tau]

  [while_exit_operate
     : {b:bool.expression}{body:prog}{sigma:STATE}
       (is_false (b (lookupSTATE sigma))) ->
       prog_operate sigma (while b body) sigma]
  [while_body_operate
     : {b:bool.expression}{body:prog}{sigma,tau,eta:STATE}
       (is_true (b (lookupSTATE sigma))) ->
       (prog_operate sigma body tau) ->
       (prog_operate tau (while b body) eta) ->
       prog_operate sigma (while b body) eta]
  [call_operate
     : {sigma,tau:STATE}
       (prog_operate sigma S0 tau) ->
       prog_operate sigma call tau];

Module operate_thms Import lib_bool_funs operate;
Goal oper_Inversion : {S:prog}{sigma,tau:STATE}Prop;

...

Goal oper_inversion : {sigma|STATE}{S|prog}{tau|STATE}
{D:prog_operate sigma S tau}(oper_Inversion S sigma tau);

...

Goal oper_inversion_ifthenelse :
  {b|expression bool}{S1,S2|prog}{sigma,tau|STATE}{c|bool}
  {eq:Eq (b sigma) c}(prog_operate sigma (ifthenelse b S1 S2) tau) ->
  prog_operate sigma (if c S1 S2) tau;

...

Goal oper_inversion_while :
  {c|bool}{b|STATE->bool}{S|prog}{sigma,tau|STATE}
  (Eq (b sigma) c)->(prog_operate sigma (while b S) tau) ->
  if c (Ex [rho:STATE]and (prog_operate sigma S rho)
 (prog_operate rho (while b S) tau)) (Eq sigma tau);

...

Goal oper_determ :
  {sigma|STATE}{S|prog}{tau|STATE}
  {Prem_tau|prog_operate sigma S tau}{C:STATE->Prop}{eta:STATE}
  {Prem_eta:prog_operate sigma S eta}
  {C_eta:C eta}C tau;

...

(* Contexts for Hoare Logic *)
Module Hcontext Import HAuxAssertion lib_prod lib_rel;
[Spec [T:Type] = prod (Assertion T) (Assertion T)];
(*  Corresponds to Definition 3.4 on page 60. *)
Inductive [HContext : Type] Theorems
Constructors [HNone:HContext][HSome:{T|Type}{spec:Spec T}HContext];
Goal HContext_disjoint : {T|Type}{spec:Spec T}not (Eq HNone (HSome spec));

...

$Goal HContext_predicate : {P:Pred HContext}{O:HContext}Prop;
intros _;
Induction O; Refine trueProp; intros; Refine P (HSome spec);

$Save HContext_predicate;
$Goal HSome_update :
  {T|Type}{O:HContext}{B|Type}{b:Spec B}{f:{A|Type}{a:Spec A}T}T;
intros _;
Induction O;
  intros; Refine f b;
  intros; Refine f spec;
$Save HSome_update;
Goal HContext_injective :
  {A,B|Type}{a|Spec A}{b|Spec B}(Eq (HSome a) (HSome b)) ->
  {P:{T|Type}{t:Spec T}Prop}(P b) -> P a;
intros;
Claim Eq (HContext_predicate ([O:HContext]HSome_update O b P) (HSome a))
          (HContext_predicate ([O:HContext]HSome_update O b P) (HSome b));
Qrepl ?-0; Immed; Refine Eq_resp; Immed;
Save HContext_injective;

Module H Import Hcontext operate;
[HSomeSpec [T|Type][p,q:Assertion T] = HSome (Pair p q)];
(*  Corresponds to Figure 3.1 on page 63. *)
Inductive
  [HD : {T|Type}
         {Gamma:HContext}
{P:Assertion T}{S:prog}{Q:Assertion T}Prop]
Relation NoReductions
Constructors
  [HDAssumption : {T|Type}{p,q:Assertion T}HD (HSomeSpec p q) p call q]
  [HDSkip : {T|Type}{Gamma:HContext}{p:Assertion T}HD Gamma p skip p]
  [HDAssign : {T|Type}{x:VAR.DecSetoid_carrier}{t:expression (sort x)}
                {Gamma:HContext}{p:Assertion T}
                HD Gamma (UpdateAssertion x t p) (assign x t) p]
  [HDSeq : {T|Type}{Gamma|HContext}{p,r,q|Assertion T}{S1,S2:prog}
             (HD Gamma p S1 r) ->
   (HD Gamma r S2 q) ->
   HD  Gamma p (seq S1 S2) q]
  [HDIfthenelse :
     {T|Type}{Gamma|HContext}{pre,post|Assertion T}
     {b|expression bool}{TH,EL:prog}
     (HD Gamma ([z:T][sigma:STATE]and (pre z sigma)
                        (is_true (b sigma.lookupSTATE)))
         TH post) ->
     (HD Gamma ([z:T][sigma:STATE]and (pre z sigma)
      (is_false (b sigma.lookupSTATE)))
         EL post) ->
     HD Gamma pre (ifthenelse b TH EL) post]
  [HDWhile :
   {Gamma|HContext}{T,W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
   {u:expression(W)} (* convergence function *)
   {p:Assertion(T)}{b:STATE->bool}{body:prog}
   {While_step:{t:W}
      HD Gamma ([Z:T][sigma:STATE]
          (p Z sigma) ∧ (is_true(b sigma)) ∧ (Eq (u sigma) t))
         body ([Z:T][tau:STATE](p Z tau) ∧ (Lt' (u tau) t))}
   HD Gamma p (while b body) ([Z:T][tau:STATE](p Z tau) ∧ (is_false(b tau)))]
  [HDCall :
     {T,W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
     {p|W->Assertion T}

  {q:Assertion T}
  {Call_step:{t:W}
     HD (HSomeSpec ([Z:T][sigma:STATE]Ex [u:W](p(u)(Z)(sigma)) ∧ (Lt' u t))
     q) (p(t)) S0 q}
           HD HNone ([Z:T][sigma:STATE]Ex [t:W]p(t)(Z)(sigma)) call q]
[HDConsequence : {Gamma:HContext}
                   {T|Type}{p,q:Assertion T}
                   {T1|Type}{p1,q1:Assertion T1}
                   {S:prog}
                   {ConsSC:KSC p q p1 q1}
                   {ConsPrem:HD Gamma p1 S q1}
                   HD Gamma p S q];

Module Htheorems Import H;
Goal HoareConsequenceLeft :
{T|Type}{Gamma|HContext}
{pre_s : Assertion T}{pre,post:Assertion T}{S:prog}
{strengthen : {z:T}{sigma:STATE}(pre_s z sigma) -> pre z sigma}
{prem : HD Gamma pre S post}
HD Gamma pre_s S post;

...

Goal HDSkipBU :
{T|Type}{Gamma|HContext}{p,q:Assertion T}
({z:T}{sigma:STATE}(p z sigma)->q z sigma) -> HD Gamma p skip q;

...

Goal HDAssignBU :
{x:VAR.DecSetoid_carrier}{t:expression (sort x)}
{T|Type}{Gamma|HContext}
{p,p':Assertion T}
{p'p:{z:T}{sigma:STATE}
      (p' z sigma) ->  (compose (p z) (STATE_update x (t sigma)) sigma)}
HD Gamma p' (assign x t) p;

...

(*  Corresponds to Figure 3.1.8 on page 72. *)
Goal HDAdaptation :
{T,U|Type}{Gamma|HContext}{q|Assertion T}{pre,post|Assertion U}
{S|prog}
{prem : HD Gamma pre S post}
[p [z:T][sigma:STATE] =
  Ex [z':U]and (pre z' sigma)
  ({tau:STATE}(post z' tau) -> q z tau)]
HD Gamma p S q;

...

(* Semantics of Hoare logic*)
Module Hsemantics Import operate Hcontext;
[HAtomicSem [T|Type][p:Assertion T][S:prog][q:Assertion T]
= {z|T}{sigma|STATE}
  (p z sigma) -> Ex [tau:STATE]and (prog_operate sigma S tau) (q z tau)];
(*  Corresponds to Lemma 3.6 on page 64. *)
Goal HSemEx :
{T,U|Type}{p:U->Assertion T}{S:prog}{q:Assertion T}
iff (HAtomicSem ([Z:T][sigma:STATE]Ex [t:U]p(t)(Z)(sigma)) S q)
     ({t:U}HAtomicSem (p(t)) S q);

...

(*  Corresponds to Definition 3.5 on page 62. *)

Goal HSem :
  {T|Type}{Gamma:HContext}{p:Assertion T}{S:prog}{q:Assertion T}Prop;
Induction Gamma;
  Refine HAtomicSem; (* empty context *)
  intros;
  Refine (HAtomicSem spec.Fst call spec.Snd) ->
     HAtomicSem p S q;
Save HSem;

Module Hsound Import Hsemantics H operate_thms;
[Gamma:HContext][T|Type(0)]
[p,r,q:Assertion T]
[b:expression bool]
[S1,S2:prog];
Goal HDAssumptionSound : HSem (HSomeSpec p q) p call q;

...

$Goal HDInvAtomicSound : HAtomicSem p skip p;

...

Goal HDInvSound : HSem Gamma p skip p;

...

$Goal HDAssignAtomicSound :
  {x:VAR.DecSetoid_carrier}
  {t:expression (sort x)}
  HAtomicSem (UpdateAssertion x t p) (assign x t) p;

...

Goal HDAssignSound :
  {x:VAR.DecSetoid_carrier}{t:expression (sort x)}
  HSem Gamma (UpdateAssertion x t p) (assign x t) p;

...

$Goal HDSeqAtomicSound :
  {Seq_prem1:HAtomicSem p S1 r}
  {Seq_prem2:HAtomicSem r S2 q}
  HAtomicSem p (seq S1 S2) q;

...

Goal HDSeqSound :
  {Seq_prem1:HSem Gamma p S1 r}
  {Seq_prem2:HSem Gamma r S2 q}
  HSem Gamma p (seq S1 S2) q;

...

$Goal HDIfthenelseAtomicSound :
  {Ifthenelse_prem1:
       HAtomicSem ([Z:T][sigma:STATE ]
   (p Z sigma) ∧ (is_true (eval sigma b)))
 S1 q}
  {Ifthenelse_prem2:
     HAtomicSem ([Z:T][sigma:STATE ]
       (p Z sigma) ∧ (is_false (eval sigma b)))
                             S2 q}
     HAtomicSem p (ifthenelse b S1 S2) q;

...

Goal HDIfthenelseSound :
  {Ifthenelse_prem1:
       HSem Gamma ([Z:T][sigma:STATE ]
   (p Z sigma) ∧ (is_true (eval sigma b)))

S1 q}
{Ifthenelse_prem2:
  HSem Gamma ([Z:T][sigma:STATE ]
     (p Z sigma) ∧ (is_false (eval sigma b)))
                          S2 q}
  HSem Gamma p (ifthenelse b S1 S2) q;

...

$Goal HDWhileAtomicSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
{u:(STATE )->W}
{body:prog}
{While_step:{t:W}
     HAtomicSem ([Z:T][sigma:STATE ]
 (p Z sigma) ∧ (is_true(eval(sigma)(b))) ∧
 (Eq (u sigma) t))
     body ([Z:T][tau:STATE ](p Z tau) ∧ (Lt' (u tau) t))}
 HAtomicSem p (while b body)
  ([Z:T][tau:STATE ](p Z tau) ∧ (is_false(eval tau b)));

...

Goal HDWhileSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
{u:(STATE )->W}
{body:prog}
{While_step:{t:W}
     HSem Gamma ([Z:T][sigma:STATE ]
 (p Z sigma) ∧ (is_true(eval(sigma)(b))) ∧
 (Eq (u sigma) t))
     body ([Z:T][tau:STATE ](p Z tau) ∧ (Lt' (u tau) t))}
 HSem Gamma p (while b body)
  ([Z:T][tau:STATE ](p Z tau) ∧ (is_false(eval tau b)));

...

(** Our new consequence rule preserves soundness **)
$Goal HDConsequenceAtomicsoundness :
{T|Type}{p,q:Assertion T}
{T1|Type}{p1,q1:Assertion T1}
{S:prog}
{ConsSC:KSC p q p1 q1}
{ConsPrem:HAtomicSem p1 S q1}HAtomicSem p S q;

...

Goal HDConSound :
{U|Type}{pre,post:Assertion U}
{S:prog}
{Prem : KSC p q pre post}
{prem : HSem Gamma pre S post}
  HSem Gamma p S q;

...

Goal HDCallSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
  {p|W->Assertion T}
  {Call_step:{t:W}
     HSem (HSomeSpec
         ([Z:T][sigma:STATE]
  Ex [u:W](p(u)(Z)(sigma)) ∧ (Lt' u t))
 q) (p(t)) S0 q}
 HSem HNone
     ([Z:T][sigma:STATE]Ex [t:W]p(t)(Z)(sigma))
     call q;

...

Discharge Gamma;
(*  Corresponds to Theorem 3.7 on page 65. *)
Goal Hsoundness :
{T|Type}

{Gamma|HContext}
{P|Assertion T}{S|prog}{Q|Assertion T}
{der:HD Gamma P S Q}
HSem Gamma P S Q;

...

Module boperate Import operate;
(*  Corresponds to Definition 3.9 on page 66. *)
Inductive [prog_boperate : {sigma:STATE}{S:prog}{tau:STATE}{n:nat}Prop]
Relation NoReductions Inversion
Constructors
[skip_boperate
: {sigma:STATE}{n:nat}prog_boperate sigma skip sigma n]
[assign_boperate
: {x:VAR.DecSetoid_carrier}{t:x.sort.expression}
{sigma:STATE}{n:nat}
prog_boperate sigma (assign x t) (STATE_update x (t sigma) sigma) n]
[seq_boperate
: {S1,S2:prog}{sigma,tau,eta:STATE}{n:nat}
(prog_boperate sigma S1 tau n) -> (prog_boperate tau S2 eta n)->
prog_boperate sigma (seq S1 S2) eta n]
[ifthen_boperate
: {b:bool.expression}{S1,S2:prog}{sigma,tau:STATE}{n:nat}
(is_true (b (lookupSTATE sigma))) ->
(prog_boperate sigma S1 tau n) ->
prog_boperate sigma (ifthenelse b S1 S2) tau n]
[ifelse_boperate
: {b:bool.expression}{S1,S2:prog}{sigma,tau:STATE}{n:nat}
(is_false (b (lookupSTATE sigma))) ->
(prog_boperate sigma S2 tau n) ->
prog_boperate sigma (ifthenelse b S1 S2) tau n]
[while_exit_boperate
: {b:bool.expression}{body:prog}{sigma:STATE}{n:nat}
(is_false (b (lookupSTATE sigma))) ->
prog_boperate sigma (while b body) sigma n]
[while_body_boperate
: {b:bool.expression}{body:prog}{sigma,tau,eta:STATE}{n:nat}
(is_true (b (lookupSTATE sigma))) ->
(prog_boperate sigma body tau n) ->
(prog_boperate tau (while b body) eta n) ->
prog_boperate sigma (while b body) eta n]
[call_boperate
: {sigma,tau:STATE}{n:nat}
(prog_boperate sigma S0 tau n) ->
prog_boperate sigma call tau (suc n)];

Module boperate_thms

Import lib_bool_funs boperate lib_nat_Prop_rels lib_max_min;
Goal boper_Inversion : {S:prog}{sigma,tau:STATE}{n:nat}Prop;

...

Goal boper_inversion : {sigma|STATE}{S|prog}{tau|STATE}{n|nat}
{D:prog_boperate sigma S tau n}(boper_Inversion S sigma tau n);

...

Goal boper_ifthen : {b|STATE->bool}{S1,S2|prog}{sigma,tau|STATE}{n|nat}
(is_true (b sigma))->
(prog_boperate sigma (ifthenelse b S1 S2) tau n) ->

prog_boperate sigma S1 tau n;

...

Goal boper_ifelse : {b|STATE->bool}{S1,S2|prog}{sigma,tau|STATE}{n|nat}
(is_false (b sigma)) ->
(prog_boperate sigma (ifthenelse b S1 S2) tau n) ->
prog_boperate sigma S2 tau n;

...

Goal boper_while : {c|bool}{b|STATE->bool}{S|prog}{sigma,tau|STATE}{n|nat}
(Eq (b sigma) c)->(prog_boperate sigma (while b S) tau n) ->
         (and (is_false (b tau))
      (if c (Ex [rho:STATE]
              and (prog_boperate sigma S rho n)
                  (prog_boperate rho (while b S) tau n))
                               (Eq sigma tau)));

...

Goal boper_while_false :
  {b|STATE->bool}{S|prog}{sigma,tau|STATE}{n|nat}
  (prog_boperate sigma (while b S) tau n) -> is_false (b tau);

...

Goal boper_determ :
  {sigma|STATE}{S|prog}{tau|STATE}{n|nat}
  {Prem_tau|prog_boperate sigma S tau n}{C:STATE->Prop}{eta:STATE}
  {m|nat}{Prem_eta:prog_boperate sigma S eta m}
  {C_eta:C eta}C tau;

...

(*  Corresponds to Lemma 3.10 on page 66. *)
Goal boperate_not_strict :
  {sigma|STATE}{S|prog}{tau|STATE}{n|nat}
  (prog_boperate sigma S tau n) ->
  {m:nat}(Le n m) -> prog_boperate sigma S tau m;

...

(*  Corresponds to Lemma 3.11 on page 67. *)
Goal operate2boperate :
  {sigma|STATE}{S|prog}{tau|STATE}
  {Prem:prog_operate sigma S tau}
  Ex [n:nat]prog_boperate sigma S tau n;

...

Goal boperate2operate :
  {sigma|STATE}{S|prog}{tau|STATE}{n|nat}
  {Prem:prog_boperate sigma S tau n}
         prog_operate sigma S tau;

...

Freeze Eq;
Goal boperate_zero :
  {sigma,tau:STATE}not (prog_boperate sigma call tau zero);

...

(* Completeness of Hoare logic *)
Module Hcomplete Import boperate_thms operate_thms
      Htheorems Hsemantics WF;
$[Hoarep0 [S:prog][n:nat] =
  [Z,sigma:STATE]prog_boperate sigma S Z n];
$[psi [n:nat] = Hoarep0 call (suc n)];
[q [Z,tau:STATE] = Eq Z tau];
Freeze Eq;

(*  Corresponds to Lemma 3.12 on page 67. *)
Goal Hoare_completeness_step :
      {n:nat}
      HD (HSomeSpec ([Z,sigma:STATE]
                       Ex ([u:nat](psi u Z sigma ∧ Lt u n)))
   q)
         (psi n) S0 q;

...

(*  Corresponds to Theorem 3.8 on page 65. *)
Goal HL_most_general :
   {S:prog}
     HD HNone  ([z:STATE][sigma:STATE]prog_operate sigma S z)
               S q;

...

(*  Corresponds to Corollary 3.13 on page 69. *)
Goal HL_complete :
  {S|prog}{T|Type}{p|Rel T STATE}{q|Rel T STATE}
  {semant:HSem HNone p S q}HD HNone p S q;

...

(* Proof in Hoare logic that our factorial procedure leaves x invariant*)
Module Hfac Import fac Htheorems;
Cut [S0 = FactorialProcedure];
[HoareFactorialInvariantAssertion [z:nat][sigma:STATE] = Eq (sigma x) z]
[HoareFactorialInvariantCF
  = HD HNone
        HoareFactorialInvariantAssertion call HoareFactorialInvariantAssertion];
(*  Corresponds to Lemma 3.14 on page 69. *)
Goal HoareFactorialInvariantCorrect : HoareFactorialInvariantCF;

...

Module VDMcontext Import lib_prod lib_rel state;
[VDMSpec = prod (Pred STATE) (Rel STATE STATE)];
Inductive [VDMContext : Type] Theorems
Constructors [VDMNone:VDMContext][VDMSome:{spec:VDMSpec}VDMContext];
[VDMSomeSpec [p:Pred STATE][q:Rel STATE STATE]
  = VDMSome (Pair p q)];

Module VDMsemantics Import VDMcontext operate;
[VDMAtomicSem
  [p:Pred STATE][S:prog][q:Rel STATE STATE ]
  = {sigma|STATE}(p sigma) ->
  Ex [tau:STATE]and (prog_operate sigma S tau) (q tau sigma)];
Goal VDMSemEx :
  {U|Type}
  {p:U->Pred STATE}
  {S:prog}
  {q:Rel STATE STATE}
  iff (VDMAtomicSem ([sigma:STATE]
     Ex [t:U]p(t)(sigma)) S q)
       ({t:U}VDMAtomicSem (p(t)) S q);

...

Goal VDMSem :

{Gamma:VDMContext}
{p:Pred STATE}
{S:prog}
{q:Rel STATE STATE}
Prop;

...

Module VDM Import tms_rel VDMcontext operate;
[VDM_Pred_to_Rel [P:Pred STATE]
= [tau,_:STATE]P tau : Rel STATE STATE];
(*  Corresponds to Figure 3.4 on page 76. *)
Inductive
[VDMder :
{Gamma:VDMContext}{P:Pred STATE}{S:prog}{Q:Rel STATE STATE}Prop]
Relation NoReductions
Constructors
[VDMAssumption :
{p:Pred STATE}{q:Rel STATE STATE}
VDMder (VDMSome (Pair p q)) p call q]
[VDMSkip : {Gamma:VDMContext}
          VDMder Gamma ([_:STATE]trueProp) skip (Eq|STATE)]
[VDMAssign :
{Gamma:VDMContext}
{x:VAR.DecSetoid_carrier}{t:expression (sort x)}
VDMder Gamma ([_:STATE]trueProp) (assign x t)
([tau,sigma:STATE]
  Eq tau (STATE_update x (eval sigma t) sigma))]
[VDMSeq :
{Gamma:VDMContext}
{p1,p2:STATE.Pred}{r1,r2:Rel STATE STATE}{S1,S2:prog}
(VDMder Gamma p1 S1 [tau,sigma:STATE]and (p2 tau) (r1 tau sigma)) ->
(VDMder Gamma p2 S2 r2) ->
VDMder Gamma p1 (seq S1 S2) (composeRel r2 r1)]
[VDMIfthenelse :
{Gamma:VDMContext}
{pre:STATE.Pred}{post:Rel STATE STATE}
{b:bool.expression}{TH,EL:prog}
(VDMder Gamma ([sigma:STATE](pre sigma) ∧ (is_true (b sigma.lookupSTATE)))
         TH post) ->
(VDMder Gamma ([sigma:STATE](pre sigma) ∧ (is_false (b sigma.lookupSTATE)))
         EL post) ->
VDMder Gamma pre (ifthenelse b TH EL) post]
[VDMWhile :
{Gamma:VDMContext}
{P:STATE.Pred}
{sofar:Rel STATE STATE}
{sofar_trans: trans sofar}
{sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
{b:bool.expression}{body:prog}
{Prem:VDMder Gamma
              ([sigma:STATE](P sigma) ∧ (is_true (b sigma.lookupSTATE)))
              body
     ([tau,sigma:STATE](P tau) ∧ (sofar tau sigma))}
VDMder Gamma P (while b body)
       ([tau,sigma:STATE](P tau) ∧ (is_false (b tau.lookupSTATE)) ∧
(reflclose sofar tau sigma))]
[VDMCall :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}

  {p:W->Pred STATE}
  {q:Rel STATE STATE}
  {Call_step:
     {t:W} VDMder (VDMSomeSpec ([sigma:STATE]
Ex [u:W](p(u)(sigma)) ∧ (Lt' u t))
 q)
     (p(t)) S0 q}
 VDMder VDMNone ([sigma:STATE]Ex [t:W]p(t)(sigma)) call q]
[VDMConsequence :
  {Gamma:VDMContext}
  {p,p1:Pred STATE}{q,q1:Rel STATE STATE}
  {S:prog}
  {ConsSC1:{sigma|STATE}(p sigma) -> (p1 sigma)}
  {ConsSC2:{tau,sigma|STATE}(p sigma) ->
     (q1 tau sigma) -> q tau sigma}
  {ConsPrem:VDMder Gamma p1 S q1}
  VDMder Gamma p S q];

Module VDMtheorems Import VDM;
Goal VDMPre :
{Gamma:VDMContext}{pre|STATE.Pred}{S|prog}{post|Rel STATE STATE}
{Prem:VDMder Gamma pre S post}
VDMder Gamma pre S [tau,sigma:STATE]and (pre sigma) (post tau sigma);

...

Goal VDMConsequenceLeft :
           {Gamma:VDMContext}
           {pre_s,pre : STATE.Pred}{post:Rel STATE STATE}{S:prog}
           {strengthen : {sigma:STATE}(pre_s sigma) -> pre sigma}
           {prem : VDMder Gamma pre S post}
  VDMder Gamma pre_s S post;

...

Goal VDMConsequenceRight:
{Gamma:VDMContext}
{pre : STATE.Pred}{post,post_w:Rel STATE STATE}{S:prog}
{weaken : {sigma,tau:STATE}(post tau sigma) -> post_w tau sigma}
{prem : VDMder Gamma pre S post}
VDMder Gamma pre S post_w;

...

Freeze Eq;
Goal VDMSkipBU :
{Gamma:VDMContext}
{p:Rel STATE STATE}{p':STATE.Pred}
{p'p:{sigma:STATE}(p' sigma) -> p sigma sigma}
VDMder Gamma p' skip p;

...

Goal VDMAssignBU :
{Gamma:VDMContext}
{x:VAR.DecSetoid_carrier}{t:expression (sort x)}
{p:Rel STATE STATE}{p':STATE.Pred}
{p'p:
  {tau:STATE}(p' tau) ->
  p (STATE_update x (eval tau t) tau) tau}
VDMder Gamma p' (assign x t) p;

...

Goal VDMSeqBU :
{Gamma:VDMContext}
{p1,p2:STATE.Pred}{r,r1,r2:Rel STATE STATE}{S1,S2:prog}
{weakening: SubRel (composeRel r2 r1) r}
(VDMder Gamma p1 S1 [tau,sigma:STATE]and (p2 tau) (r1 tau sigma)) ->

  (VDMder Gamma p2 S2 r2) ->
  VDMder Gamma p1 (seq S1 S2) r;

...

Goal VDMLoopBU :
   {Gamma:VDMContext}
   {P,I:STATE.Pred}
   {Q,sofar:Rel STATE STATE}
      {sofar_trans: trans sofar}
      {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel I))}
   {b:expression bool}{body:prog}
   {ScPre:SubPred P I}
   {ScPost:{tau,sigma:STATE}(I sigma) -> (I tau) ->
             (is_false (eval tau b)) ->
   (reflclose sofar tau sigma) -> Q tau sigma}
   {Prem:
      VDMder Gamma ([sigma:STATE](I sigma) ∧
       (is_true (eval sigma b)))
                  body
         ([tau,sigma:STATE](I tau) ∧ (sofar tau sigma))}
   VDMder Gamma P (while b body) Q;

...

Module VDMsound Import VDMsemantics VDM operate_thms;
[p:Pred STATE][q:Rel STATE STATE]
[b:expression bool]
[S1,S2:prog];
Goal VDMAssumptionSound : VDMSem (VDMSomeSpec p q) p call q;

...

$Goal VDMInvAtomicSound :
  VDMAtomicSem  ([_:STATE]trueProp) skip (Eq|STATE);

...

[Gamma:VDMContext];
Goal VDMInvSound :
  VDMSem Gamma ([_:STATE]trueProp) skip (Eq|STATE);

...

$Goal VDMAssignAtomicSound :
  {x:VAR.DecSetoid_carrier}
  {t:expression (sort x)}
  VDMAtomicSem  ([_:STATE]trueProp) (assign x t)
       ([tau,sigma:STATE]
Eq tau (STATE_update x (eval sigma t) sigma));

...

Goal VDMAssignSound :
  {x:VAR.DecSetoid_carrier}
  {t:expression (sort x)}
  VDMSem Gamma ([_:STATE]trueProp) (assign x t)
       ([tau,sigma:STATE]
Eq tau (STATE_update x (eval sigma t) sigma));

...

$Goal VDMSeqAtomicSound :
  {p1,p2:STATE.Pred}
  {r1,r2:Rel STATE STATE}
  {Seq_prem1:VDMAtomicSem  p1 S1 ([tau,sigma:STATE]
  and (p2 tau) (r1 tau sigma))}
  {Seq_prem2:VDMAtomicSem  p2 S2 r2}
  VDMAtomicSem  p1 (seq S1 S2) (composeRel r2 r1);

...

Goal VDMSeqSound :
{p1,p2:STATE.Pred}
{r1,r2:Rel STATE STATE}
{Seq_prem1:VDMSem Gamma p1 S1 ([tau,sigma:STATE]
and (p2 tau) (r1 tau sigma))}
{Seq_prem2:VDMSem Gamma p2 S2 r2}
VDMSem Gamma p1 (seq S1 S2) (composeRel r2 r1);

...

$Goal VDMIfthenelseAtomicSound :
{pre:STATE.Pred}
{post:Rel STATE STATE}
{TH,EL:prog}
{Ifthenelse_prem1:
  VDMAtomicSem  ([sigma:STATE]
(pre sigma) ∧ (is_true (eval sigma b)))
  TH post}
{Ifthenelse_prem2:
  VDMAtomicSem  ([sigma:STATE]
(pre sigma) ∧ (is_false (eval sigma b)))
  EL post}
VDMAtomicSem  pre (ifthenelse b TH EL) post;

...

Goal VDMIfthenelseSound :
{pre:STATE.Pred}
{post:Rel STATE STATE}
{TH,EL:prog}
{Ifthenelse_prem1:
     VDMSem Gamma ([sigma:STATE]
(pre sigma) ∧ (is_true (eval sigma b)))
  TH post}
{Ifthenelse_prem2:
  VDMSem Gamma ([sigma:STATE]
(pre sigma) ∧ (is_false (eval sigma b)))
  EL post}
  VDMSem Gamma pre (ifthenelse b TH EL) post;

...

$Goal VDMWhileAtomicSound :
{P:STATE.Pred}
  {sofar:Rel STATE STATE}
  {sofar_trans: trans sofar}
  {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
  {body:prog}
  {Prem:
     VDMAtomicSem  ([sigma:STATE]
      (P sigma) ∧ (is_true (eval sigma b)))
                body
       ([tau,sigma:STATE]
(P tau) ∧ (sofar tau sigma))}
 VDMAtomicSem  P (while b body)
         ([tau,sigma:STATE]
  (P tau) ∧ (is_false (eval tau b)) ∧
                             (reflclose sofar tau sigma));

...

Goal VDMWhileSound :
{P:STATE.Pred}
  {sofar:Rel STATE STATE}
  {sofar_trans: trans sofar}
  {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
  {body:prog}
  {Prem:
     VDMSem Gamma ([sigma:STATE]
      (P sigma) ∧ (is_true (eval sigma b)))
                body
       ([tau,sigma:STATE]
(P tau) ∧ (sofar tau sigma))}

 VDMSem Gamma P (while b body)
         ([tau,sigma:STATE]
  (P tau) ∧ (is_false (eval tau b)) ∧
  (reflclose sofar tau sigma));

...

Goal VDMCallSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
  {p:W->Pred STATE}
  {q:Rel STATE STATE}
  {Call_step:{t:W}
     VDMSem (VDMSomeSpec
         ([sigma:STATE]
  Ex [u:W](p(u)(sigma)) ∧ (Lt' u t))
 q)
     (p(t)) S0 q}
 VDMSem VDMNone
     ([sigma:STATE]Ex [t:W]p(t)(sigma))
     (call) q;

...

$Goal VDMConAtomicSound :
{p1:Pred STATE}
{q1:Rel STATE STATE}
{S:prog}
{ConsSC1:{sigma|STATE}(p sigma) -> (p1 sigma)}
{ConsSC2:{tau,sigma|STATE}(p sigma) ->
  (q1 tau sigma) -> q tau sigma}
{ConsPrem:VDMAtomicSem  p1 S q1}
VDMAtomicSem  p S q;

...

Goal VDMConSound :
{p1:Pred STATE}
{q1:Rel STATE STATE}
{S:prog}
{ConsSC1:{sigma|STATE}(p sigma) -> (p1 sigma)}
{ConsSC2:{tau,sigma|STATE}(p sigma) ->
  (q1 tau sigma) -> q tau sigma}
{ConsPrem:VDMSem Gamma p1 S q1}
VDMSem Gamma p S q;

...

Discharge p;
(*  Corresponds to Theorem 3.17 on page 75. *)
Goal VDMsoundness :
{Gamma|VDMContext}
{P|Pred STATE}{S|prog}{Q|Rel STATE STATE}
{der:VDMder Gamma P S Q}VDMSem Gamma P S Q;

...

Module VDMcomplete

Import VDMsemantics VDMtheorems termination WF boperate_thms;
[VDMp0 [S:prog][n:nat][sigma:STATE] =
   Ex [tau:STATE]prog_boperate sigma S tau n];

[psi [n:nat] = VDMp0 call (suc n)]
[p [n:nat][sigma:STATE] = Ex ([u:nat](psi u sigma ∧ Lt u n))];
Goal VDMp02Termin :
{S:prog}{sigma:STATE}{n:nat}(VDMp0 S n sigma) -> Termin S sigma;

...

[b:expression bool][body:prog][n:nat];

Goal VDMWFsofar :
  WF (andRel (sofar b body)
        (VDM_Pred_to_Rel (Termin (while b body))));

...

Goal VDMWFbsofar :
  WF (andRel (sofar b body)
        (VDM_Pred_to_Rel (VDMp0 (while b body) n)));

...

Discharge b;
Goal VDM_completeness_step :
  {n:nat}{S:prog}
  VDMder (VDMSome (Pair (p n)
                           ([tau,sigma:STATE]prog_operate sigma call tau)))
        (VDMp0 S n) S ([tau,sigma:STATE]prog_operate sigma S tau);

...

Goal VDM_most_general :
   {S:prog}
     VDMder VDMNone ([sigma:STATE]Ex [tau:STATE]prog_operate sigma S tau)
               S
      ([tau,sigma:STATE]prog_operate sigma S tau);

...

(*  Corresponds to Theorem 3.18 on page 75. *)
Goal VDM_complete :
  {S|prog}{p|STATE.Pred}{q|Rel STATE STATE}
  {semant:VDMSem VDMNone p S q}VDMder VDMNone p S q;

...

B.3 Local Variables

Module state Import DepUpdate;
[VAR|DecSetoid]
[sort|SORT|VAR];
(*  Corresponds to Definition 3.20 on page 79. *)
[STATE [sort:SORT|VAR] = FS sort];
[lookup [sigma:STATE sort] = sigma];
[STATE_update = FS_update]
[STATE_lookup_update = FS_lookup_update]
[STATE_update_dyn = FS_update_dyn]
[STATE_update_dyn' = FS_update_dyn']
[STATE_update_dyn'_lookup = FS_update_dyn'_lookup]
[STATE_lookup_update_dyn = FS_lookup_update_dyn];
[STATE_update_dyn_dyn' = FS_update_dyn_dyn'];
(*  Corresponds to Definition 3.21 on page 79. *)
[expression [sort:SORT|VAR][T:Type] = (STATE sort) -> T];
[eval [T|Type][sigma:STATE sort][t:expression sort T] = t sigma];
Discharge VAR;
[STATE_update_dyn'_update_dyn
  = FS_update_dyn'_update_dyn
  : {VAR|DecSetoid}{sort|SORT|VAR}
     {y:VAR.DecSetoid_carrier}{t:sort(y)}{U|Type(0)}
     {u:U}{sigma:STATE sort}
     Eq (STATE_update_dyn' y t (STATE_update_dyn y u sigma))
        (STATE_update y t sigma)];

[STATE_update_dyn_lookup
  = FS_update_dyn_lookup
  : {VAR|DecSetoid}{sort|SORT|VAR}
     {y:VAR.DecSetoid_carrier}{U|Type(0)}
     {v:U}{sigma:STATE sort}
     Eq (STATE_update_dyn y v sigma y)
        (coerce (Eq_sym (sort_update_lookup y U sort)) v)];

Module assertion Import state;
[VAR|DecSetoid];
(*  Corresponds to Definition 3.22 on page 79. *)
[Assertion [T:Type][vardecl:VAR.DecSetoid_carrier->Type]
  = T -> (STATE vardecl) -> Prop]
[vardecl|VAR.DecSetoid_carrier->Type][T,U|Type]
[UpdateAssertion
  [x:VAR.DecSetoid_carrier][t:expression vardecl (vardecl x)]
  [p:Assertion T vardecl]
  = [z:T][sigma:STATE vardecl]
     [sigma' = STATE_update x (t sigma) sigma]p z sigma']
[ExAssertion [P:U->Assertion T vardecl]
  = [z:T][sigma:STATE vardecl]Ex ([u:U]P u z sigma)];
[KSC [p,q:Assertion T vardecl][p1,q1:Assertion U vardecl]
  = {z|T}{sigma|STATE vardecl}(p z sigma) ->
      Ex [z1:U](p1 z1 sigma ∧
       ({tau:STATE vardecl}(q1 z1 tau) -> q z tau))];
Discharge VAR;

Module syntax Import lib_bool state;
[VAR|DecSetoid];
(*  Corresponds to Definition 3.23 on page 80. *)
Inductive [prog:{sort:SORT(VAR)}Type]
Constructors
  [skip : {sort:SORT(VAR)}prog sort]
  [assign
     : {sort|SORT(VAR)}
       {x:VAR.DecSetoid_carrier}{t:expression sort (sort x)}prog sort]
  [seq : {sort|SORT(VAR)}{S1,S2:prog(sort)}prog(sort)]
  [ifthenelse
     : {sort|SORT(VAR)}
       {b:expression sort bool}{S1,S2:prog sort}prog sort]
  [while
     : {sort|SORT(VAR)}
       {b:expression sort bool}{body:prog sort}prog sort]
  [new
     : {sort|SORT(VAR)}{T|Type}
        {x:VAR.DecSetoid_carrier}{t:expression sort T}
        {S:prog (sort_update x T sort)}prog sort];
Goal ifloop

  : {T|Type}{sort|SORT(VAR)}
     {S:prog sort}{then:T}{else:T}T;

...

Goal ifassign
  : {T|Type}{sort|SORT(VAR)}
     {S:prog sort}{then:T}{else:T}T;

...

Goal prog_domain
  : {sort|SORT(VAR)}{S:prog sort}Type;

...

[P : {sort|SORT(VAR)}{S:prog sort}Prop];
Goal prog_predicate
  : {sort|SORT(VAR)}{S:prog sort}Prop;

...

Discharge P;
Goal assign_update :
  {T|Type}{sort|SORT(VAR)}
  {S:prog sort}{y:VAR.DecSetoid_carrier}{sort|SORT(VAR)}{u:expression sort (sort y)}
  {f:{sort|SORT(VAR)}
      {x:VAR.DecSetoid_carrier}{t:expression sort (sort x)}T}
  T;

...

Goal prog_assign_injective' :
   {sort|SORT(VAR)}{ix0,iy0|VAR.DecSetoid_carrier}
   {ix1|expression sort (sort ix0)}{iy1|expression sort (sort iy0)}
   (Eq  (assign iy0 iy1) (assign ix0 ix1))->
   {P:{sort|SORT(VAR)}
       {ix0:VAR.DecSetoid_carrier}{ix1:expression sort (sort ix0)}Prop}
   (P ix0 ix1) -> P iy0 iy1;

...

Goal loop_extract_b
  : {sort|SORT(VAR)}{default:expression sort bool}
     {S:prog sort}expression sort bool;

...

Goal loop_extract_body
  : {sort|SORT(VAR)}{S:prog sort}prog sort;

...

Discharge VAR;

Module operate Import syntax;
[VAR|DecSetoid];
(*  Corresponds to Definition 3.24 on page 80. *)
Inductive
  [prog_operate
     : {vardecl|VAR.DecSetoid_carrier->Type}
       {sigma:STATE vardecl}{S:prog vardecl}{tau:STATE vardecl}Prop]
Relation NoReductions Inversion
Constructors
  [skip_operate
     : {vardecl|VAR.DecSetoid_carrier->Type}{sigma:STATE vardecl}
       prog_operate sigma (skip vardecl) sigma]
  [assign_operate

: {vardecl|VAR.DecSetoid_carrier->Type}
{x:VAR.DecSetoid_carrier}{t:expression vardecl (vardecl x)}
{sigma:STATE vardecl}
prog_operate sigma (assign x t) (STATE_update x (t sigma) sigma)]
[seq_operate
: {vardecl|VAR.DecSetoid_carrier->Type}{S1,S2:prog vardecl}
{sigma,tau,eta:STATE vardecl}
(prog_operate sigma S1 tau) ->
(prog_operate tau S2 eta)->
prog_operate sigma (seq S1 S2) eta]
[ifthen_operate
: {vardecl|VAR.DecSetoid_carrier->Type}
{b:expression vardecl bool}{S1,S2:prog vardecl}
{sigma,tau:STATE vardecl}
(is_true (b (sigma))) ->
(prog_operate sigma S1 tau) ->
prog_operate sigma (ifthenelse b S1 S2) tau]
[ifelse_operate
: {vardecl|VAR.DecSetoid_carrier->Type}
{b:expression vardecl bool}{S1,S2:prog vardecl}
{sigma,tau:STATE vardecl}
(is_false (b (sigma))) ->
(prog_operate sigma S2 tau) ->
prog_operate sigma (ifthenelse b S1 S2) tau]
[while_exit_operate
: {vardecl|VAR.DecSetoid_carrier->Type}
{b:expression vardecl bool}{body:prog vardecl}
{sigma:STATE vardecl}
(is_false (b (sigma))) ->
prog_operate sigma (while b body) sigma]
[while_body_operate
: {vardecl|VAR.DecSetoid_carrier->Type}
{b:expression vardecl bool}{body:prog vardecl}
{sigma,tau,eta:STATE vardecl}
(is_true (b (sigma))) ->
(prog_operate sigma body tau) ->
(prog_operate tau (while b body) eta) ->
prog_operate sigma (while b body) eta]
[new_operate
: {vardecl|VAR.DecSetoid_carrier->Type}{T|Type}
{x:VAR.DecSetoid_carrier}{t:expression vardecl T}
{S:prog (sort_update x T vardecl)}
{sigma|STATE vardecl}{tau|STATE (sort_update x T vardecl)}
(prog_operate (STATE_update_dyn x (t sigma) sigma) S tau) ->
prog_operate sigma (new x t S) (STATE_update_dyn' x (sigma x) tau)];
Discharge VAR;

Module operate_thms Import operate lib_bool_thms;
[VAR|DecSetoid];
Goal oper_Inversion
: {vardecl|(DecSetoid_carrier VAR)->Type}
{S:prog vardecl}{sigma,tau:STATE vardecl}Prop;

...

Goal oper_inversion
: {sort|VAR.DecSetoid_carrier->Type}
{sigma|STATE sort}{S|prog sort}{tau|STATE sort}
{D:prog_operate sigma S tau}(oper_Inversion S sigma tau);

...

Goal oper_inversion_ifthenelse :
{sort|VAR.DecSetoid_carrier->Type}{c|bool}
{b|expression sort bool}{S1,S2|prog sort}{sigma,tau|STATE sort}
(Eq (b sigma) c)->(prog_operate sigma (ifthenelse b S1 S2) tau) ->

prog_operate sigma (if c S1 S2) tau;

...

Goal oper_inversion_while :
{sort|VAR.DecSetoid_carrier->Type}
{c|bool}{b|expression sort bool}{S|prog sort}{sigma,tau|STATE sort}
(Eq (b sigma) c)->(prog_operate sigma (while b S) tau) ->
if c
   (Ex [rho:STATE sort]and (prog_operate sigma S rho)
                         (prog_operate rho (while b S) tau))
   (Eq sigma tau);

...

Goal oper_determ :
{sort|VAR.DecSetoid_carrier->Type}
{sigma|STATE sort}{S|prog sort}{tau|STATE sort}
{Prem_tau|prog_operate sigma S tau}{C:(STATE sort)->Prop}
{eta:STATE sort}
{Prem_eta:prog_operate sigma S eta}
{C_eta:C eta}C tau;

...

Discharge VAR;

Module termination Import operate_thms;
[VAR|DecSetoid][sort|SORT|VAR];
[Termin [S:prog sort] =
[sigma:STATE sort]
Ex [tau:STATE sort]prog_operate sigma S tau : (STATE sort).Pred];
Goal seq_termin1 :
{S1,S2|prog sort}{sigma|STATE sort}(Termin (seq S1 S2) sigma) ->
Termin S1 sigma;

...

Goal seq_termin2 :
{S1,S2|prog sort}{sigma,tau|STATE sort}
(Termin (seq S1 S2) sigma) -> (prog_operate sigma S1 tau) ->
Termin S2 tau;

...

Goal ifthenelse_termin :
{b|expression sort bool}{S1,S2|prog sort}{sigma|STATE sort}{c|bool}
{eq:Eq (b sigma) c}
(Termin (ifthenelse b S1 S2) sigma) ->
if c (Termin S1 sigma) (Termin S2 sigma);

...

Goal while_termin :
{b|expression sort bool}{S|prog sort}{sigma,tau|STATE sort}
(Termin (while b S) sigma) ->
(is_true (b sigma)) ->
(prog_operate sigma S tau) ->
Termin (while b S) tau;

...

Goal while_termin_body :
{b|expression sort bool}{S|prog sort}{sigma|STATE sort}
(is_true (b sigma)) -> (Termin (while b S) sigma) -> Termin S sigma;

...

Discharge VAR;

Module WF Import TransClosure termination syntax;

[VAR|DecSetoid][sort|SORT|VAR];
[b:expression(sort)(bool)][body:prog(sort)];
[sofar
  [sort|SORT|VAR][b:expression(sort)(bool)][body:prog(sort)]
  [tau,sigma:STATE sort]
   = TransClosure
         ([sigma,tau:STATE(sort)](is_true (b sigma)) ∧
                       (prog_operate sigma body tau)) sigma tau];
Goal WF_sofar :
  WF ([tau,sigma:STATE(sort)](Termin (while b body) sigma ∧
                                     sofar b body tau sigma));

...

Discharge VAR;

Module H_AuxVar Import lib_rel lib_nat assertion operate;
[VAR|DecSetoid];
[Assertion_compress
  [T,U|Type][vardecl|VAR.DecSetoid_carrier->Type][x|VAR.DecSetoid_carrier]
  [P:Assertion T vardecl][v:vardecl x]
   = [z:T][sigma:STATE (sort_update x U vardecl)]
      P z (STATE_update_dyn' x v sigma)];
(*  Corresponds to Figure 3.5 on page 82. *)
Inductive
  [HD : {T|Type}{vardecl|VAR.DecSetoid_carrier->Type}
{P:Assertion T vardecl}{S:prog vardecl}{Q:Assertion T vardecl}Prop]
Relation NoReductions
Constructors
  [HDSkip :
     {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}{p:Assertion T vardecl}
     HD p (skip vardecl) p]
  [HDAssign
     : {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}
       {x:VAR.DecSetoid_carrier}{t:expression vardecl (vardecl x)}
       {p:Assertion T vardecl}
       HD (UpdateAssertion x t p) (assign x t) p]
  [HDSeq
     : {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}
       {p,r,q|Assertion T vardecl}{S1,S2:prog vardecl}
       (HD p S1 r) -> (HD r S2 q) -> HD p (seq S1 S2) q]
  [HDIfthenelse :
     {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}{pre,post|Assertion T vardecl}
     {b|expression vardecl bool}{TH,EL:prog vardecl}
     (HD ([z:T][sigma:STATE vardecl]and (pre z sigma)
                        (is_true (b (sigma))))
         TH post) ->
     (HD ([z:T][sigma:STATE vardecl]
          and (pre z sigma)
      (is_false (b (sigma))))
         EL post) ->
     HD pre (ifthenelse b TH EL) post]
  [HDWhile :
   {T,W|Type}{vardecl:VAR.DecSetoid_carrier->Type}
   {Lt':Rel W W}{is_wf : WF Lt'}
   {u:expression(vardecl)(W)} (* convergence function *)
   {p:Assertion(T)(vardecl)}{b:expression(vardecl)(bool)}{body:prog vardecl}
   {While_step:{t:W}

      HD ([Z:T][sigma:STATE vardecl]
          (p Z sigma) ∧ (is_true(b sigma)) ∧ (Eq (u sigma) t))
         body ([Z:T][tau:STATE vardecl](p Z tau) ∧ (Lt' (u tau) t))}
   HD p (while b body) ([Z:T][tau:STATE vardecl](p Z tau) ∧ (is_false(b tau)))]
  [HDNew :
     {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}{p,q|Assertion T vardecl}
     {x:VAR.DecSetoid_carrier}{U|Type}{t|expression vardecl U}
     {S|prog (sort_update x U vardecl)}
     {HDNewPremiss:
       {v:vardecl x}
       HD ([z:T][sigma:STATE (sort_update x U vardecl)]
         (Assertion_compress p v z sigma) ∧
         (Eq (coerce (sort_update_lookup x U vardecl) (sigma x))
              (t (STATE_update_dyn' x v sigma))))
         S (Assertion_compress q v)}
      HD p (new x t S) q]
  [HDConsequence
     : {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}{p,q:Assertion T vardecl}
       {T1|Type}{p1,q1:Assertion T1 vardecl}
       {S:prog vardecl}
       {ConsSC: KSC p q p1 q1}
       {ConsPrem:HD p1 S q1} HD p S q];

Discharge VAR;

Module Htheorems Import H_AuxVar;
[VAR|DecSetoid][sort|SORT|VAR];
Goal HoareConsequenceLeft :
  {T|Type}
  {pre_s : Assertion T sort}{pre,post:Assertion T sort}{S:prog(sort)}
  {strengthen : {z:T}{sigma:STATE(sort)}(pre_s z sigma) -> pre z sigma}
  {prem : HD pre S post}
  HD pre_s S post;

...

Goal HDSkipBU :
  {T|Type}{p,q:Assertion T sort}
  ({z:T}{sigma:STATE(sort)}(p z sigma)->q z sigma) -> HD p (skip ?) q;

...

Goal HDAssignBU :
  {x:VAR.DecSetoid_carrier}{t:expression(sort) (sort x)}{T|Type}
  {p,p':Assertion T sort}
  {p'p:{z:T}{sigma:STATE(sort)}
        (p' z sigma) ->  (compose (p z) (STATE_update x (t sigma)) sigma)}
  HD p' (assign x t) p;

...

Discharge VAR;

(* Semantics of Hoare logic*)
Module Hsemantics Import assertion operate;
[VAR|DecSetoid][T|Type][vardecl|VAR.DecSetoid_carrier->Type]
[HSem [p:Assertion T vardecl][S:prog vardecl][q:Assertion T vardecl]
  = {z|T}{sigma|STATE vardecl}
     (p z sigma) ->
     Ex [tau:STATE vardecl]and (prog_operate sigma S tau) (q z tau)];
Discharge VAR;

(* Soundness of Hoare logic *)
Module Hsound Import Hsemantics H_AuxVar operate_thms;
[VAR|DecSetoid];
(** Our new consequence rule preserves soundness **)
$Goal HDConsequencesoundness :
  {T|Type}{vardecl|(DecSetoid_carrier VAR)->Type}{p,q:Assertion T vardecl}
  {T1|Type}{p1,q1:Assertion T1 vardecl}
  {S:prog vardecl}
  {ConsSC:KSC p q p1 q1}
  {ConsPrem:HSem p1 S q1}HSem p S q;

...

(*  Corresponds to Theorem 3.25 on page 81. *)
Goal Hsoundness :
  {T|Type}{vardecl|VAR.DecSetoid_carrier->Type}
  {P|Assertion T vardecl}{S|prog vardecl}{Q|Assertion T vardecl}
  {der:HD P S Q}HSem P S Q;

...

Discharge VAR;

Module HAuxComplete Import operate_thms Hsemantics WF Htheorems;
[VAR|DecSetoid];
Freeze Eq;
(*  Corresponds to Theorem 3.26 on page 83. *)
Goal HL_most_general :
   {sort|SORT|VAR}{S:prog(sort)}
     HD ([Z,sigma:STATE(sort)]prog_operate sigma S Z)
        S (Eq|(STATE(sort)));

...

(*  Corresponds to Corollary 3.27 on page 83. *)
Goal HL_complete :
  {sort|SORT|VAR}{S|prog(sort)}{T|Type}{p,q|Assertion T sort}
  {semant:HSem p S q}HD p S q;

...

Discharge VAR;

Module Hlc Import Hsound HAuxComplete;
[VAR:DecSetoid];
(*  Corresponds to Corollary 3.28 on page 84. *)
Goal HDBlocksLC :
  {T|Type}{vardecl:VAR.DecSetoid_carrier->Type}
  {x:VAR.DecSetoid_carrier}{U|Type}
  [vardecl' = sort_update x U vardecl]
  {p|Assertion T vardecl'}{q|Assertion T vardecl}
  {t|expression vardecl U}
  {S|prog vardecl'}
  {Prem:{v:vardecl x}
     HD p S ([Z:T][tau:STATE vardecl']q Z (STATE_update_dyn' x v tau))}
  HD ([Z:T][sigma:STATE vardecl]
        p Z (STATE_update_dyn x (eval sigma t) sigma))
      (new x t S) q;
intros; Refine HL_complete; Expand HSem;
intros; Refine Hsoundness (Prem (sigma x)); Assumption +2;
intros tau tau_details; andE tau_details;
Refine ExIn; Refine -0 pair; Refine +1 new_operate;
Immed;

Save HDBlocksLC;
Discharge VAR;

Module VDM Import lib_rel syntax;
[VAR|DecSetoid][vardecl|VAR.DecSetoid_carrier->Type];
[VDM_Pred_to_Rel
[P:Pred (STATE vardecl)] =
[sigma,_:STATE vardecl]P sigma : Rel (STATE vardecl) (STATE vardecl)];
[reflclose [R:Rel (STATE vardecl) (STATE vardecl)] =
[tau,sigma:STATE vardecl](R tau sigma) ∨ (Eq tau sigma)];
Discharge vardecl;
(*  Corresponds to Figure 3.7 on page 86. *)
Inductive
[VDMder : {vardecl|VAR.DecSetoid_carrier->Type}
         {P:Pred (STATE vardecl)}{S:prog vardecl}
{Q:Rel (STATE vardecl) (STATE vardecl)}Prop]
Relation NoReductions
Constructors
[VDMSkip :
{vardecl|VAR.DecSetoid_carrier->Type}
VDMder ([_:(STATE vardecl)]trueProp) (skip vardecl)
        ([sigma,tau:(STATE vardecl)]Eq tau sigma)]
[VDMAssign :
{vardecl|VAR.DecSetoid_carrier->Type}
{x:VAR.DecSetoid_carrier}{t:expression vardecl (vardecl x)}
VDMder ([_:(STATE vardecl)]trueProp)
        (assign x t)
([tau,sigma:(STATE vardecl)]
 Eq tau (STATE_update x (t(sigma)) sigma))]
[VDMSeq :
{vardecl|VAR.DecSetoid_carrier->Type}
{p1,p2:(STATE vardecl).Pred}
{r1,r2:Rel (STATE vardecl) (STATE vardecl)}{S1,S2:prog vardecl}
(VDMder p1 S1 [tau,sigma:(STATE vardecl)]and (p2 tau) (r1 tau sigma)) ->
(VDMder p2 S2 r2) ->
VDMder p1 (seq S1 S2) (composeRel r2 r1)]
[VDMIfthenelse :
{vardecl|VAR.DecSetoid_carrier->Type}
{pre:(STATE vardecl).Pred}{post:Rel (STATE vardecl) (STATE vardecl)}
{b:expression vardecl bool}{TH,EL:prog vardecl}
(VDMder ([sigma:(STATE vardecl)]
  (pre sigma) ∧ (is_true (eval sigma b)))
         TH post) ->
(VDMder ([sigma:(STATE vardecl)]
  (pre sigma) ∧ (is_false (eval sigma b)))
         EL post) ->
VDMder pre (ifthenelse b TH EL) post]
[VDMWhile :
{vardecl|VAR.DecSetoid_carrier->Type}
{P:(STATE vardecl).Pred}
{sofar:Rel (STATE vardecl) (STATE vardecl)}
 {sofar_trans: trans sofar}
 {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
{b:expression vardecl bool}{body:prog vardecl}
{Prem:VDMder ([sigma:(STATE vardecl)]
(P sigma) ∧ (is_true (eval sigma b)))
              body

       ([tau,sigma:(STATE vardecl)](P tau) ∧ (sofar tau sigma))}
 VDMder P (while b body)
         ([tau,sigma:(STATE vardecl)]
  (P tau) ∧ (is_false (eval tau b)) ∧
  (reflclose sofar tau sigma))]
[VDMNew :
  {vardecl:VAR.DecSetoid_carrier->Type}
  {p|Pred (STATE vardecl)}{q|Rel (STATE vardecl) (STATE vardecl)}
  {x:VAR.DecSetoid_carrier}{U|Type}{t|expression vardecl U}
  {S|prog (sort_update x U vardecl)}
  {HDNewPremiss:
     {v:vardecl x}
     VDMder ([sigma:STATE (sort_update x U vardecl)]
      p (STATE_update_dyn' x v sigma) ∧
       (Eq (coerce (sort_update_lookup x U vardecl) (sigma x))
           (t (STATE_update_dyn' x v sigma))))
       S ([tau,sigma:STATE (sort_update x U vardecl)]
  q (STATE_update_dyn' x v tau)
     (STATE_update_dyn' x v sigma))}
   VDMder p (new x t S) q]
[VDMConsequence :
  {vardecl|VAR.DecSetoid_carrier->Type}
  {p,p1:Pred (STATE vardecl)}{q,q1:Rel (STATE vardecl) (STATE vardecl)}
  {S:prog vardecl}
  {ConsPre:{sigma|(STATE vardecl)}(p sigma) -> p1 sigma}
  {ConsPost:{tau,sigma|(STATE vardecl)}(p sigma) ->
              (q1 tau sigma) -> q tau sigma}
  {ConsPrem:VDMder p1 S q1}
  VDMder p S q];
Discharge VAR;

Module VDMtheorems Import VDM;
[VAR|DecSetoid][vardecl|VAR.DecSetoid_carrier->Type];
Goal VDMPre :
{pre|Pred (STATE vardecl)}{S|prog vardecl}
{post|Rel (STATE vardecl) (STATE vardecl)}
{Prem:VDMder pre S post}
VDMder pre S
  [tau,sigma:(STATE vardecl)]and (pre sigma) (post tau sigma);

...

Goal VDMConsequenceLeft :
{pre_s,pre : Pred (STATE vardecl)}
{post:Rel (STATE vardecl) (STATE vardecl)}{S:prog vardecl}
{strengthen : {sigma:(STATE vardecl)}(pre_s sigma) -> pre sigma}
{prem : VDMder pre S post}
VDMder pre_s S post;

...

Goal VDMConsequenceRight:
{pre : Pred (STATE vardecl)}
{post,post_w:Rel (STATE vardecl) (STATE vardecl)}{S:prog vardecl}
{weaken :
  {tau,sigma:(STATE vardecl)}(post tau sigma) -> post_w tau sigma}
{prem : VDMder pre S post}
VDMder pre S post_w;

...

Goal VDMSkipBU :
{p:Rel (STATE vardecl) (STATE vardecl)}{p':Pred (STATE vardecl)}
{p'p:{sigma:(STATE vardecl)}(p' sigma) -> p sigma sigma}
VDMder p' (skip ?) p;

...

Goal VDMAssignBU :
{x:VAR.DecSetoid_carrier}{t:expression vardecl (vardecl x)}
{p:Rel (STATE vardecl) (STATE vardecl)}{p':Pred (STATE vardecl)}
{p'p:
  {sigma:(STATE vardecl)}(p' sigma) ->
  p (STATE_update x (t sigma) sigma) sigma}
VDMder p' (assign x t) p;

...

Goal VDMSeqBU :
{p1,p2:Pred (STATE vardecl)}
{r,r1,r2:Rel (STATE vardecl) (STATE vardecl)}{S1,S2:prog vardecl}
{weakening: SubRel (composeRel r2 r1) r}
(VDMder p1 S1 [tau,sigma:(STATE vardecl)]and (p2 tau) (r1 tau sigma)) ->
(VDMder p2 S2 r2) ->
VDMder p1 (seq S1 S2) r;

...

Goal VDMLoopBU :
 {P,I:Pred (STATE vardecl)}
 {Q,sofar:Rel (STATE vardecl) (STATE vardecl)}
   {sofar_trans: trans sofar}
   {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel I))}
 {b:expression vardecl bool}{body:prog vardecl}
 {ScPre:SubPred P I}
 {ScPost:{tau,sigma:(STATE vardecl)}(I sigma) -> (I tau) ->
          (is_false (eval tau b)) ->
 (reflclose sofar tau sigma) -> Q tau sigma}
 {Prem:
   VDMder ([sigma:(STATE vardecl)]
     (I sigma) ∧ (is_true (eval sigma b)))
                body
   ([tau,sigma:(STATE vardecl)](I tau) ∧ (sofar tau sigma))}
 VDMder P (while b body) Q;

...

Discharge VAR;

Module VDMsemantics Import lib_rel operate;
[VAR|DecSetoid][vardecl|SORT VAR];
[VDMSem
[p:Pred (STATE vardecl)]
[S:prog vardecl]
[r:Rel (STATE vardecl) (STATE vardecl)]
= {sigma:STATE vardecl}
  (p sigma) ->
Ex [tau:STATE vardecl]and (prog_operate sigma S tau) (r tau sigma)];
Discharge VAR;

(* Soundness of VDM's decomposition rules *)
Module VDMsound Import VDMsemantics VDM operate_thms;
[VAR|DecSetoid];
(*  Corresponds to Theorem 3.29 on page 85. *)
Goal VDMsoundness :
{vardecl|VAR.DecSetoid_carrier->Type}
{P|Pred (STATE vardecl)}
{S|prog vardecl}
{Q|Rel (STATE vardecl) (STATE vardecl)}
{der:VDMder P S Q}VDMSem P S Q;

...

Discharge VAR;

Module VDMcomplete Import VDMsemantics VDMtheorems termination WF;
[VAR|DecSetoid];
[vardecl|SORT VAR][b:expression vardecl bool][body:prog vardecl];
[VDM_Most_General [S:prog vardecl] =
  VDMder (Termin S) S ([tau,sigma:STATE vardecl]prog_operate sigma S tau)];
$Goal VDMWFsofar :
  WF (andRel (sofar b body) (VDM_Pred_to_Rel (Termin (while b body))));

...

Freeze Eq;
Goal VDM_most_general_loop :
  {ih:VDM_Most_General body}VDM_Most_General (while b body);

...

Discharge vardecl;
Goal VDM_most_general :
  {vardecl|VAR.DecSetoid_carrier->Type}{S:prog vardecl}
  VDM_Most_General S;

...

(*  Corresponds to Theorem 3.30 on page 85. *)
Goal VDM_complete :
  {vardecl|SORT VAR}
  {S|prog vardecl}
  {p|(STATE vardecl).Pred}{q|Rel (STATE vardecl) (STATE vardecl)}
  {semant:VDMSem p S q}VDMder p S q;

...

Discharge VAR;

B.4 Recursive Procedures and Static Binding

Module state Import lib_bool_funs DepUpdate;

[VAR|DecSetoid];
[gsort:SORT|VAR];
Record [STATE_internal:Type]
Parameters [lsort:SORT|VAR] Theorems
Fields [global_internal : FS gsort][local_internal : FS lsort];
[Env_internal = VAR.DecSetoid_carrier->bool]
[E:Env_internal];
[Env_internal_empty = [x:VAR.DecSetoid_carrier]false : Env_internal];
[Env_add [f:Env_internal][y:VAR.DecSetoid_carrier]
  = [x:VAR.DecSetoid_carrier]if (VAR.DecSetoid_eq x y) true (f x)
     : Env_internal];
Goal Env_add_lookup :
  {x:VAR.DecSetoid_carrier}
  Eq (Env_add E x x) true;

...

(*  Corresponds to Definition 3.32 on page 88. *)
[STATE = STATE_internal];
[x:VAR.DecSetoid_carrier]
[if_local [T|Type][t,e:T] = if (E x) t e];

[AccSort = if_local (lsort x) (gsort x)];
Goal STATE_update_internal : {t:AccSort}{sigma:STATE}STATE;
Expand AccSort; Expand if_local;
Induction (E x);
  (** updating local component **)
  intros; Refine make_STATE_internal sigma.global_internal;
     Refine FS_update x t sigma.local_internal;
  (** updating global component **)
  intros; Refine make_STATE_internal ? sigma.local_internal;
     Refine FS_update x t sigma.global_internal;
Save STATE_update_internal;
Discharge x;
(* Corresponds to Definition 3.33 on page 89.*)
[expression [sort:SORT|VAR][T:Type] = (FS sort) -> T];
Goal lookup_internal : {sigma:STATE}{x:VAR.DecSetoid_carrier}(AccSort x);
intros; Expand AccSort; Expand if_local;
Induction (E x);
  Refine sigma.local_internal x;
  Refine sigma.global_internal x;
Save lookup_internal;
[T|Type][sigma:STATE][t:expression AccSort T]
[eval_internal  = t (lookup_internal sigma)];
[STATE_update_local_internal [x:VAR.DecSetoid_carrier][v:lsort x]
  = make_STATE_internal sigma.global_internal
                           (FS_update x v sigma.local_internal)
  : STATE];
Discharge E;
Goal Env_empty_global :
  {x:VAR.DecSetoid_carrier}Eq (gsort x) (AccSort Env_internal_empty x);

...

Discharge lsort;
[lsortA|SORT VAR];
Goal STATE_coerce_internal :
  {lsortB|SORT VAR}
  {coerce:EXTEQ lsortA lsortB}{sigma:STATE lsortA}STATE lsortB;

...

Goal STATE_coerce_redundant_internal :
  {sigma:STATE lsortA}
  Eq (STATE_coerce_internal (EXTEQrefl ?) sigma) sigma;

...

Discharge lsortA;
Goal FS_update_commutative :
  {x,y|VAR.DecSetoid_carrier}{T|Type(0)}{v:T}{lsort1|SORT VAR}
  [lsort'1 = sort_update x T lsort1]
  {prog_eq'y : Eq (lsort1 y) (lsort'1 y)}
  {E:Env_internal}{xglobal:is_false (E x)}
  {prog_eq : EXTEQ (AccSort lsort1 E) (AccSort lsort'1 E)}
  {t : expression (AccSort lsort1 E) (lsort1 y)}
  {eq:is_false (VAR.DecSetoid_eq y x)}
  {sigma1 : STATE lsort1}
  Eq (FS_update y

                 (eval_internal ? E
                   (make_STATE_internal ? (global_internal ? sigma1)
                      (FS_update_dyn x v
                        (local_internal ? sigma1)))
                   ([sigma'2:FS (AccSort lsort'1 E)]
                     Eq_subst prog_eq'y ([z'3:Type(0)]z'3)
                      (t ([var:DecSetoid_carrier VAR]
                           Eq_subst (Eq_sym (prog_eq var)) ([z'4:Type(0)]z'4)
                             (sigma'2 var)))))
                 (FS_update_dyn x v
                   (local_internal ? sigma1)))
             (FS_update_dyn x v
               (FS_update y (eval_internal ? E sigma1 t) (local_internal ? sigma1)));

...

Goal AccSort_update :
  {T:Type(0)}{E:Env_internal}{lsort:SORT|VAR}
  {x,y:VAR.DecSetoid_carrier}
  Eq (sort_update x T (AccSort lsort E) y)
      (AccSort (sort_update x T lsort) (Env_add E x) y);

...

Discharge gsort;
[gsort,lsort|SORT|VAR]
[lookup               = lookup_internal gsort lsort]
[STATE_update        = STATE_update_internal gsort lsort];
[STATE_update_local = STATE_update_local_internal gsort lsort]
[eval                 = eval_internal gsort lsort];
[global               = global_internal gsort lsort]
[local                = local_internal gsort lsort]
[make_STATE          = make_STATE_internal gsort lsort
                       : (FS gsort) -> (FS lsort) -> STATE gsort lsort];
[STATE_coerce        = STATE_coerce_internal gsort];
[STATE_coerce_redundant
  = STATE_coerce_redundant_internal gsort|lsort
  : {sigma:STATE gsort lsort}
     Eq (STATE_coerce (EXTEQrefl ?) sigma) sigma];
DischargeKeep lsort;
[E:Env_internal][sigma:STATE gsort lsort][x:VAR.DecSetoid_carrier][T|Type]
[t:expression (AccSort gsort lsort E) T];
[STATE_add_local' [v:T]
  = make_STATE sigma.global (FS_update_dyn x v sigma.local)];
[STATE_add_local = STATE_add_local'(eval E sigma t)];
[v:lsort x][tau:STATE gsort (sort_update x T lsort)]
[STATE_remove_local
  =  make_STATE tau.global (FS_update_dyn' x v tau.local)
  : STATE gsort lsort];
[STATEuL = STATE_remove_local]
[STATE_elim = STATE_internal_elim];

Discharge v;
Goal STATE_remove_add' :
  {v:T}Eq (STATE_remove_local (local sigma x) (STATE_add_local' v))

       sigma;

...

Goal STATE_remove_add :
 Eq (STATE_remove_local (local sigma x) STATE_add_local)
     sigma;

...

Discharge sigma;
Goal STATE_add_remove :
  {T|Type(0)}{x|VAR.DecSetoid_carrier}
  {sigma:STATE gsort (sort_update x T lsort)}{v:lsort x}
  Eq sigma
     (STATE_add_local' (STATE_remove_local x v sigma) x
       (coerce (sort_update_lookup x T lsort) (local sigma x)));

...

DischargeKeep lsort;
Goal STATE_coerce_add_remove :
  {T|Type(0)}{y:VAR.DecSetoid_carrier}
  {v:lsort y}[lsort' = sort_update y T lsort]{sigma:STATE gsort lsort'}
  Eq (STATE_coerce (sort_dupdate y ? T) (STATE_add_local' sigma y v))
  (STATE_remove_local y v sigma);

...

Discharge VAR;
[VAR:DecSetoid]
[Env  = Env_internal|VAR]
[Env_empty = Env_internal_empty|VAR : Env];
Discharge VAR;

Module assertion Import state;
[VAR|DecSetoid]
(*  Corresponds to Definition 3.34 on page 90. *)
[Assertion [gsort,lsort:SORT VAR][T:Type] =
  T -> (STATE gsort lsort) -> Prop];
[gsort,lsort|VAR.DecSetoid_carrier->Type][T|Type]
[Assertion_and [p,q:Assertion gsort lsort T]
  = [Z:T][sigma:STATE gsort lsort](p Z sigma) ∧ (q Z sigma)
  : Assertion gsort lsort T]
[E:VAR.DecSetoid_carrier->bool][x:VAR.DecSetoid_carrier]
$[AS = AccSort gsort lsort E]
[Assertion_update
[t:expression AS (AS x)]
  [p:Assertion gsort lsort T]
  = [Z:T][sigma:STATE gsort lsort]
      p Z (STATE_update E x (eval E sigma t) sigma)
  : Assertion gsort lsort T];
[Assertion_update_const [v:AS x]
  = Assertion_update [_:FS|?|AS]v];
[U,V|Type(0)][p',q':Assertion gsort lsort V]
$[lsort' = sort_update x U lsort];

[Fr [T|Type(0)][p:Assertion gsort lsort T][v:lsort x]
  = [Z:T][sigma:STATE gsort lsort']p Z (STATE_remove_local x v sigma)];
[Assertion_preserve [T|Type(0)][p:Assertion gsort lsort T][v:lsort x]
  = [Z:T#U][sigma:STATE gsort lsort']
  (Fr p v Z.1 sigma) ∧
  (Eq (coerce (sort_update_lookup x U lsort) (sigma.local x)) Z.2)];
[KSC [x:(Assertion gsort lsort T)#(Assertion gsort lsort T)]
      [y:(Assertion gsort lsort U)#(Assertion gsort lsort U)]
  = {Z:T}{sigma,tau:STATE gsort lsort}
     (x.1 Z sigma) ->
     Ex [Z':U]and (y.1 Z' sigma) ((y.2 Z' tau) -> x.2 Z tau)];
DischargeKeep U;
Goal KSC_refl : refl (KSC|T);

...

Goal KSC_trans : trans (KSC|T);

...

Discharge VAR;

Module syntax Import state;
(* Corresponds to Definition 3.36 on page 91.*)
Inductive [prog:{sort:SORT|VAR}Type(2)] ElimOver Type
Parameters [VAR|DecSetoid]
Constructors
  [skip : {sort:SORT|VAR}prog sort]
  [assign
     : {sort|SORT|VAR}
       {x:VAR.DecSetoid_carrier}{t:expression sort (sort x)}prog sort]
  [seq
     : {sort|SORT|VAR}{S1,S2:prog sort}prog sort]
  [ifthenelse
     : {sort|SORT|VAR}{b:expression sort bool}
       {S1,S2:prog sort}prog sort]
  [while
     : {sort|SORT|VAR}{b:expression sort bool}
       {body:prog sort}prog sort]
  [call : {sort:SORT|VAR}prog sort]
  [new
     : {sort|SORT|VAR}{T|Type(0)}
       {x:VAR.DecSetoid_carrier}{t:expression sort T}
       {S:prog (sort_update x T sort)} prog sort];
Goal ifcall :
  {sort|SORT|VAR}{S:prog sort}
  {then:Prop}{else:Prop}Prop;

...

Goal ifloop
  : {sort|SORT|VAR}{S:prog sort}{T|Type}
     {then:T}{else:T}T;

...

Goal ifblock
  : {sort|SORT|VAR}{S:prog sort}{T|Type}

     {then:T}{else:T}T;

...

Goal loop_extract_b :
  {sort|SORT|VAR}{default:expression sort bool}
  {S:prog sort} expression sort bool;

...

Goal loop_extract_body : {sort|SORT|VAR}{S:prog sort}prog sort;

...

Goal coerce_expression :
  {T|Type(0)}{VAR:DecSetoid}
  {sortA,sortB:SORT VAR}
  {coerce:{x:VAR.DecSetoid_carrier}Eq (sortA x) (sortB x)}
  {t:expression sortA T}expression sortB T;

...

Goal coerce_expression' :
  {VAR:DecSetoid}{x:VAR.DecSetoid_carrier}
  {sortA,sortB:SORT VAR}
  {coerce:{x:VAR.DecSetoid_carrier}Eq (sortA x) (sortB x)}
  {t:expression sortA (sortA x)}expression sortB (sortB x);

...

[sortA,sortB|SORT VAR];
[eq:EXTEQ sortA sortB];
Goal coerce_sort_update
  : {T:Type(0)}{x:DecSetoid_carrier VAR}
     EXTEQ (sort_update x T sortA) (sort_update x T sortB);

...

Discharge sortA;
Goal coerce_prog :
  {sortA,sortB|SORT VAR}
  {coerce:EXTEQ sortA sortB}
  {S:prog sortA}prog sortB;

...

Discharge VAR;

Module syntax_thms Import syntax lib_nat;
[VAR|DecSetoid];
[sortB|SORT VAR][n:nat][sortf:nat->SORT VAR]
[coerce_fam : {n:nat}EXTEQ (sortf n) (sortf (suc n))];
Goal expression_iter_coerce :
  {T|Type(0)}{sortA|SORT VAR}{t:expression sortA T}
  {coerce_init: EXTEQ sortA (sortf zero)}
  {coerce_fin : EXTEQ (sortf n) sortB}
  expression sortB T;

...

Goal prog_iter_coerce :
  {sortA|SORT VAR}{S:prog sortA}
  {coerce_init: EXTEQ sortA (sortf zero)}
  {coerce_fin : EXTEQ (sortf n) sortB}
  prog sortB;

...

Goal Eq_trans'
: {sortA,sortB,sortC|SORT VAR}
  {ab : EXTEQ sortA sortB}{bc : EXTEQ sortB sortC}
  EXTEQ sortA sortC;

...

Goal coerce_expression_redun :
{T|Type(0)}{sort|SORT VAR}{t:expression sort T}
Eq (coerce_expression VAR sort sort (EXTEQrefl sort) t) t;

...

Goal coerce_prog_redun :
{sort|SORT VAR}
{S:prog sort}
Eq (coerce_prog (EXTEQrefl sort) S) S;

...

[sortC:SORT VAR][c2:EXTEQ sortB sortC];
Goal coerce_expression_denest' :
{sortA:SORT VAR}{c1:EXTEQ sortA sortB}
{x:DecSetoid_carrier VAR}{t:expression sortA (sortA x)}
Eq (coerce_expression' VAR x sortB sortC c2
       (coerce_expression' VAR x sortA sortB c1 t))
   (coerce_expression' VAR x sortA sortC (Eq_trans' c1 c2) t);

...

Goal coerce_expression_denest :
{T|Type(0)}{sortA:SORT VAR}{c1:EXTEQ sortA sortB}
{t:expression sortA T}
Eq (coerce_expression VAR sortB sortC c2
       (coerce_expression VAR sortA sortB c1 t))
   (coerce_expression VAR sortA sortC (Eq_trans' c1 c2) t);

...

Discharge sortB;
Goal coerce_prog_denest :
{sortA|SORT VAR}{S:prog sortA}{sortB,sortC|SORT VAR}
{c1:EXTEQ sortA sortB}{c2:EXTEQ sortB sortC}
Eq (coerce_prog c2 (coerce_prog c1 S))
   (coerce_prog (Eq_trans' c1 c2) S);

...

Freeze Eq;
Goal prog_elim' :
{C_prog:{gsort,lsort|SORT|VAR}
{E|Env|VAR}(prog (AccSort gsort lsort E))->Type}
({gsort,lsort:SORT|VAR}{E:Env|VAR}C_prog (skip (AccSort gsort lsort E)))->
({gsort,lsort|SORT|VAR}{E|Env|VAR}
 {x:DecSetoid_carrier VAR}
 {t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
 C_prog (assign x t))->
({gsort,lsort|SORT|VAR}{E|Env|VAR}
 {S1,S2:prog (AccSort gsort lsort E)}{S1_ih:C_prog S1}{S2_ih:C_prog S2}
 C_prog (seq S1 S2))->
({gsort,lsort|SORT|VAR}{E|Env|VAR}
 {b:expression (AccSort gsort lsort E) bool}
 {S1,S2:prog (AccSort gsort lsort E)}{S1_ih:C_prog S1}
 {S2_ih:C_prog S2}C_prog (ifthenelse b S1 S2))->
({gsort,lsort|SORT|VAR}{E|Env|VAR}
 {b:expression (AccSort gsort lsort E) bool}
 {body:prog (AccSort gsort lsort E)}{body_ih:C_prog body}
 C_prog (while b body))->
 ({gsort,lsort:SORT|VAR}{E:Env|VAR}C_prog (call (AccSort gsort lsort E)))->
({gsort,lsort|SORT|VAR}{E|Env|VAR}{T|Type(0)}
 {x:DecSetoid_carrier VAR}{t:expression (AccSort gsort lsort E) T}

{S:prog (sort_update x T (AccSort gsort lsort E))}
{S_ih:C_prog (coerce_prog (AccSort_update gsort T E lsort x) S)}
C_prog (new x t S))->
{gsort,lsort|SORT|VAR}{E|Env|VAR}{z:prog (AccSort gsort lsort E)}C_prog z;

...

Discharge VAR;
Unfreeze Eq;

Module operate Import lib_bool_thms syntax;
(* Corresponds to Definition 3.37 on page 91.*)
Inductive
[prog_operate
: {lsort|SORT|VAR}{E:Env|VAR}
{sigma:STATE gsort lsort}
{S:prog (AccSort gsort lsort E)}
{tau:STATE gsort lsort}Prop]
Relation
(* Lego can not generate an Inversion theorem due to type dependency
We prove it manually in the module operate_thms                  *)
NoReductions
Parameters
[VAR|DecSetoid][gsort|SORT|VAR][S0:prog gsort]
Constructors
[skip_operate :
{lsort|SORT|VAR}{E:Env|VAR}{sigma:STATE gsort lsort}
prog_operate E sigma (skip (AccSort gsort lsort E)) sigma]
[assign_operate
: {lsort|SORT|VAR}{E:Env|VAR}{sigma:STATE gsort lsort}
{x|VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
prog_operate E sigma (assign x t)
  (STATE_update E x (eval E sigma t) sigma)]
[seq_operate
: {lsort|SORT|VAR}{E:Env|VAR}{S1,S2:prog (AccSort gsort lsort E)}
{sigma,tau,eta:STATE gsort lsort}
(prog_operate E sigma S1 tau) ->
(prog_operate E tau S2 eta) ->
prog_operate E sigma (seq S1 S2) eta]
[ifthen_operate
: {lsort|SORT|VAR}{E:Env|VAR}{b:expression (AccSort gsort lsort E) bool}
 {S1,S2:prog  (AccSort gsort lsort E)}
 {sigma,tau:STATE gsort lsort}
 (is_true (eval E sigma b)) ->
 (prog_operate E sigma S1 tau) ->
 prog_operate E sigma (ifthenelse b S1 S2) tau]
[ifelse_operate
: {lsort|SORT|VAR}{E:Env|VAR}{b:expression (AccSort gsort lsort E) bool}
{S1,S2:prog  (AccSort gsort lsort E)}
{sigma,tau:STATE gsort lsort}
(is_false (eval E sigma b)) -> (prog_operate E sigma S2 tau) ->
prog_operate E sigma (ifthenelse b S1 S2) tau]
[while_exit_operate
: {lsort|SORT|VAR}{E:Env|VAR}{b:expression (AccSort gsort lsort E) bool}
{body:prog  (AccSort gsort lsort E)}
{sigma:STATE gsort lsort}
(is_false (eval E sigma b)) ->
prog_operate E sigma (while b body) sigma]

[while_body_operate
  : {lsort|SORT|VAR}{E:Env|VAR}{b:expression (AccSort gsort lsort E) bool}
     {body:prog  (AccSort gsort lsort E)}
     {sigma,tau,eta:STATE gsort lsort}
     (is_true (eval E sigma b)) ->
     (prog_operate E sigma body tau) ->
     (prog_operate E tau (while b body) eta) ->
     prog_operate E sigma (while b body) eta]

[call_operate
  : {lsort|SORT|VAR}{E:Env|VAR}{sigma,tau:STATE gsort lsort}
     (prog_operate (Env_empty|VAR) sigma
        (coerce_prog (Env_empty_global gsort lsort) S0) tau)->
     (prog_operate E sigma (call (AccSort gsort lsort E)) tau)]

[new_operate
  : {lsort|SORT|VAR}{E:Env|VAR}{T:Type(0)}
     {x:VAR.DecSetoid_carrier}{t:expression (AccSort gsort lsort E) T}
     {S:prog (sort_update x T (AccSort gsort lsort E))}
     {sigma:STATE gsort lsort}
     {tau:STATE gsort (sort_update x T lsort)}
     (prog_operate (Env_add E x)
        (STATE_add_local E sigma x t)
 (coerce_prog (AccSort_update gsort T E lsort x) S) tau) ->
     prog_operate E sigma (new x t S)
       (STATE_remove_local x (sigma.local x) tau)];
Discharge VAR;

Module operate_thms Import lib_bool_funs operate;
[VAR|DecSetoid];
Goal oper_inversion_assign :
{sort|SORT|VAR}{x:DecSetoid_carrier VAR}{t:expression sort (sort x)}
{gsort,lsort|SORT|VAR}(prog gsort)->{E:Env|VAR}
{sort_eq:Eq sort (AccSort gsort lsort E)}{sigma:STATE gsort lsort}
{tau:STATE gsort lsort}Prop;

...

Goal oper_Inversion :
{gsort,lsort|SORT|VAR}
{S0:prog gsort}{E:Env|VAR}
{S:prog (AccSort gsort lsort E)}
{sigma,tau:STATE gsort lsort}Prop;

...

Goal oper_inversion :
{gsort,lsort|SORT|VAR}
{S0|prog gsort}{E|Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}
{D:prog_operate S0 E sigma S tau}
(oper_Inversion S0 E S sigma tau);

...

[gsort,lsort|SORT|VAR][S0|prog gsort][E|Env|VAR]
[b|expression (AccSort gsort lsort E) bool];
Goal oper_inversion_ifthenelse :
{S1,S2|prog (AccSort gsort lsort E)}{sigma,tau|STATE gsort lsort}
{c|bool}{eq:Eq (eval E sigma b) c}
(prog_operate S0 E sigma (ifthenelse b S1 S2) tau) ->
prog_operate S0 E sigma (if c S1 S2) tau;

...

Goal oper_inversion_while :
{c|bool}{S|prog (AccSort gsort lsort E)}{sigma,tau|STATE gsort lsort}
(Eq (eval E sigma b) c)->
(prog_operate S0 E sigma (while b S) tau) ->
if c (Ex [rho:STATE gsort lsort]and (prog_operate S0 E sigma S rho)
                                 (prog_operate S0 E rho (while b S) tau))
      (Eq sigma tau);

...

Discharge lsort;
Goal oper_determ :
{lsort|SORT|VAR}
{S0|prog gsort}{E|Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}
{Prem_tau|prog_operate S0 E sigma S tau}{C:(STATE gsort lsort)->Prop}
{eta:STATE gsort lsort}
{Prem_eta:prog_operate S0 E sigma S eta}
{C_eta:C eta}C tau;

...

Goal call_env_indep :
{E,E':Env|VAR}{lsort|SORT|VAR}
{S0|prog gsort}
{sigma|STATE gsort lsort}{tau|STATE gsort lsort}
[sort = AccSort gsort lsort]
{Der:prog_operate S0 E  sigma (call E.sort) tau}
prog_operate S0 E' sigma (call E'.sort) tau;

...

[lsort|SORT|VAR][x:VAR.DecSetoid_carrier][U|Type(0)]
[S0:prog gsort][E|Env|VAR][v:lsort x]
$[lsort' = sort_update x U lsort]
[sigma|STATE gsort lsort'][tau|STATE gsort lsort];
$[call_reduce_env_internal :
[S=S0]
(prog_operate S0 (Env_empty|?) (STATE_remove_local x v sigma)
   (coerce_prog (Env_empty_global gsort lsort) S) tau) ->
prog_operate S0 (Env_empty|?) sigma
  (coerce_prog (Env_empty_global gsort lsort') S)
  (STATE_add_local' tau x (coerce (sort_update_lookup x U lsort)
  (local sigma x)))];
Goal call_reduce_env :
{Prem:prog_operate S0 E (STATE_remove_local x v sigma)
          (call ?) tau}
prog_operate S0 (Env_add E x) sigma (call ?)
  (STATE_add_local' tau x
      (coerce (sort_update_lookup x U lsort) (local sigma x)));

...

Discharge VAR;

Module termination Import operate_thms lib_rel;
[VAR|DecSetoid][gsort,lsort|SORT|VAR][E:Env|VAR][S0:prog gsort];
$[myAccSort = AccSort gsort lsort E]
$[myState   = STATE gsort lsort]
[Termin [S:prog myAccSort]
= [sigma:myState]Ex [tau:myState]prog_operate S0 E sigma S tau
: myState.Pred];

Goal seq_termin1 :
  {S1,S2|prog myAccSort}{sigma|myState}
  (Termin (seq S1 S2) sigma) -> Termin S1 sigma;

...

Goal seq_termin2 :
  {S1,S2|prog myAccSort}{sigma,tau|myState}
  (Termin (seq S1 S2) sigma) -> (prog_operate S0 E sigma S1 tau) ->
  Termin S2 tau;

...

Goal ifthenelse_termin :
  {b|expression myAccSort bool}{S1,S2|prog myAccSort}{sigma|myState}{c|bool}
  {eq:Eq (eval E sigma b) c}
  (Termin (ifthenelse b S1 S2) sigma) ->
  if c (Termin S1 sigma) (Termin S2 sigma);

...

Goal while_termin :
  {b|expression myAccSort bool}{S|prog myAccSort}{sigma,tau|myState}
  (Termin (while b S) sigma) ->
  (is_true (eval E sigma b)) ->
  (prog_operate S0 E sigma S tau) ->
  Termin (while b S) tau;

...

Goal while_termin_body :
  {b|expression myAccSort bool}{S|prog myAccSort}{sigma|myState}
  (is_true (eval E sigma b)) -> (Termin (while b S) sigma) -> Termin S sigma;

...

Discharge VAR;

Module WF Import TransClosure termination syntax;
[VAR|DecSetoid][gsort|SORT|VAR][S0:prog gsort]
  [E:Env|VAR][lsort|SORT|VAR];
$[myAccSort = AccSort gsort lsort E]
$[myState   = STATE gsort lsort]
[b:expression myAccSort bool][body:prog myAccSort];
[sofar [tau,sigma:myState] =
  TransClosure
         ([sigma,tau:myState](is_true (eval E sigma b)) ∧
                       (prog_operate S0 E sigma body tau)) sigma tau];
DischargeKeep E;
Goal WF_sofar :
  WF ([tau,sigma:myState]
       (Termin E S0 (while b body) sigma ∧ sofar E b body tau sigma));

...

Discharge VAR;

Module boperate Import lib_nat_Prop_rels lib_max_min operate;
(*  Corresponds to Figure 3.9 on page 94. *)
Inductive
  [prog_boperate
     : {lsort|SORT|VAR}{E:Env|VAR}
       {sigma:STATE gsort lsort}
       {S:prog (AccSort gsort lsort E)}{tau:STATE gsort lsort}
       {n:nat}Prop]

Relation NoReductions
Parameters
[VAR|DecSetoid][gsort|SORT|VAR][S0:prog gsort]
Constructors
[skip_boperate
: {lsort|SORT|VAR}{E:Env|VAR}{sigma:STATE gsort lsort}{n:nat}
prog_boperate E sigma (skip  (AccSort gsort lsort E)) sigma n]
[assign_boperate
: {lsort|SORT|VAR}{E:Env|VAR}{sigma:STATE gsort lsort}
{x:VAR.DecSetoid_carrier}
{t:expression  (AccSort gsort lsort E) (AccSort gsort lsort E x)}
{n:nat}
prog_boperate E sigma (assign x t)
  (STATE_update E x (eval E sigma t) sigma) n]
[seq_boperate
: {lsort|SORT|VAR}{E:Env|VAR}
{S1,S2:prog (AccSort gsort lsort E)}
{sigma,tau,eta:STATE gsort lsort}{n:nat}
(prog_boperate E sigma S1 tau n) ->
(prog_boperate E tau S2 eta n)->
prog_boperate E sigma (seq S1 S2) eta n]
[ifthen_boperate
: {lsort|SORT|VAR}{E:Env|VAR}
{b:expression  (AccSort gsort lsort E) bool}
{S1,S2:prog (AccSort gsort lsort E)}{sigma,tau:STATE gsort lsort}{n:nat}
(is_true (eval E sigma b)) ->
(prog_boperate E sigma S1 tau n) ->
prog_boperate E sigma (ifthenelse b S1 S2) tau n]
[ifelse_boperate
: {lsort|SORT|VAR}{E:Env|VAR}
{b:expression  (AccSort gsort lsort E) bool}
{S1,S2:prog (AccSort gsort lsort E)}{sigma,tau:STATE gsort lsort}{n:nat}
(is_false (eval E sigma b)) ->
(prog_boperate E sigma S2 tau n) ->
prog_boperate E sigma (ifthenelse b S1 S2) tau n]
[while_exit_boperate
: {lsort|SORT|VAR}{E:Env|VAR}
 {b:expression  (AccSort gsort lsort E) bool}
 {body:prog (AccSort gsort lsort E)}{sigma:STATE gsort lsort}{n:nat}
(is_false (eval E sigma b)) ->
prog_boperate E sigma (while  b body) sigma n]
[while_body_boperate
: {lsort|SORT|VAR}{E:Env|VAR}
{b:expression  (AccSort gsort lsort E) bool}
{body:prog (AccSort gsort lsort E)}{sigma,tau,eta:STATE gsort lsort}
{n:nat}
(is_true (eval E sigma b)) ->
(prog_boperate E sigma body tau n) ->
(prog_boperate E tau (while b body) eta n) ->
prog_boperate E sigma (while b body) eta n]
[call_boperate
: {lsort|SORT|VAR}{E:Env|VAR}{sigma,tau:STATE gsort lsort}{n:nat}
(prog_boperate (Env_empty|VAR) sigma
   (coerce_prog (Env_empty_global gsort lsort) S0) tau n)->
(prog_boperate E sigma (call  (AccSort gsort lsort E)) tau (suc n))]
[new_boperate
: {lsort|SORT|VAR}{E:Env|VAR}{T:Type(0)}
{x:VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) T}
{S:prog (sort_update x T (AccSort gsort lsort E))}
{sigma:STATE gsort lsort}{n|nat}
{tau:STATE gsort (sort_update x T lsort)}
(prog_boperate (Env_add E x) (STATE_add_local E sigma x t)
  (coerce_prog (AccSort_update gsort T E lsort x) S) tau n) ->
prog_boperate E sigma (new x t S)
               (STATE_remove_local x (sigma.local x) tau) n];

Goal boperate_not_strict :
{lsort|SORT|VAR}{E:Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}{tau|STATE gsort lsort}{n|nat}
(prog_boperate E sigma S tau n) ->{m:nat}(Le n m) ->
prog_boperate E sigma S tau m;

...

(*  Corresponds to Lemma 3.38 on page 93. *)
Goal operate2boperate :
{lsort|SORT|VAR}{E:Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}
{Prem:prog_operate S0 E sigma S tau}
Ex [n:nat]prog_boperate E sigma S tau n;

...

Goal boperate2operate :
{lsort|SORT|VAR}{E:Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}{n|nat}
{Prem:prog_boperate E sigma S tau n}
       prog_operate S0 E sigma S tau;

...

Freeze Eq;
Goal boperate_zero :
{lsort|SORT|VAR}{E:Env|VAR}{sigma,tau:STATE gsort lsort}
not (prog_boperate E sigma (call  (AccSort gsort lsort E)) tau zero);

...

Discharge VAR;
Unfreeze Eq;

Module boperate_thms Import

lib_bool_funs lib_nat_suc_thms boperate operate_thms syntax_thms;
[VAR|DecSetoid][gsort|SORT|VAR][S0|prog gsort];
Goal boper_Inversion :
{lsort|SORT|VAR}{E:Env|VAR}
{S:prog (AccSort gsort lsort E)}{sigma,tau:STATE gsort lsort}{n:nat}Prop;

...

Goal boper_inversion :
{lsort|SORT|VAR}{E|Env|VAR}
{sigma|STATE gsort lsort}{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}{n|nat}
{D:prog_boperate S0 E sigma S tau n}
(boper_Inversion E S sigma tau n);

...

Goal boper_ifthen :
{lsort|SORT|VAR}{E|Env|VAR}
{b|expression (AccSort gsort lsort E) bool}
{S1,S2|prog (AccSort gsort lsort E)}{sigma,tau|STATE gsort lsort}{n|nat}
(is_true (eval E sigma b))->
(prog_boperate S0 E sigma (ifthenelse b S1 S2) tau n) ->
prog_boperate S0 E sigma S1 tau n;

...

Goal boper_ifelse :
{lsort|SORT|VAR}{E|Env|VAR}

{b|expression (AccSort gsort lsort E) bool}
{S1,S2|prog (AccSort gsort lsort E)}{sigma,tau|STATE gsort lsort}{n|nat}
(is_false (eval E sigma b)) ->
(prog_boperate S0 E sigma (ifthenelse b S1 S2) tau n) ->
prog_boperate S0 E sigma S2 tau n;

...

Goal boper_while :
{lsort|SORT|VAR}{E|Env|VAR}
{c|bool}{b|expression (AccSort gsort lsort E) bool}
{S|prog (AccSort gsort lsort E)}
{sigma,tau|STATE gsort lsort}{n|nat}
(Eq (eval E sigma b) c)->
(prog_boperate S0 E sigma (while b S) tau n) ->
(and (is_false (eval E tau b))
      (if c (Ex [rho:STATE gsort lsort]
        and (prog_boperate S0 E sigma S rho n)
             (prog_boperate S0 E rho (while b S) tau n))
             (Eq sigma tau)));

...

Goal boper_while_false :
{lsort|SORT|VAR}{E|Env|VAR}
{b|expression (AccSort gsort lsort E) bool}
{S|prog (AccSort gsort lsort E)}{sigma,tau|STATE gsort lsort}{n|nat}
(prog_boperate S0 E sigma (while b S) tau n) ->
is_false (eval E tau b);

...

(*  Corresponds to Lemma 3.39 on page 95. *)
Goal boper_determ :
{lsort|SORT|VAR}{E|Env|VAR}
{sigma|STATE gsort lsort}{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}{n|nat}
{Prem_tau|prog_boperate S0 E sigma S tau n}
{C:(STATE gsort lsort)->Prop}{eta:STATE gsort lsort}
{m|nat}{Prem_eta:prog_boperate S0 E sigma S eta m}
{C_eta:C eta}C tau;

...

DischargeKeep VAR;

Goal eval_under_coerc :
{T | Type(0)}{x : DecSetoid_carrier VAR}{lsort1 | SORT|VAR}{E1 | Env|VAR}
{x_global : is_false (E1 x)}{b : expression (AccSort gsort lsort1 E1) bool}
{sigma1 : STATE gsort lsort1}[lsort'1 = sort_update x T lsort1]
{v:T}
{prog_eq' : EXTEQ (AccSort gsort lsort1 E1) (AccSort gsort lsort'1 E1)}
Eq (eval E1 (STATE_add_local' sigma1 x v)
              (coerce_expression VAR (AccSort gsort lsort1 E1)
               (AccSort gsort lsort'1 E1) prog_eq' b))
   (eval E1 sigma1 b);

...

Goal eval_under_coerc' :
{lsort,lsort' | SORT|VAR}{E | Env|VAR}
{b : expression (AccSort gsort lsort E) bool}
{sigma : STATE gsort lsort}
{eq : EXTEQ lsort lsort'}{prog_eq : EXTEQ ??}
Eq (eval E (STATE_coerce eq sigma)
              (coerce_expression VAR (AccSort gsort lsort E)
               (AccSort gsort lsort' E) prog_eq b))
   (eval E sigma b);

...

Goal AccSort_coerce :
{lsort,lsort'|SORT VAR}{E|Env|VAR}
{eq:EXTEQ lsort lsort'}
EXTEQ (AccSort gsort lsort E) (AccSort gsort lsort' E);

...

Unfreeze Eq;
Goal boperate_coerce :
{lsort|SORT VAR}{E|Env|VAR}
{sigma|STATE gsort lsort}{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}{n|nat}
{Der:prog_boperate S0 E sigma S tau n}
{lsort':SORT VAR}{eq:EXTEQ lsort lsort'}
prog_boperate S0 E (STATE_coerce eq sigma)
  (coerce_prog (AccSort_coerce eq) S) (STATE_coerce eq tau)
  n;

...

[U|Type(0)][y:VAR.DecSetoid_carrier][v:U];
[lsort|SORT|VAR][E|Env|VAR][yGlobal:is_false (E y)];
Goal prog_coerce_preserve_local :
EXTEQ (AccSort gsort lsort E)
       (AccSort gsort (sort_update y U lsort) E);

...

Discharge lsort;
(*  Corresponds to Theorem 3.41 on page 95. *)
$Goal boper_preserve_local :
{lsort|SORT VAR}{E:Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}{n|nat}
{Prem:prog_boperate S0 E sigma S tau n}
{yGlobal:is_false (E y)}
prog_boperate S0 E (STATE_add_local' sigma y v)
  (coerce_prog (prog_coerce_preserve_local yGlobal) S)
  (STATE_add_local' tau y v) n;

...

(*  Corresponds to Lemma 3.40 on page 95. *)
Goal bcall_env_indep :
{lsort|SORT|VAR}{E,E':Env|VAR}
{sigma|STATE gsort lsort}{tau|STATE gsort lsort}{n|nat}
{Der:prog_boperate S0 E  sigma (call ?) tau n}
prog_boperate S0 E' sigma (call ?) tau n;

...

[TR : {E:Env|VAR}{lsort|SORT VAR}
        {sigma:STATE gsort lsort}
        {S:prog (AccSort gsort lsort E)}
        {tau:STATE gsort lsort}
      Prop]
[OPERLOCAL'
  = {T|Type(0)}{E|Env|VAR}{lsort|SORT VAR}{x:VAR.DecSetoid_carrier}
     [lsort' = sort_update x T lsort]
     {sigma,tau|STATE gsort lsort'}
     {v:lsort x}
     (TR (Env_add E x) sigma (call ?) tau) ->
     TR E (STATE_remove_local x v sigma) (call ?)
          (STATE_remove_local x v tau)];
[OPERLOCAL
  = {T|Type(0)}{E|Env|VAR}{lsort|SORT VAR}{x:VAR.DecSetoid_carrier}
     [lsort' = sort_update x T lsort]
     {sigma|STATE gsort lsort'}{tau|STATE gsort lsort}
     {v:lsort x}

     (TR (Env_add E x) (STATE_remove_local x v sigma) (call ?) tau) ->
     TR E sigma (call ?)
       (STATE_add_local' tau x (coerce (sort_update_lookup x T lsort)
(local sigma x)))];
Discharge VAR;

Module operate_thms2 Import boperate_thms;
[VAR|DecSetoid][gsort|SORT|VAR][S0|prog gsort];
[y:VAR.DecSetoid_carrier][T|Type(0)][v:T];
Goal oper_preserve_local :
{lsort|SORT VAR}{E:Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}
{Prem:prog_operate S0 E sigma S tau}
{yGlobal:is_false (E y)}
prog_operate S0 E (STATE_add_local' sigma y v)
  (coerce_prog (prog_coerce_preserve_local y yGlobal) S)
  (STATE_add_local' tau y v);

...

Goal operate_coerce :
{lsort|SORT VAR}{E|Env|VAR}
{sigma|STATE gsort lsort}{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}
{Der:prog_operate S0 E sigma S tau}
{lsort':SORT VAR}{eq:EXTEQ lsort lsort'}
prog_operate S0 E (STATE_coerce eq sigma)
  (coerce_prog (AccSort_coerce eq) S) (STATE_coerce eq tau);

...

Discharge T;
Unfreeze Eq;
(*  Corresponds to Corollary 3.42 on page 98. *)
Goal operate_resp_context :
{lsort|SORT|VAR}
{E|Env|VAR}
{sigma|STATE gsort lsort}
{S|prog (AccSort gsort lsort E)}
{tau|STATE gsort lsort}
{Der:prog_operate S0 E sigma S tau}
{hidden:is_false (E y)}
Eq (sigma.local y) (tau.local y);

...

Goal boper_local' :
{n|nat}
OPERLOCAL' ([E:Env|VAR][lsort|SORT VAR]
   [sigma:STATE gsort lsort]
   [S:prog (AccSort gsort lsort E)]
   [tau:STATE gsort lsort]prog_boperate S0 E sigma S tau n);

...

Goal boper_local :
{n|nat}
OPERLOCAL ([E:Env|VAR][lsort|SORT VAR]
   [sigma:STATE gsort lsort]
   [S:prog (AccSort gsort lsort E)]
   [tau:STATE gsort lsort]prog_boperate S0 E sigma S tau n);

...

Goal oper_local :
 OPERLOCAL ([E:Env|VAR][lsort|SORT VAR]
     [sigma:STATE gsort lsort]
     [S:prog (AccSort gsort lsort E)]
     [tau:STATE gsort lsort]prog_operate S0 E sigma S tau);

...

Goal oper_local' :
 OPERLOCAL' ([E:Env|VAR][lsort|SORT VAR]
     [sigma:STATE gsort lsort]
     [S:prog (AccSort gsort lsort E)]
     [tau:STATE gsort lsort]prog_operate S0 E sigma S tau);

...

Discharge VAR;

(* Contexts for Hoare Logic *)
Module Hcontext Import lib_prod lib_rel assertion;
[VAR|DecSetoid][gsort,lsort:SORT VAR]
[Spec [T:Type] =
  prod (Assertion gsort lsort T) (Assertion gsort lsort T)];
Inductive [HContext : Type] Theorems
Constructors
  [HNone:HContext]
  [HSome_internal:{T|Type}{spec:Spec T}HContext];
Goal HContext_disjoint : {T|Type}{spec:Spec T}not (Eq HNone (HSome_internal spec));

...

$Goal HContext_predicate : {P:Pred HContext}{O:HContext}Prop;

...

$Goal HSome_update :
  {T|Type}{O:HContext}{B|Type}{b:Spec B}{f:{A|Type}{a:Spec A}T}T;

...

Goal HContext_injective :
  {A,B|Type}{a|Spec A}{b|Spec B}(Eq (HSome_internal a) (HSome_internal b)) ->
  {P:{T|Type}{t:Spec T}Prop}(P b) -> P a;

...

Discharge gsort;
[gsort,lsort|SORT VAR];
[HSome = HSome_internal gsort lsort]
[HSomeSpec [T|Type][p,q:Assertion gsort lsort T] = HSome (Pair p q)];
DischargeKeep lsort;
Goal Hcontext_preserve :
  {U|Type(0)}
  {Gamma:HContext gsort lsort}{x:VAR.DecSetoid_carrier}{v:lsort x}
  HContext gsort (sort_update x U lsort);
Induction Gamma;
  intros; Refine HNone; (* empty context *)
  intros; Refine HSomeSpec;
     Refine +1 Assertion_preserve x spec.Fst v;
        Refine Assertion_preserve x spec.Snd v;
Save Hcontext_preserve;
Discharge VAR;

(* Semantics of Hoare logic *)
Module Hsemantics Import Hcontext operate;
[VAR|DecSetoid][gsort,lsort|SORT VAR]
[S0:prog gsort][E:Env|VAR][T|Type];
[HAtomicSem
[p:Assertion gsort lsort T]
[S:prog (AccSort gsort lsort E)]
[q:Assertion gsort lsort T]
= {z|T}{sigma|STATE gsort lsort}
  (p z sigma) ->
Ex [tau:STATE gsort lsort]
and (prog_operate S0 E sigma S tau) (q z tau)];
Goal HSemEx :
{U|Type}
{p:U->Assertion gsort lsort T}
{S:prog (AccSort gsort lsort E)}
{q:Assertion gsort lsort T}
iff (HAtomicSem ([Z:T][sigma:STATE gsort lsort]
  Ex [t:U]p(t)(Z)(sigma)) S q)
     ({t:U}HAtomicSem (p(t)) S q);

...

DischargeKeep T;
(*  Corresponds to Definition 3.43 on page 98. *)
Goal HSem :
{Gamma:HContext gsort lsort}
{p:Assertion gsort lsort T}
{S:prog (AccSort gsort lsort E)}
{q:Assertion gsort lsort T}
Prop;
Induction Gamma;
Refine HAtomicSem; (* empty context *)
intros;
Refine (HAtomicSem spec.Fst (call ?) spec.Snd) ->
  HAtomicSem p S q;
Save HSem;
Discharge VAR;

Module H Import Hcontext syntax;
[VAR|DecSetoid][gsort|SORT VAR][S0:prog gsort];
(*  Corresponds to Figure 3.10 on page 101. *)
Inductive
[HD :
  {E:Env|VAR}{lsort|SORT VAR}{T|Type(0)}
  {Gamma:HContext gsort lsort}
  {p:Assertion gsort lsort T}
  {S:prog (AccSort gsort lsort E)}
  {q:Assertion gsort lsort T}
  Prop]
Relation NoReductions
Constructors
[HDAssumption :
  {E:Env|VAR}{lsort|SORT|VAR}{T|Type}{p,q:Assertion gsort lsort T}
  HD E (HSomeSpec p q) p (call (AccSort gsort lsort E)) q]
[HDInv :

{E:Env|VAR}{lsort|SORT|VAR}{T|Type}
{Gamma:HContext gsort lsort}{p:Assertion gsort lsort T}
HD E Gamma p (skip (AccSort gsort lsort E)) p]
[HDAssign :
{E:Env|VAR}{lsort|SORT|VAR}{T|Type}
{Gamma:HContext gsort lsort}{p:Assertion gsort lsort T}
{x:VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
HD E Gamma (Assertion_update E x t p) (assign x t) p]
[HDSeq :
{E|Env|VAR}{lsort|SORT VAR}{T|Type}
{Gamma:HContext gsort lsort}
{p,q,r:Assertion gsort lsort T}
{S1,S2|prog (AccSort gsort lsort E)}
{Seq_prem1:HD E Gamma p S1 r}{Seq_prem2:HD E Gamma r S2 q}
HD E Gamma p (seq S1 S2) q]
[HDIfthenelse :
{E|Env|VAR}{lsort|SORT VAR}{T|Type}
{Gamma:HContext gsort lsort}
{p,q:Assertion gsort lsort T}
{S1,S2|prog (AccSort gsort lsort E)}
{b:expression (AccSort gsort lsort E) bool}
{Ifthenelse_prem1:
  HD E Gamma ([Z:T][sigma:STATE gsort lsort]
(p Z sigma) ∧ (is_true (eval E sigma b)))
S1 q}
{Ifthenelse_prem2:
  HD E Gamma ([Z:T][sigma:STATE gsort lsort]
(p Z sigma) ∧ (is_false (eval E sigma b)))
                        S2 q}
HD E Gamma p (ifthenelse b S1 S2) q]
[HDWhile :
{E|Env|VAR}{lsort|SORT VAR}{T|Type}
{Gamma:HContext gsort lsort}
{p:Assertion gsort lsort T}
{b:expression (AccSort gsort lsort E) bool}
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
{u:(STATE gsort lsort)->W} (* convergence function *)
{body:prog (AccSort gsort lsort E)}
{While_step:{t:W}
  HD E Gamma ([Z:T][sigma:STATE gsort lsort]
(p Z sigma) ∧ (is_true(eval(E)(sigma)(b))) ∧
(Eq (u sigma) t))
  body ([Z:T][tau:STATE gsort lsort](p Z tau) ∧ (Lt' (u tau) t))}
HD E Gamma p (while b body)
([Z:T][tau:STATE gsort lsort](p Z tau) ∧ (is_false(eval E tau b)))]
[HDNew :
{E|Env|VAR}{lsort|SORT|VAR}{T,U|Type}
{Gamma:HContext gsort lsort}{p,q:Assertion gsort lsort T}
{x:VAR.DecSetoid_carrier}{t:expression (AccSort gsort lsort E) U}
{S:prog (sort_update x U (AccSort gsort lsort E))}
{NewPrem :
  {v:lsort x}
  HD (Env_add E x) (Hcontext_preserve Gamma x v)
      ([Z:T][sigma:STATE gsort (sort_update x U lsort)]
(Fr x p v Z sigma) ∧
(Eq (coerce (sort_update_lookup x U lsort) (sigma.local x))
 (eval E (STATE_remove_local x v sigma) t)))
  (coerce_prog (AccSort_update gsort U E lsort x) S)
  (Fr x q v)}
HD E Gamma p (new x t S) q]
[HDCall :
{E|Env|VAR}{lsort|SORT|VAR}{T|Type}
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}

     {p|W->Assertion gsort lsort T}
     {q:Assertion gsort lsort T}
     {Call_step:{t:W}
       HD (Env_empty|VAR) (HSomeSpec
           ([Z:T][sigma:STATE gsort lsort]
     Ex [u:W](p(u)(Z)(sigma)) ∧ (Lt' u t))
   q)
       (p(t))
          (coerce_prog (Env_empty_global gsort lsort) S0) q}
   HD E (HNone gsort lsort)
       ([Z:T][sigma:STATE gsort lsort]Ex [t:W]p(t)(Z)(sigma))
       (call (AccSort gsort lsort E)) q]
  [HDCon :
     {E|Env|VAR}{lsort|SORT VAR}{T,U|Type}
     {Gamma:HContext gsort lsort}
     {p,q:Assertion gsort lsort T}{pre,post:Assertion gsort lsort U}
     {S:prog (AccSort gsort lsort E)}
     {Prem : KSC (p,q) (pre,post)}
     {prem : HD E Gamma pre S post}
     HD E Gamma p S q];
Discharge VAR;

Module Htheorems Import H;
[VAR|DecSetoid][gsort|SORT VAR][S0:prog gsort]
[E:Env|VAR][lsort|SORT VAR][T|Type]
[Gamma:HContext gsort lsort];
[p,q:Assertion gsort lsort T];
Goal HDConl :
  {pre|Assertion gsort lsort T}{S|prog (AccSort gsort lsort E)}
  {Prem: SubRel p pre}
  {prem : HD S0 E Gamma pre S q}HD S0 E Gamma p S q;

...

Goal HDConr :
  {post|Assertion gsort lsort T}{S|prog (AccSort gsort lsort E)}
  {Prem: SubRel post q}
  {prem: HD S0 E Gamma p S post}HD S0 E Gamma p S q;

...

DischargeKeep p;
Goal HDInv_lemma :  (SubRel p q) -> HD S0 E Gamma p (skip ?)  q;

...

$[sort = AccSort gsort lsort E];
Goal HDAssign_lemma :
  {x:VAR.DecSetoid_carrier}{t:expression (AccSort gsort lsort E) (sort x)}
  (SubRel q
           ([Z:T][sigma:STATE gsort lsort]
             p Z (STATE_update E x (eval E sigma t) sigma))) ->
  HD S0 E Gamma q (assign x t) p;

...

Discharge VAR;

Module Hsound Import Htheorems Hsemantics operate_thms2;
[VAR|DecSetoid][gsort|SORT|VAR][S0:prog gsort]

[E:Env|VAR][lsort|SORT|VAR][T|Type][Gamma:HContext gsort lsort]
[p,r,q:Assertion gsort lsort T]
[b:expression (AccSort gsort lsort E) bool]
[S1,S2:prog (AccSort gsort lsort E)];
Goal HDAssumptionSound :
  HSem S0 E (HSomeSpec p q) p (call (AccSort gsort lsort E)) q;

...

$Goal HDInvAtomicSound :
  HAtomicSem S0 E p (skip (AccSort gsort lsort E)) p;

...

Goal HDInvSound :
  HSem S0 E Gamma  p (skip (AccSort gsort lsort E)) p;

...

$Goal HDAssignAtomicSound :
  {x:VAR.DecSetoid_carrier}
  {t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
  HAtomicSem S0 E (Assertion_update E x t p) (assign x t) p;

...

Goal HDAssignSound :
  {x:VAR.DecSetoid_carrier}
  {t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
  HSem S0 E Gamma (Assertion_update E x t p) (assign x t) p;

...

$Goal HDSeqAtomicSound :
  {Seq_prem1:HAtomicSem S0 E p S1 r}
  {Seq_prem2:HAtomicSem S0 E r S2 q}
  HAtomicSem S0 E p (seq S1 S2) q;

...

Goal HDSeqSound :
  {Seq_prem1:HSem S0 E Gamma p S1 r}
  {Seq_prem2:HSem S0 E Gamma r S2 q}
  HSem S0 E Gamma p (seq S1 S2) q;

...

$Goal HDIfthenelseAtomicSound :
  {Ifthenelse_prem1:
       HAtomicSem S0 E ([Z:T][sigma:STATE gsort lsort]
   (p Z sigma) ∧ (is_true (eval E sigma b)))
 S1 q}
  {Ifthenelse_prem2:
     HAtomicSem S0 E ([Z:T][sigma:STATE gsort lsort]
       (p Z sigma) ∧ (is_false (eval E sigma b)))
                             S2 q}
     HAtomicSem S0 E p (ifthenelse b S1 S2) q;

...

Goal HDIfthenelseSound :
  {Ifthenelse_prem1:
       HSem S0 E Gamma ([Z:T][sigma:STATE gsort lsort]
   (p Z sigma) ∧ (is_true (eval E sigma b)))
 S1 q}
  {Ifthenelse_prem2:
     HSem S0 E Gamma ([Z:T][sigma:STATE gsort lsort]
       (p Z sigma) ∧ (is_false (eval E sigma b)))
                             S2 q}
     HSem S0 E Gamma p (ifthenelse b S1 S2) q;

...

$Goal HDWhileAtomicSound :
  {W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
  {u:(STATE gsort lsort)->W}

{body:prog (AccSort gsort lsort E)}
{While_step:{t:W}
     HAtomicSem S0 E ([Z:T][sigma:STATE gsort lsort]
 (p Z sigma) ∧ (is_true(eval(E)(sigma)(b))) ∧
 (Eq (u sigma) t))
     body ([Z:T][tau:STATE gsort lsort](p Z tau) ∧ (Lt' (u tau) t))}
 HAtomicSem S0 E p (while b body)
  ([Z:T][tau:STATE gsort lsort](p Z tau) ∧ (is_false(eval E tau b)));

...

Goal HDWhileSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
{u:(STATE gsort lsort)->W}
{body:prog (AccSort gsort lsort E)}
{While_step:{t:W}
     HSem S0 E Gamma ([Z:T][sigma:STATE gsort lsort]
 (p Z sigma) ∧ (is_true(eval(E)(sigma)(b))) ∧
 (Eq (u sigma) t))
     body ([Z:T][tau:STATE gsort lsort](p Z tau) ∧ (Lt' (u tau) t))}
 HSem S0 E Gamma p (while b body)
  ([Z:T][tau:STATE gsort lsort](p Z tau) ∧ (is_false(eval E tau b)));

...

Goal HDNewSound :
{U|Type}
{x:VAR.DecSetoid_carrier}{t:expression (AccSort gsort lsort E) U}
{S:prog (sort_update x U (AccSort gsort lsort E))}
{NewPrem :
  {v:lsort x}
  HSem S0 (Env_add E x) (Hcontext_preserve Gamma x v)
  ([Z:T][sigma:STATE gsort (sort_update x U lsort)]
     (Fr x p v Z sigma) ∧
     (Eq (coerce (sort_update_lookup x U lsort) (sigma.local x))
(eval E (STATE_remove_local x v sigma) t)))
  (coerce_prog (AccSort_update gsort U E lsort x) S)
  (Fr x q v)}
HSem S0 E Gamma p (new x t S) q;

...

Goal HDCallSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
  {p|W->Assertion gsort lsort T}
  {Call_step:{t:W}
     HSem S0 (Env_empty|VAR) (HSomeSpec
         ([Z:T][sigma:STATE gsort lsort]
  Ex [u:W](p(u)(Z)(sigma)) ∧ (Lt' u t))
 q)
     (p(t))
        (coerce_prog (Env_empty_global gsort lsort) S0) q}
 HSem S0 E (HNone gsort lsort)
     ([Z:T][sigma:STATE gsort lsort]Ex [t:W]p(t)(Z)(sigma))
     (call (AccSort gsort lsort E)) q;

...

$Goal HDConAtomicSound :
{U|Type}{pre,post:Assertion gsort lsort U}
{S:prog (AccSort gsort lsort E)}
{Prem : KSC (p,q) (pre,post)}
{prem : HAtomicSem S0 E pre S post}
  HAtomicSem S0 E p S q;

...

Goal HDConSound :
{U|Type}{pre,post:Assertion gsort lsort U}
{S:prog (AccSort gsort lsort E)}
{Prem : KSC (p,q) (pre,post)}
{prem : HSem S0 E Gamma pre S post}
  HSem S0 E Gamma p S q;

...

Discharge E;
(*  Corresponds to Theorem 3.44 on page 102. *)
Goal HSound :
{E|Env|VAR}{lsort|SORT VAR}{T|Type(0)}
{Gamma|HContext gsort lsort}
{p|Assertion gsort lsort T}
{S|prog (AccSort gsort lsort E)}
{q|Assertion gsort lsort T}
{der:HD S0 E Gamma p S q}HSem S0 E Gamma p S q;

...

Discharge VAR;

Module Hcomplete Import Hsemantics Htheorems WF operate_thms2;
[VAR|DecSetoid][gsort|SORT VAR][S0:prog gsort];
[Hoarep0
[lsort|SORT|VAR][E:Env|VAR][S:prog (AccSort gsort lsort E)][n:nat] =
[Z,sigma:STATE gsort lsort]prog_boperate S0 E sigma S Z n];
[psi [lsort:SORT|VAR][E:Env|VAR][n:nat]
= Hoarep0 E (call (AccSort gsort lsort E)) (suc n)]
[p [lsort:SORT|VAR][E:Env|VAR][n:nat][Z,sigma:STATE gsort lsort]
= Ex ([u:nat](psi lsort E u Z sigma ∧ Lt u n))];
Discharge gsort;
Freeze Eq;
(* Corresponds to Theorem 3.45 on page 104.*)
Goal completeness_step :
{gsort,lsort|SORT|VAR}
{S0:prog gsort}{n:nat}
HD S0 (Env_empty|VAR)
(HSomeSpec (p S0 lsort (Env_empty|VAR) n) (Eq|(STATE ??)))
(psi S0 lsort (Env_empty|VAR) n)
(coerce_prog (Env_empty_global gsort lsort) S0)
(Eq|(STATE gsort lsort));

...

(*  Corresponds to Theorem 3.46 on page 107. *)
Goal HL_most_general :
 {gsort,lsort|SORT|VAR} {E:Env|VAR}{S:prog (AccSort gsort lsort E)}
 {S0:prog gsort}
  HD S0 E (HNone ??) ([Z,sigma:STATE gsort lsort]
       prog_operate S0 E sigma S Z)
      S (Eq|(STATE gsort lsort));

...

(*  Corresponds to Corollary 3.47 on page 107. *)
Goal HL_complete :
{gsort,lsort|SORT|VAR}{E:Env|VAR}{S0:prog gsort}
{S|prog (AccSort gsort lsort E)}{T|Type}
{p,q|Assertion gsort lsort T}
{semant:HSem S0 E (HNone ??) p S q}HD S0 E (HNone ??) p S q;

...

Discharge VAR;
Unfreeze Eq;

Module Hadmiss Import Hsound Hcomplete;
[VAR|DecSetoid][gsort|SORT|VAR][S0:prog gsort]
[T|Type][lsort|SORT|VAR]
[E:Env|VAR][q|Assertion gsort lsort T];
(*  Corresponds to Corollary 3.48 on page 107. *)
Goal HDNRCall :
  {p|Assertion gsort lsort T}{Prem:HD S0 (Env_empty|VAR) (HNone ??) p
     (coerce_prog (Env_empty_global gsort lsort) S0) q}
  HD S0 E (HNone gsort lsort) p (call ?) q;

...

[Gamma|HContext gsort lsort][U|Type(0)];
Goal Adaptation :
  {pre,post:Assertion gsort lsort U}{S:prog ?}
  {prem : HD S0 E Gamma pre S post}
  [p [Z:T][sigma:STATE gsort lsort] =
     {tau:STATE gsort lsort}Ex [Z':U]and (pre Z' sigma)
                                      ((post Z' tau) -> q Z tau)]
  HD S0 E Gamma p S q;

...

Goal HDNew_LC :
  {x:VAR.DecSetoid_carrier}{t:expression ? U}
  [sort = sort_update x U lsort]
  {p:Assertion gsort sort T}{S:prog ?}
  {NewPrem : {v:lsort x}
     [E = Env_add E x]
     HD S0 E (Hcontext_preserve Gamma x v) p
          (coerce_prog (AccSort_update gsort ?? lsort x) S)
          (Fr x q v)}
  HD S0 E Gamma ([Z:T][sigma:STATE ??]p Z (STATE_add_local E sigma x t))
   (new x t S) q;

...

Discharge VAR;

Module static Import Hadmiss;
Inductive [myVAR:Type] Double
Constructors [X,Z:myVAR];
Goal myVAR_eq : myVAR -> myVAR -> bool;

...

Goal myVAR_eq_refl : {x:myVAR}is_true (myVAR_eq x x);

...

Goal myVAR_eq_char : Eqchar myVAR_eq;

...

Goal myVAR_DecSetoid : DecSetoid;

...

(*  Corresponds to Example 3.31 on page 87. *)
[mysort = [_:myVAR]nat : myVAR_DecSetoid.DecSetoid_carrier -> Type]
[S0 = assign|myVAR_DecSetoid X ([sigma:FS mysort]sigma(Z))
     : prog|myVAR_DecSetoid mysort];
Goal myprog : prog|myVAR_DecSetoid mysort;
Refine seq; Refine assign; Refine Z; Intros; Refine one;
Refine new; Refine +1 Z; Intros +1; Refine zero; Refine call;

Save myprog;
[E:Env|myVAR_DecSetoid]
[E' [x:myVAR_DecSetoid.DecSetoid_carrier] =
  (andalso (inv (myVAR_DecSetoid.DecSetoid_eq x X))
     (andalso (inv (myVAR_DecSetoid.DecSetoid_eq x Z)) (E x)))];
Goal myprog_coerce : EXTEQ mysort (AccSort mysort mysort E');

...

Goal Xcoerce :
  Eq (AccSort%%myVAR_DecSetoid mysort mysort E' X) (mysort X);

...

[post [Z,sigma:STATE mysort mysort] =
  Eq (coerce Xcoerce (lookup(E')(sigma)(X))) one];
(*  Corresponds to Lemma 3.49 on page 109. *)
Goal myprog_correct :
  HD S0 E' (HNone ??) ([_,_:STATE ??]trueProp)
  (coerce_prog myprog_coerce myprog) post;

...

(* Contexts for VDM *)
Module VDMcontext Import lib_prod lib_rel state;
[VAR|DecSetoid][gsort,lsort:SORT VAR]
[VDMSpec =
  prod (Pred (STATE gsort lsort))
        (Rel (STATE gsort lsort) (STATE gsort lsort))];
Inductive [VDMContext : Type] Theorems
Constructors
  [VDMNone:VDMContext]
  [VDMSome_internal:{spec:VDMSpec}VDMContext];
Discharge gsort;
[gsort,lsort|SORT VAR];
[VDMSome = VDMSome_internal gsort lsort]
[VDMSomeSpec
  [p:Pred (STATE gsort lsort)]
  [q:Rel (STATE gsort lsort) (STATE gsort lsort)] =
  VDMSome (Pair p q)];
DischargeKeep lsort;
[U|Type(0)]
[x:VAR.DecSetoid_carrier]
[v:lsort x]
$[lsort' = sort_update x U lsort]
$[STATE' = STATE gsort lsort'];
[VDMPr [q:Rel (STATE gsort lsort) (STATE gsort lsort)] =
  [tau,sigma:STATE'](q (STATEuL x v tau) (STATEuL x v sigma)) ∧
                         (Eq (tau.local x) (sigma.local x))];
Goal VDMcontextPr :
  {Gamma:VDMContext gsort lsort}
  VDMContext gsort (sort_update x U lsort);
Induction Gamma;
  intros; Refine VDMNone; (* empty context *)
  intros; Refine VDMSomeSpec;

  Intros sigma; Refine spec.Fst (STATEuL x v sigma);
  Refine  VDMPr spec.Snd;
Save VDMcontextPr;
Discharge VAR;

Module VDMsemantics Import VDMcontext operate;
[VAR|DecSetoid][gsort,lsort|SORT VAR]
[S0:prog gsort][E:Env|VAR];
[VDMAtomicSem
[p:Pred (STATE gsort lsort)]
[S:prog (AccSort gsort lsort E)]
[q:Rel (STATE gsort lsort) (STATE gsort lsort) ]
= {sigma|STATE gsort lsort}
  (p sigma) ->
Ex [tau:STATE gsort lsort]
and (prog_operate S0 E sigma S tau) (q tau sigma)];
Goal VDMSemEx :
{U|Type}
{p:U->Pred (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{q:Rel (STATE gsort lsort) (STATE gsort lsort)}
iff (VDMAtomicSem ([sigma:STATE gsort lsort]
  Ex [t:U]p(t)(sigma)) S q)
     ({t:U}VDMAtomicSem (p(t)) S q);

...

Goal VDMSem :
{Gamma:VDMContext gsort lsort}
{p:Pred (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{q:Rel (STATE gsort lsort) (STATE gsort lsort)}
Prop;
Induction Gamma;
Refine VDMAtomicSem; (* empty context *)
intros;
Refine (VDMAtomicSem spec.Fst (call ?) spec.Snd) ->
  VDMAtomicSem p S q;
Save VDMSem;
Discharge VAR;

Module VDM Import tms_rel VDMcontext operate;
[VAR|DecSetoid][gsort|SORT VAR][S0:prog gsort];
[lsort|SORT VAR];
[VDM_Pred_to_Rel [P:Pred (STATE gsort lsort)]
= [sigma,_:STATE gsort lsort]P sigma
: Rel (STATE gsort lsort) (STATE gsort lsort)];
Discharge lsort;
(*  Corresponds to Figure 3.12 on page 111. *)
Inductive
[VDMder :
  {E:Env|VAR}{lsort|SORT VAR}
  {Gamma:VDMContext gsort lsort}
  {P:Pred (STATE gsort lsort)}
  {S:prog (AccSort gsort lsort E)}

{Q:Rel (STATE gsort lsort) (STATE gsort lsort)}
Prop]
Relation NoReductions
Constructors
[VDMAssumption :
{E:Env|VAR}{lsort|SORT VAR}
{p:Pred (STATE gsort lsort)}
{q:Rel (STATE gsort lsort) (STATE gsort lsort)}
VDMder E (VDMSomeSpec p q) p (call (AccSort gsort lsort E)) q]
[VDMSkip :
{E:Env|VAR}{lsort|SORT VAR}
{Gamma:VDMContext gsort lsort}
VDMder E Gamma ([_:(STATE gsort lsort)]trueProp)
(skip (AccSort gsort lsort E))
  (Eq|(STATE gsort lsort))]
[VDMAssign :
{E:Env|VAR}{lsort|SORT VAR}
{Gamma:VDMContext gsort lsort}
{x:VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
VDMder E Gamma ([_:(STATE gsort lsort)]trueProp) (assign x t)
  ([tau,sigma:STATE gsort lsort]
Eq tau (STATE_update E x (eval E sigma t) sigma))]
[VDMSeq :
{E:Env|VAR}{lsort|SORT VAR}
{Gamma:VDMContext gsort lsort}
{p1,p2:(STATE gsort lsort).Pred}
{r1,r2:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S1,S2:prog (AccSort gsort lsort E)}
(VDMder E Gamma p1 S1 ([tau,sigma:(STATE gsort lsort)]
and (p2 tau) (r1 tau sigma))) ->
(VDMder E Gamma p2 S2 r2) ->
VDMder E Gamma p1 (seq S1 S2) (composeRel r2 r1)]
[VDMIfthenelse :
{E:Env|VAR}{lsort|SORT VAR}
{Gamma:VDMContext gsort lsort}
{pre:(STATE gsort lsort).Pred}
{post:Rel (STATE gsort lsort) (STATE gsort lsort)}
{b:expression (AccSort gsort lsort E) bool}
{TH,EL:prog (AccSort gsort lsort E)}
(VDMder E Gamma ([sigma:(STATE gsort lsort)]
(pre sigma) ∧ (is_true (eval E sigma b)))
         TH post) ->
(VDMder E Gamma ([sigma:(STATE gsort lsort)]
(pre sigma) ∧ (is_false (eval E sigma b)))
         EL post) ->
VDMder E Gamma pre (ifthenelse b TH EL) post]
[VDMWhile :
{E:Env|VAR}{lsort|SORT VAR}
{Gamma:VDMContext gsort lsort}
{P:(STATE gsort lsort).Pred}
{sofar:Rel (STATE gsort lsort) (STATE gsort lsort)}
{sofar_trans: trans sofar}
{sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
{b:expression (AccSort gsort lsort E) bool}
{body:prog (AccSort gsort lsort E)}
{Prem:
  VDMder E Gamma ([sigma:STATE gsort lsort]
   (P sigma) ∧ (is_true (eval E sigma b)))
              body
     ([tau,sigma:STATE gsort lsort]
(P tau) ∧ (sofar tau sigma))}
VDMder E Gamma P (while b body)

       ([tau,sigma:STATE gsort lsort]
(P tau) ∧ (is_false (eval E tau b)) ∧
                          (reflclose sofar tau sigma))]
[VDMNew :
{E|Env|VAR}{lsort|SORT VAR}{U|Type}
{Gamma:VDMContext gsort lsort}
{p:Pred (STATE gsort lsort)}
{q:Rel (STATE gsort lsort) (STATE gsort lsort)}
{x:VAR.DecSetoid_carrier}{t:expression (AccSort gsort lsort E) U}
{S:prog (sort_update x U (AccSort gsort lsort E))}
{NewPrem :
  {v:lsort x}
  VDMder (Env_add E x) (VDMcontextPr x v Gamma)
      ([sigma:STATE gsort (sort_update x U lsort)]
(p (STATEuL x v sigma)) ∧
(Eq (coerce (sort_update_lookup x U lsort) (sigma.local x))
 (eval E (STATE_remove_local x v sigma) t)))
  (coerce_prog (AccSort_update gsort U E lsort x) S)
  ([tau,sigma:STATE gsort (sort_update x U lsort)]
q (STATEuL x v tau) (STATEuL x v sigma))}
VDMder E Gamma p (new x t S) q]
[VDMCall :
{E:Env|VAR}{lsort|SORT VAR}
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
{p:W->Pred (STATE gsort lsort)}
{q:Rel (STATE gsort lsort) (STATE gsort lsort)}
{Call_step:{t:W}
  VDMder (Env_empty|VAR) (VDMSomeSpec
       ([sigma:STATE gsort lsort]
Ex [u:W](p(u)(sigma)) ∧ (Lt' u t))
q)
  (p(t))
      (coerce_prog (Env_empty_global gsort lsort) S0) q}
VDMder E (VDMNone gsort lsort)
  ([sigma:STATE gsort lsort]Ex [t:W]p(t)(sigma))
  (call (AccSort gsort lsort E)) q]
[VDMConsequence :
{E:Env|VAR}{lsort|SORT VAR}
{Gamma:VDMContext gsort lsort}
{p,p1:Pred (STATE gsort lsort)}
{q,q1:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{ConsSC1:{sigma|STATE gsort lsort}(p sigma) -> (p1 sigma)}
{ConsSC2:{tau,sigma|STATE gsort lsort}(p sigma) ->
(q1 tau sigma) -> q tau sigma}
{ConsPrem:VDMder E Gamma p1 S q1}
VDMder E Gamma p S q];
Discharge VAR;

Module VDMtheorems Import VDM ;
[VAR|DecSetoid][gsort|SORT VAR][S0:prog gsort]
[E:Env|VAR][lsort|SORT VAR]
[Gamma:VDMContext gsort lsort];
Goal VDMPre :
{Gamma:VDMContext gsort lsort}
{pre|(STATE gsort lsort).Pred}
{S|prog (AccSort gsort lsort E)}
{post|Rel (STATE gsort lsort) (STATE gsort lsort)}
{Prem:VDMder S0 E Gamma pre S post}
VDMder S0 E Gamma pre S
[tau,sigma:(STATE gsort lsort)]and (pre sigma) (post tau sigma);

...

Goal VDMConsequenceLeft :
{Gamma:VDMContext gsort lsort}
{pre_s,pre : (STATE gsort lsort).Pred}
{post:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{strengthen : {sigma:(STATE gsort lsort)}(pre_s sigma) -> pre sigma}
{prem : VDMder S0 E Gamma pre S post}
VDMder S0 E Gamma pre_s S post;

...

Goal VDMConsequenceRight:
{Gamma:VDMContext gsort lsort}
{pre : (STATE gsort lsort).Pred}
{post,post_w:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{weaken :
  {sigma,tau:(STATE gsort lsort)}(post tau sigma) -> post_w tau sigma}
{prem : VDMder S0 E Gamma pre S post}
VDMder S0 E Gamma pre S post_w;

...

Goal VDMSkipBU :
{p:Rel (STATE gsort lsort) (STATE gsort lsort)}{p':(STATE gsort lsort).Pred}
{p'p:{sigma:(STATE gsort lsort)}(p' sigma) -> p sigma sigma}
VDMder S0 E Gamma p' (skip ?) p;

...

Goal VDMAssignBU :
{x:VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
{p:Rel (STATE gsort lsort) (STATE gsort lsort)}
{p':(STATE gsort lsort).Pred}
{p'p:
  {sigma,tau:(STATE gsort lsort)}
  (p' sigma) ->
  p (STATE_update E x (eval E sigma t) tau) sigma}
VDMder S0 E Gamma p' (assign x t) p;

...

Goal VDMSeqBU :
{Gamma:VDMContext gsort lsort}
{p1,p2:(STATE gsort lsort).Pred}
{r,r1,r2:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S1,S2:prog (AccSort gsort lsort E)}
{weakening: SubRel (composeRel r2 r1) r}
(VDMder S0 E Gamma p1 S1
  [tau,sigma:(STATE gsort lsort)]and (p2 tau) (r1 tau sigma)) ->
(VDMder S0 E Gamma p2 S2 r2) ->
VDMder S0 E Gamma p1 (seq S1 S2) r;

...

Goal VDMLoopBU :
 {P,I:(STATE gsort lsort).Pred}
 {Q,sofar:Rel (STATE gsort lsort) (STATE gsort lsort)}
   {sofar_trans: trans sofar}
   {sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel I))}
 {b:expression ? bool}{body:prog ?}
 {ScPre:SubPred P I}
 {ScPost:{tau,sigma:(STATE gsort lsort)}(I sigma) -> (I tau) ->
          (is_false (eval E tau b)) ->
 (reflclose sofar tau sigma) -> Q tau sigma}
 {Prem:
   VDMder S0 E Gamma ([sigma:(STATE gsort lsort)](I sigma) ∧
     (is_true (eval E sigma b)))
                body
       ([tau,sigma:(STATE gsort lsort)](I tau) ∧ (sofar tau sigma))}
 VDMder S0 E Gamma P (while b body) Q;

...

Discharge VAR;

Module VDMsound Import VDMsemantics VDM operate_thms2;
[VAR|DecSetoid][gsort|SORT|VAR][S0:prog gsort]
[E:Env|VAR][lsort|SORT|VAR][Gamma:VDMContext gsort lsort]
[p:Pred (STATE gsort lsort)][q:Rel (STATE gsort lsort) (STATE gsort lsort)]
[b:expression (AccSort gsort lsort E) bool]
[S1,S2:prog (AccSort gsort lsort E)];
Goal VDMAssumptionSound :
VDMSem S0 E (VDMSomeSpec p q) p (call (AccSort gsort lsort E)) q;

...

$Goal VDMInvAtomicSound :
VDMAtomicSem S0 E ([_:(STATE gsort lsort)]trueProp)
  (skip (AccSort gsort lsort E))
     (Eq|(STATE gsort lsort));

...

Goal VDMInvSound :
VDMSem S0 E Gamma ([_:(STATE gsort lsort)]trueProp)
  (skip (AccSort gsort lsort E))
     (Eq|(STATE gsort lsort));

...

$Goal VDMAssignAtomicSound :
{x:VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
VDMAtomicSem S0 E ([_:(STATE gsort lsort)]trueProp) (assign x t)
     ([tau,sigma:STATE gsort lsort]
Eq tau (STATE_update E x (eval E sigma t) sigma));

...

Goal VDMAssignSound :
{x:VAR.DecSetoid_carrier}
{t:expression (AccSort gsort lsort E) (AccSort gsort lsort E x)}
VDMSem S0 E Gamma ([_:(STATE gsort lsort)]trueProp) (assign x t)
     ([tau,sigma:STATE gsort lsort]
Eq tau (STATE_update E x (eval E sigma t) sigma));

...

$Goal VDMSeqAtomicSound :
{p1,p2:(STATE gsort lsort).Pred}
{r1,r2:Rel (STATE gsort lsort) (STATE gsort lsort)}
{Seq_prem1:VDMAtomicSem S0 E p1 S1 ([tau,sigma:(STATE gsort lsort)]
and (p2 tau) (r1 tau sigma))}
{Seq_prem2:VDMAtomicSem S0 E p2 S2 r2}
VDMAtomicSem S0 E p1 (seq S1 S2) (composeRel r2 r1);

...

Goal VDMSeqSound :
{p1,p2:(STATE gsort lsort).Pred}
{r1,r2:Rel (STATE gsort lsort) (STATE gsort lsort)}
{Seq_prem1:VDMSem S0 E Gamma p1 S1 ([tau,sigma:(STATE gsort lsort)]
and (p2 tau) (r1 tau sigma))}
{Seq_prem2:VDMSem S0 E Gamma p2 S2 r2}
VDMSem S0 E Gamma p1 (seq S1 S2) (composeRel r2 r1);

...

$Goal VDMIfthenelseAtomicSound :
{pre:(STATE gsort lsort).Pred}
{post:Rel (STATE gsort lsort) (STATE gsort lsort)}
{TH,EL:prog (AccSort gsort lsort E)}
{Ifthenelse_prem1:
  VDMAtomicSem S0 E ([sigma:(STATE gsort lsort)]
(pre sigma) ∧ (is_true (eval E sigma b)))
  TH post}
{Ifthenelse_prem2:
  VDMAtomicSem S0 E ([sigma:(STATE gsort lsort)]

(pre sigma) ∧ (is_false (eval E sigma b)))
EL post}
VDMAtomicSem S0 E pre (ifthenelse b TH EL) post;

...

Goal VDMIfthenelseSound :
{pre:(STATE gsort lsort).Pred}
{post:Rel (STATE gsort lsort) (STATE gsort lsort)}
{TH,EL:prog (AccSort gsort lsort E)}
{Ifthenelse_prem1:
  VDMSem S0 E Gamma ([sigma:(STATE gsort lsort)]
(pre sigma) ∧ (is_true (eval E sigma b)))
TH post}
{Ifthenelse_prem2:
VDMSem S0 E Gamma ([sigma:(STATE gsort lsort)]
(pre sigma) ∧ (is_false (eval E sigma b)))
EL post}
VDMSem S0 E Gamma pre (ifthenelse b TH EL) post;

...

$Goal VDMWhileAtomicSound :
{P:(STATE gsort lsort).Pred}
{sofar:Rel (STATE gsort lsort) (STATE gsort lsort)}
{sofar_trans: trans sofar}
{sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
{body:prog (AccSort gsort lsort E)}
{Prem:
  VDMAtomicSem S0 E ([sigma:STATE gsort lsort]
   (P sigma) ∧ (is_true (eval E sigma b)))
              body
     ([tau,sigma:STATE gsort lsort]
(P tau) ∧ (sofar tau sigma))}
VDMAtomicSem S0 E P (while b body)
       ([tau,sigma:STATE gsort lsort]
(P tau) ∧ (is_false (eval E tau b)) ∧
                          (reflclose sofar tau sigma));

...

Goal VDMWhileSound :
{P:(STATE gsort lsort).Pred}
{sofar:Rel (STATE gsort lsort) (STATE gsort lsort)}
{sofar_trans: trans sofar}
{sofar_wf: WF (andRel sofar (VDM_Pred_to_Rel P))}
{body:prog (AccSort gsort lsort E)}
{Prem:
  VDMSem S0 E Gamma ([sigma:STATE gsort lsort]
   (P sigma) ∧ (is_true (eval E sigma b)))
              body
     ([tau,sigma:STATE gsort lsort]
(P tau) ∧ (sofar tau sigma))}
VDMSem S0 E Gamma P (while b body)
       ([tau,sigma:STATE gsort lsort]
(P tau) ∧ (is_false (eval E tau b)) ∧
                          (reflclose sofar tau sigma));

...

Goal VDMNewSound :
{U|Type(0)}
{x:VAR.DecSetoid_carrier}{t:expression (AccSort gsort lsort E) U}
{S:prog (sort_update x U (AccSort gsort lsort E))}
{NewPrem :
{v:lsort x}
VDMSem S0 (Env_add E x) (VDMcontextPr x v Gamma)
      ([sigma:STATE gsort (sort_update x U lsort)]
(p (STATEuL x v sigma)) ∧
(Eq (coerce (sort_update_lookup x U lsort) (sigma.local x))
 (eval E (STATE_remove_local x v sigma) t)))
(coerce_prog (AccSort_update gsort U E lsort x) S)
  ([tau,sigma:STATE gsort (sort_update x U lsort)]
q (STATEuL x v tau) (STATEuL x v sigma))}

VDMSem S0 E Gamma p (new x t S) q;

...

Goal VDMCallSound :
{W|Type}{Lt':Rel W W}{is_wf : WF Lt'}
  {p:W->Pred (STATE gsort lsort)}
  {q:Rel (STATE gsort lsort) (STATE gsort lsort)}
  {Call_step:{t:W}
     VDMSem S0 (Env_empty|VAR) (VDMSomeSpec
         ([sigma:STATE gsort lsort]
  Ex [u:W](p(u)(sigma)) ∧ (Lt' u t))
 q)
     (p(t))
        (coerce_prog (Env_empty_global gsort lsort) S0) q}
 VDMSem S0 E (VDMNone gsort lsort)
     ([sigma:STATE gsort lsort]Ex [t:W]p(t)(sigma))
     (call (AccSort gsort lsort E)) q;

...

$Goal VDMConAtomicSound :
{p1:Pred (STATE gsort lsort)}
{q1:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{ConsSC1:{sigma|STATE gsort lsort}(p sigma) -> (p1 sigma)}
{ConsSC2:{tau,sigma|STATE gsort lsort}(p sigma) ->
  (q1 tau sigma) -> q tau sigma}
{ConsPrem:VDMAtomicSem S0 E p1 S q1}
VDMAtomicSem S0 E p S q;

...

Goal VDMConSound :
{p1:Pred (STATE gsort lsort)}
{q1:Rel (STATE gsort lsort) (STATE gsort lsort)}
{S:prog (AccSort gsort lsort E)}
{ConsSC1:{sigma|STATE gsort lsort}(p sigma) -> (p1 sigma)}
{ConsSC2:{tau,sigma|STATE gsort lsort}(p sigma) ->
  (q1 tau sigma) -> q tau sigma}
{ConsPrem:VDMSem S0 E Gamma p1 S q1}
VDMSem S0 E Gamma p S q;

...

Discharge E;
(*  Corresponds to Theorem 3.50 on page 110. *)
$Goal VDMsoundness :
{E|Env|VAR}{lsort|SORT VAR}{Gamma|VDMContext gsort lsort}
{P|Pred (STATE ??)}{S|prog ?}{Q|Rel (STATE ??) (STATE ??)}
{der:VDMder S0 E Gamma P S Q}
VDMSem S0 E Gamma P S Q;

...

Discharge VAR;

Module VDMcomplete Import operate_thms2 WF VDMsemantics VDMtheorems;
[VAR|DecSetoid][gsort|SORT VAR][S0:prog gsort];

[VDMp0
[lsort|SORT VAR][E|Env VAR][S:prog (AccSort gsort lsort E)]
[n:nat][sigma:(STATE gsort lsort)] =
   Ex [tau:(STATE gsort lsort)]prog_boperate S0 E sigma S tau n];
[psi [lsort:SORT|VAR][E:Env|VAR][n:nat]
= VDMp0 (call (AccSort gsort lsort E)) (suc n)]
[p [lsort:SORT|VAR][E:Env|VAR][n:nat][sigma:STATE gsort lsort]
= Ex ([u:nat](psi lsort E u sigma ∧ Lt u n))];

[lsort|SORT VAR][E|Env VAR];
Goal VDMp02Termin :
{S:prog (AccSort gsort lsort E)}{sigma:(STATE gsort lsort)}{n:nat}
(VDMp0 S n sigma) -> Termin E S0 S sigma;

...

[VDMSC
[x,y:(Pred (STATE gsort lsort))
  #(Rel (STATE gsort lsort) (STATE gsort lsort))] =
{tau,sigma|STATE gsort lsort}(x.1 sigma) ->
((y.1 sigma) ∧ ((y.2 tau sigma) -> x.2 tau sigma))];
Goal VDMSC_refl : refl VDMSC;

...

[b:expression (AccSort gsort lsort E) bool]
[body:prog (AccSort gsort lsort E)];

[n:nat];
Goal VDMWFbsofar :
WF (andRel (sofar S0 ? b body)
      (VDM_Pred_to_Rel (VDMp0 (while b body) n)));

...

Discharge gsort;
Goal VDM_completeness_step :
{gsort,lsort|SORT|VAR}
{S0:prog gsort}{n:nat}
[CSO = coerce_prog (Env_empty_global gsort lsort) S0]
VDMder S0 (Env_empty VAR)
(VDMSomeSpec (p S0 lsort (Env_empty|VAR) n)
  ([tau,sigma:(STATE gsort lsort)]
     prog_operate S0 (Env_empty VAR) sigma (call ?) tau))
 (VDMp0 S0 CSO n)
CSO
([tau,sigma:(STATE gsort lsort)]
  prog_operate S0 (Env_empty VAR) sigma CSO tau);

...

Goal VDM_most_general :
{gsort,lsort|SORT|VAR}{E|Env VAR}
{S:prog (AccSort gsort lsort E)}{S0:prog gsort}
VDMder S0 E (VDMNone ??)
([sigma:(STATE gsort lsort)]
  Ex [tau:(STATE gsort lsort)]prog_operate S0 E sigma S tau)
S
([tau,sigma:(STATE gsort lsort)]prog_operate S0 E sigma S tau);

...

(*  Corresponds to Theorem 3.51 on page 110. *)
Goal VDM_complete :
{gsort,lsort|SORT VAR}{E|Env VAR}{S0|prog gsort}
{S|prog (AccSort gsort lsort E)}
{p|(STATE gsort lsort).Pred}
{q|Rel (STATE gsort lsort) (STATE gsort lsort)}
{semant:VDMSem S0 E (VDMNone ??) p S q}VDMder S0 E (VDMNone ??) p S q;

...

Discharge VAR;

B.5 Quicksort

Module quicksort_theory Import lib_nat_Prop_rels lib_list_sorted lib_bool_funs
                                   lib_nat_bool_rels lib_nat_rels;
Goal minus_zero_Le : {a,b|nat}(Eq (b .minus a) zero) -> Le b a;

...

Goal Le_zero_Eq_zero : {a|nat}(Le a zero) -> Eq a zero;

...

Goal Lt_imp_Le_pred : {a,b|nat}(Lt a b) -> Le a (pred b);

...

Goal Le_suc_imp_Lt_pred_suc : {a,b|nat}(Le a (suc b)) -> Lt (pred a) (suc b);

...

Goal Le_Lt_contradiction : {a,b|nat}{ab:Le a b}{ba:Lt b a}absurd;

...

Goal minus_suc_left :
  {a,b|nat}(Le a b) -> Eq (minus (suc b) a) (suc (minus b a));

...

Freeze Eq;
Goal Lt_minus_stable_left :
  {a,b,c|nat}(Lt a c) -> (Le c b) -> Lt (minus b c) (minus b a);

...

Goal Lt_minus_stable_right :
  {a,b,c|nat}(Lt a c) -> (Le c b) -> Lt (minus (pred c) a) (minus b a);

...

[s|Type];
Goal singletonSorted : {R:Rel s s}{x:s}Sorted R (singleton x);

...

Goal nat_array_to_list : {A:nat->s}{a,b:nat}list s;
intros A' a' b';
Refine nat_double_elim [a,b:nat]{A:nat->s}list s;
  Refine -0 A'; Refine -0 b'; Refine -0 a';
  (** nat_array_to_list zero zero **)
  intros; Refine singleton (A zero);
  (** nat_array_to_list zero (suc n) **)
  intros _nat_array_to_list_zero_n A;
  Refine cons (A zero) (nat_array_to_list_zero_n (compose A suc));
  (** nat_array_to_list (suc m) zero **)
  intros; Refine nil;
  (** nat_array_to_list (suc m) (suc n) **)
  intros; Refine H n (compose A suc);
Save nat_array_to_list;
Goal nat_array_to_list_nil :
  {a,b|nat}{A:nat->s}
  (b .Lt a)
   -> Eq (nat_array_to_list A a b) (nil s);

...

Goal nat_array_to_list_cons :
 {a,b|nat}{A:nat->s}(a .Le b) ->
   Eq (nat_array_to_list A a b)
       (cons (A a) (nat_array_to_list A (suc a) b));

...

Goal nat_array_to_list_cons' :
 {b:nat}{X:nat->s}
 Eq (nat_array_to_list X zero b)
     (cons (X zero) (nat_array_to_list X one b));

...

Goal nat_array_to_list_singleton :
  {b:nat}{X:nat->s}
  Eq (nat_array_to_list X b b) (singleton (X b));

...

Goal nat_array_to_list_append :
  {a,c,b|nat}{X:nat->s}
  (a .Lt b)
     -> (b .Le c)
       ->   Eq (nat_array_to_list X a c)
                (append (nat_array_to_list X a (pred b))
                         (nat_array_to_list X b c));

...

Goal nat_array_to_list_character :
  {a,b:nat}{X:nat->s}
  ((Eq (nat_array_to_list X a b) (nil ?)) ∧ (Lt b a)) ∨
  ((Eq (nat_array_to_list X a b)
        (cons (X a) (nat_array_to_list X (suc a) b))) ∧ (Le a b));

...

Discharge s;
[stable_outside =
  [X:nat->nat][X0:nat->nat][a,b:nat]
  {c:nat}(or (Lt c a) (Lt b c)) -> Eq (X c) (X0 c)];
Goal nat_array_to_list_stable :
   {a,b:nat}{c,d|nat}{X,X'|nat->nat}
   (stable_outside X X' c d)
   -> ((Lt b c) ∨ (Lt d a))
      -> Eq (nat_array_to_list X a b) (nat_array_to_list X' a b);

...

[X,X'|nat->nat][a,b,c|nat];
[s|Type];
[update [A:nat->s][y:nat][t:s] = [x:nat]if (nat_eq y x) t (A x)];
$[Nat_array_to_list_update [a,b:nat] =
  {c|nat}{X:nat->s}{t:s}
  (not (Eq c zero)) ->
  Eq (nat_array_to_list (update X c t) a b) (nat_array_to_list X a b)];
$Goal nat_array_to_list_update_zero_zero : Nat_array_to_list_update zero zero;

...

Goal compose_suc :
      {c:nat}{X:nat->s}{t:s}  (not (Eq c zero)) ->
      Eq (compose (update X c t) suc)
                  (update (compose X suc) (pred c) t);

...

Goal compose_suc_zero :
      {X:nat->s}{t:s}Eq (compose (update X zero t) suc) (compose X suc);

...

Goal nat_array_to_list_tack :
  {a,b|nat}{X:nat->s}(Lt (pred a) b) ->
      Eq (nat_array_to_list X a b)

         (tack (X b) (nat_array_to_list X a (pred b)));

...

(*  Corresponds to Lemma 4.2 on page 115. *)
Goal nat_array_to_list_update :
  {t:s}{a,b:nat}{c|nat}{X:nat->s}
  ((Lt c a) ∨ (Lt b c)) ->
  Eq (nat_array_to_list (update X c t) a b) (nat_array_to_list X a b);

...

Discharge X;
[s,t|Type][R:Rel s t];

Goal redun_update : {b:nat}{X:nat->s}Eq X (update X b (X b));

...

(*  Corresponds to Lemma 4.3 on page 115. *)
Goal nat_array_to_list_update_factorise :
  {a,c,b|nat}{X:nat->s}{t:s}
  {ab:Le a b}{bc:Le b c}
  Eq (nat_array_to_list (update X b t) a c)
      (insert t (if (nat_eq b zero) (nil ?) (nat_array_to_list X a (pred b)))
                (nat_array_to_list X (suc b) c));

...

Goal appendPerm :
  {k,k'|list s}
  (Perm k k')->{l,l'|list s}(Perm l l') ->
  Perm (append k l) (append k' l');

...

(*  Corresponds to Lemma 4.5 on page 116. *)
Goal updatePerm :
  {a,b,c,d|nat}{X:nat->s}
  {ac:Le a c}{ad:Le a d}{cb:Le c b}{db:Le d b}
  Perm (nat_array_to_list (update (update X d (X c)) c (X d)) a b)
        (nat_array_to_list X a b);

...

[list_pointwise_ext_right = Rlist];
[list_pointwise_ext = op (Rlist (op (Rlist R)))];
Goal list_pointwise_ext_nil : {k:list s}list_pointwise_ext k (nil t);

...

Goal list_pointwise_ext_cons :
   {k:list s}{x:t}{xs:list t}
   iff (list_pointwise_ext k (cons x xs))
        (and (list_pointwise_ext k (singleton x))
              (list_pointwise_ext k xs));

...

(** morally, this lemma subsumes appclRlist in lib_list_sorted **)
Goal list_pointwise_ext_append_left :
  {k,k':list s}{l:list t}
  iff (and (list_pointwise_ext k l)
             (list_pointwise_ext k' l))
        (list_pointwise_ext (append k k') l);

...

Goal list_pointwise_ext_tack_left :
  {a,b|nat}{X|nat->s}{l|list t}
  (list_pointwise_ext (nat_array_to_list X a b) l) ->
  (list_pointwise_ext_right R (X (suc b)) l) ->
  list_pointwise_ext (nat_array_to_list X a (suc b)) l;

...

Goal list_pointwise_ext_append_right :
  {k:list s}{l,l':list t}
  iff (and (list_pointwise_ext k l)
             (list_pointwise_ext k l'))
        (list_pointwise_ext k (append l l'));

...

Goal list_pointwise_ext_tack_right :
  {a,b|nat}{X:nat->t}{l|list s}
  (list_pointwise_ext l (nat_array_to_list X a b)) ->
  (list_pointwise_ext l (singleton (X (suc b)))) ->
  list_pointwise_ext l (nat_array_to_list X a (suc b));

...

Goal list_pointwise_ext_resp_perm_left :
  {k,k'|list s}{l|list t}
  (Perm k k')
       -> (list_pointwise_ext k l)
         -> list_pointwise_ext k' l;

...

Goal list_pointwise_ext_resp_perm_right :
  {k|list s}{l,l'|list t}
  (Perm l l')
       -> (list_pointwise_ext k l)
         -> list_pointwise_ext k l';

...

[a|s][k|list s][b|t][l|list t];
Goal list_pointwise_ext_boundary :
   (list_pointwise_ext (tack a k) (cons b l))
      -> R a b;

...

Discharge a;
Goal list_pointwise_nat_array_to_list_suc :
  {r|s}{a,b|nat}{X|nat->t}
  (R r (X a)) ->
  (list_pointwise_ext_right R r (nat_array_to_list X (suc a) b)) ->
  (list_pointwise_ext_right R r (nat_array_to_list X a b));

...

DischargeKeep R;
[S:Rel s t];
Goal list_pointwise_resp_SubRel :
   (SubRel R S) -> (SubRel (list_pointwise_ext R) (list_pointwise_ext S));

...

Discharge t;
[R:Rel s s];
Goal list_pointwise_trans :
  (trans R) ->
  {k,l|list s}{m|s}
  {km:list_pointwise_ext R k (singleton m)}
  {ml:list_pointwise_ext R (singleton m) l}
  list_pointwise_ext R k l;

...

Discharge s;

Goal array_sorted_append :
  {a,b,c|nat}{X|nat->nat}
  [Xleft = nat_array_to_list X a (pred b)]
  [Xright= nat_array_to_list X b c]
  (list_pointwise_ext Le Xleft Xright)
     -> (Sorted Le Xleft)
       -> (Sorted Le Xright)
         -> Sorted Le (append Xleft Xright);

...

(*  Corresponds to Definition 4.6 on page 116. *)
[Perm' [A,B:nat->nat][a,b:nat] =
  (Perm (nat_array_to_list A a b) (nat_array_to_list B a b)) ∧
  (stable_outside A B a b)];
(*  Corresponds to Definition 4.7 on page 116. *)
[Spec_Quicksort_post [a,b:nat][x,X:nat->nat] =
      [Xab  = nat_array_to_list x a b]
      (Sorted Le Xab) ∧
      (Perm' x X a b)];
[a,b,c|nat][X0,X1,X2,X3|nat->nat]
[ac : Lt a c][cb :Le c b]
[Quicksort_Perm: Perm' X1 X0 a b]
[Quicksort_Partition:
     list_pointwise_ext Le
     (nat_array_to_list X1 a (pred c)) (nat_array_to_list X1 c b)]
[Quicksort_first_segment:  Spec_Quicksort_post a (pred c) X2 X1]
[Quicksort_second_segment: Spec_Quicksort_post c b X3 X2];
Freeze Perm nat_array_to_list list_pointwise_ext;
(*  Corresponds to Theorem 4.8 on page 116. *)
Goal Sorted_Quicksort : Spec_Quicksort_post a b X3 X0;

...

Discharge a;
Unfreeze Eq nat_array_to_list list_pointwise_ext;

Module quicksort_prog Import syntax quicksort_theory;
Inductive [QuicksortVAR:Type] Theorems
Constructors [X_,a_,b_,Pa_,Pb_,c_,d_,exch_,r_:QuicksortVAR];
Goal QuicksortVAR_eq : {x,y:QuicksortVAR}bool;

...

Goal QuicksortVAR_eq_refl : {x:QuicksortVAR}is_true (QuicksortVAR_eq x x);

...

Freeze Eq;
Goal QuicksortVAR_eq_char : Eqchar QuicksortVAR_eq;

...

Unfreeze Eq;
Goal QuicksortVAR_DecSetoid : DecSetoid;

...

[X     = X_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[a     = a_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[b     = b_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[Pa   = Pa_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[Pb   = Pb_: QuicksortVAR_DecSetoid.DecSetoid_carrier]

[c     = c_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[d     = d_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[exch = exch_: QuicksortVAR_DecSetoid.DecSetoid_carrier]
[r     = r_: QuicksortVAR_DecSetoid.DecSetoid_carrier];
Goal QuicksortSORT : {x:QuicksortVAR_DecSetoid.DecSetoid_carrier}Type;
Expand DecSetoid_carrier;
Induction x;
Refine nat ->nat;
Refine nat; Refine nat; Refine nat; Refine nat; Refine nat;
Refine nat; Refine nat; Refine nat;
Save QuicksortSORT;
(*  Corresponds to Definition 4.13 on page 120. *)
Goal Prepare:
   prog QuicksortSORT;
Refine seq;
Refine ifthenelse;
  Intros sigma; Refine lt (sigma(X)(sigma b)) (sigma(X)(sigma a));
  (** Then branch **)
  Refine new; Refine +1 exch; Intros +1 sigma; Refine sigma(X)(sigma a);
     Refine seq;
       Refine assign; Refine X;
         Intros sigma;
           Refine update (sigma X) (sigma a) (sigma X (sigma b));
       Refine assign; Refine X;
         Intros sigma;
           Refine update (sigma X) (sigma b) (sigma exch);
  (** Else branch **)
  Refine skip;
  Refine new; Refine +1 r;
     Intros +1 sigma;
       Refine (sigma(X)(sigma a));
     (** partition (a+1,b-1,r,c,X) **)
     Refine seq; Refine assign; Refine c; Intros sigma; Refine suc(sigma a);
      Refine new; Refine +1 d; Intros +1 sigma; Refine pred(sigma(b));
        Refine while; Intros sigma; Refine le (sigma(c)) (sigma(d));
          Refine ifthenelse; Intros sigma;
             Refine le (sigma(X)(sigma(c))) (sigma(r));
             Refine assign; Refine c; Intros sigma; Refine suc (sigma(c));
             Refine ifthenelse; Intros sigma;
               Refine lt (sigma(r)) (sigma(X)(sigma(d)));
               Refine assign; Refine d; Intros sigma; Refine pred (sigma(d));
               Refine seq;
                 Refine new; Refine +1 exch; Intros +1 sigma;
        Refine sigma(X)(sigma(c));
                   Refine seq;
                      Refine assign; Refine X;
                        Intros sigma;
                        Refine update (sigma X) (sigma c) (sigma(X)(sigma(d)));
                      Refine assign; Refine X;
                        Intros sigma;
                        Refine update (sigma X) (sigma d)  (sigma(exch));
                  Refine seq;
                     Refine assign; Refine c; Intros sigma; Refine suc (sigma(c));
                     Refine assign; Refine d; Intros sigma; Refine pred (sigma(d));
Save Prepare;
Goal coerce_Prepare :
  EXTEQ QuicksortSORT
  (sort_update c nat (sort_update b nat (sort_update a nat QuicksortSORT)));
Expand EXTEQ; Expand DecSetoid_carrier;
Induction x;
  Refine Eq_refl; Refine Eq_refl; Refine Eq_refl; Refine Eq_refl;
  Refine Eq_refl; Refine Eq_refl; Refine Eq_refl; Refine Eq_refl;

Refine Eq_refl;
SaveFrozen coerce_Prepare;
(*  Corresponds to Definition 4.12 on page 120. *)
Goal S0 : prog|QuicksortVAR_DecSetoid QuicksortSORT;
Refine new; Refine +1 a; Intros +1 sigma; Refine sigma(Pa);
Refine new; Refine +1 b; Intros +1 sigma; Refine sigma(Pb);
Refine ifthenelse;
(** boolean expression **)
Intros sigma;
Refine lt (sigma a) (sigma b);
(** THEN branch **)
Refine  new; Refine +1 c; Intros +1; Refine zero; (* any value will do *)
For 6 Refine seq; Refine coerce_prog coerce_Prepare Prepare;
(** a' := a **)
Refine assign; Refine Pa;
  Intros sigma; Refine sigma (a);
(** b' := c-1 **)
Refine assign; Refine Pb;
  Intros sigma; Refine pred (sigma c);
Refine call;
(** a' := c **)
Refine assign; Refine Pa;
  Intros sigma; Refine sigma c;
(** b' := b **)
Refine assign; Refine Pb;
  Intros sigma; Refine sigma b;
Refine call;
(** ELSE branch **)
Refine skip;
Save S0;

Module quicksort_spec

Import lib_prod quicksort_prog quicksort_theory assertion;
[qsSTATE = STATE QuicksortSORT QuicksortSORT]
[qsSTATE' = ((qsSTATE#nat)#nat)#nat]
[PrepareLSORT =
sort_update c nat (sort_update b nat (sort_update a nat QuicksortSORT))]
[PrepareSTATE = STATE QuicksortSORT PrepareLSORT]
[E:Env QuicksortVAR_DecSetoid];
[Spec_Quicksort_pre [Z,sigma:qsSTATE]
 = (Eq (lookup E sigma Pa) (lookup E Z Pa)) ∧
   (Eq (lookup E sigma Pb) (lookup E Z Pb)) ∧
   (Eq (lookup E sigma X) (lookup E Z X))];
Discharge E;
[sortdr = sort_update d nat (sort_update r nat QuicksortSORT)];
[E = Env_add (Env_add (Env_add (Env_empty QuicksortVAR_DecSetoid) a) b) c]
[Erd = Env_add (Env_add E r) d];
[Spec_Prepare_pre
 = [z:qsSTATE'][sigma:PrepareSTATE]

   [z=z.1.1.1]
   [A=     lookup E z a][B=     lookup E z b]
   [a=lookup E sigma a]
   [b=lookup E sigma b]
   [x=lookup E sigma X]
   [X=     lookup E z X]
   (Lt a b) ∧ (Eq a A) ∧ (Eq b B) ∧ (Eq x X)]
(*  Corresponds to Definition 4.16 on page 121. *)
[Assertion_Prepare_variant
[z:qsSTATE][sigma:PrepareSTATE] =
[x = (lookup E sigma X)]
[X = (lookup E z X)]
[A = (lookup E z a)]
[B = (lookup E z b)]
[c = (lookup E sigma c)]
[Xleft = nat_array_to_list x A (pred c)]
[Xright = nat_array_to_list x c B]
(Lt A c) ∧ (Le c B) ∧
(list_pointwise_ext Le Xleft Xright) ∧
(Perm' x X A B)];
(*  Corresponds to Definition 4.17 on page 121. *)
[Spec_Prepare_inv = [D:qsSTATE -> nat]
[z,sigma:qsSTATE]
[x = (lookup Erd sigma X)]
[X = (lookup Erd z X)]
[A = (lookup Erd z a)]
[a = (lookup Erd sigma a)]
[B = (lookup Erd z b)]
[b = (lookup Erd sigma b)]
[c = (lookup Erd sigma c)]
[r = (lookup Erd sigma r)]
(Lt A c) ∧ (Le c (suc (D sigma))) ∧ (Lt (D sigma) B) ∧
 (list_pointwise_ext Le (nat_array_to_list x A (pred c))
     (singleton r)) ∧
 (list_pointwise_ext Lt (singleton r)
     (nat_array_to_list x (suc (D sigma)) (pred B))) ∧
 (Le r (x B)) ∧
 (Perm' x X A B) ∧
  (Eq a A) ∧ (Eq b B)];
[Spec_Prepare_post
= [z:qsSTATE']
  [sigma:PrepareSTATE]
  [z = z.1.1.1]
  (Assertion_Prepare_variant z sigma) ∧
  (Eq (lookup E sigma a) (lookup E z a)) ∧
  (Eq (lookup E sigma b) (lookup E z b))];
Configure Infix - left 5;
[op- = minus];
Goal EXTEQdrPrepareLQuicksort :
EXTEQ (sort_update d nat (sort_update r nat PrepareLSORT))
        QuicksortSORT;

...

Goal drPrepareLSORT2qsSTATE :
{sigma:STATE QuicksortSORT
  (sort_update d nat (sort_update r nat PrepareLSORT))}
qsSTATE;

...

Goal Prepare_coerce' :
EXTEQ QuicksortSORT (AccSort QuicksortSORT PrepareLSORT E);

...

Module Prepare_vc1 Import quicksort_spec;

[t:nat];
(* corresponds to the case x[c]<=r *)
Goal Pvc1 :
SubRel ([Z:qsSTATE']
[sigma:STATE QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))]
(Spec_Prepare_inv ([sigma'3:qsSTATE]local sigma'3 d) Z.1.1.1
(drPrepareLSORT2qsSTATE sigma) ∧
  is_true (eval Erd sigma
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT)
  (Env_add E r)))
(AccSort QuicksortSORT
(sort_update d nat
  (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT
(sort_update r nat PrepareLSORT)
(Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
nat E PrepareLSORT r)
  nat d)
(coerce_expression QuicksortVAR_DecSetoid
  (sort_update d nat
(sort_update r nat QuicksortSORT))
  (sort_update d nat
(sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
  (coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
  ([sigma'3:FS (sort_update d nat
 (sort_update r nat
QuicksortSORT))]
le (sigma'3 c) (sigma'3 d)))))) ∧
Eq (suc (lookup Erd sigma d) - lookup Erd sigma c) t ∧
is_true (eval Erd sigma
  (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT)
(Env_add E r)))
(AccSort QuicksortSORT
(sort_update d nat
  (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT
(sort_update r nat PrepareLSORT)
(Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
nat E PrepareLSORT r)
  nat d)

      (coerce_expression QuicksortVAR_DecSetoid
        (sort_update d nat
 (sort_update r nat QuicksortSORT))
        (sort_update d nat
 (sort_update r nat
   (AccSort QuicksortSORT PrepareLSORT E)))
        (coerce_sort_update (coerce_sort_update Prepare_coerce'
      nat r) nat d)
        ([sigma'3:FS (sort_update d nat
       (sort_update r nat
QuicksortSORT))]
 le (sigma'3 X (sigma'3 c)) (sigma'3 r))))))
        ))
     ([Z:qsSTATE']
       [sigma:STATE QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))]
       (Spec_Prepare_inv ([sigma'3:qsSTATE]local sigma'3 d) Z.1.1.1
(drPrepareLSORT2qsSTATE (STATE_update Erd c
  (eval Erd sigma
     (coerce_expression' QuicksortVAR_DecSetoid
       c
       (sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT
     ) (Env_add E r)))
       (AccSort QuicksortSORT
(sort_update d nat
  (sort_update r nat PrepareLSORT
     )) (Env_add (Env_add E r) d))
       (AccSort_update QuicksortSORT nat
(Env_add E r)
(sort_update r nat PrepareLSORT)
d)
       (coerce_expression' QuicksortVAR_DecSetoid
c
(sort_update d nat
  (sort_update r nat
     (AccSort QuicksortSORT
       PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT
     (sort_update r nat
       PrepareLSORT) (Env_add E r)
     ))
(coerce_sort_update (AccSort_update QuicksortSORT
       nat E
       PrepareLSORT
       r) nat d)
(coerce_expression' QuicksortVAR_DecSetoid
  c
  (sort_update d nat
     (sort_update r nat
       QuicksortSORT))
  (sort_update d nat
     (sort_update r nat
       (AccSort QuicksortSORT
PrepareLSORT E)))
  (coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r)
     nat d)
  ([sigma'3:FS (sort_update d nat
 (sort_update r
   nat
   QuicksortSORT)
 )]
     suc (sigma'3 c)))))) sigma))

∧

Lt (suc (lookup Erd
  (STATE_update Erd c
     (eval Erd sigma

 (coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat
(AccSort QuicksortSORT
(sort_update r nat PrepareLSORT) (Env_add E r)
))
(AccSort QuicksortSORT
(sort_update d nat
(sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat
(sort_update r nat
 (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
(AccSort QuicksortSORT
 (sort_update r nat PrepareLSORT)
 (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
nat E PrepareLSORT r) nat
d)
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat
 (sort_update r nat QuicksortSORT))
(sort_update d nat
 (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
(coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
([sigma'3:FS (sort_update d nat
(sort_update r nat
QuicksortSORT))]
 suc (sigma'3 c)))))) sigma) d) -
lookup Erd
(STATE_update Erd c
  (eval Erd sigma
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT) (Env_add E r)))
(AccSort QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat
  (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT
(sort_update r nat PrepareLSORT) (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT nat
E PrepareLSORT r) nat d)
(coerce_expression' QuicksortVAR_DecSetoid c
  (sort_update d nat
(sort_update r nat QuicksortSORT))
  (sort_update d nat
(sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
  (coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
  ([sigma'3:FS (sort_update d nat
 (sort_update r nat QuicksortSORT))]
suc (sigma'3 c)))))) sigma) c) t));

...

Discharge t;

Module Prepare_vc2 Import quicksort_spec;
[t:nat];
(* corresponds to the case x[c] > d and x[d] > r *)
Goal Pvc2 :
SubRel ([Z:qsSTATE']
[sigma:STATE QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))]
(Spec_Prepare_inv ([sigma'3:qsSTATE]local sigma'3 d) Z.1.1.1
(drPrepareLSORT2qsSTATE sigma) ∧
is_true (eval Erd sigma
  (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(AccSort QuicksortSORT
(sort_update r nat PrepareLSORT)
(Env_add E r)))
(AccSort QuicksortSORT
(sort_update d nat
(sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(sort_update r nat
  (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT)
  (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
nat E PrepareLSORT r)
nat d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat QuicksortSORT))
(sort_update d nat
  (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
(coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
([sigma'3:FS (sort_update d nat
(sort_update r nat
 QuicksortSORT))]
  le (sigma'3 c) (sigma'3 d)))))) ∧
Eq (suc (lookup Erd sigma d) - lookup Erd sigma c) t ∧
is_false (eval Erd sigma
   (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(AccSort QuicksortSORT
 (sort_update r nat PrepareLSORT)
 (Env_add E r)))
(AccSort QuicksortSORT
(sort_update d nat
 (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
 (sort_update r nat
   (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
 (AccSort QuicksortSORT
   (sort_update r nat PrepareLSORT)
   (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
nat E PrepareLSORT r)
 nat d)

(coerce_expression QuicksortVAR_DecSetoid
 (sort_update d nat
   (sort_update r nat QuicksortSORT))
 (sort_update d nat
   (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E))
   )
 (coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
 ([sigma'3:FS (sort_update d nat
(sort_update r nat
  QuicksortSORT))]
   le (sigma'3 X (sigma'3 c)) (sigma'3 r)))))
   ) ∧
is_true (eval Erd sigma
  (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(AccSort QuicksortSORT
(sort_update r nat PrepareLSORT)
(Env_add E r)))
(AccSort QuicksortSORT
(sort_update d nat
(sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(sort_update r nat
  (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT)
  (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
nat E PrepareLSORT r)
nat d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat QuicksortSORT))
(sort_update d nat
  (sort_update r nat
(AccSort QuicksortSORT PrepareLSORT E)))
(coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
([sigma'3:FS (sort_update d nat
(sort_update r nat
 QuicksortSORT))]
  lt (sigma'3 r) (sigma'3 X (sigma'3 d)))))))
))
([Z:qsSTATE']
[sigma:STATE QuicksortSORT
  (sort_update d nat (sort_update r nat PrepareLSORT))]
(Spec_Prepare_inv ([sigma'3:qsSTATE]local sigma'3 d) Z.1.1.1
  (drPrepareLSORT2qsSTATE (STATE_update Erd d
(eval Erd sigma
(coerce_expression' QuicksortVAR_DecSetoid
d
(sort_update d nat
  (AccSort QuicksortSORT
(sort_update r nat PrepareLSORT
) (Env_add E r)))
(AccSort QuicksortSORT
  (sort_update d nat
(sort_update r nat PrepareLSORT
)) (Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat
  (Env_add E r)
  (sort_update r nat PrepareLSORT)
  d)

  (coerce_expression' QuicksortVAR_DecSetoid
     d
     (sort_update d nat
(sort_update r nat
(AccSort QuicksortSORT
  PrepareLSORT E)))
     (sort_update d nat
(AccSort QuicksortSORT
(sort_update r nat
  PrepareLSORT) (Env_add E r)
))
     (coerce_sort_update (AccSort_update QuicksortSORT
  nat E
  PrepareLSORT
  r) nat d)
     (coerce_expression' QuicksortVAR_DecSetoid
d
(sort_update d nat
(sort_update r nat
  QuicksortSORT))
(sort_update d nat
(sort_update r nat
  (AccSort QuicksortSORT
     PrepareLSORT E)))
(coerce_sort_update (coerce_sort_update Prepare_coerce'
     nat r)
nat d)
([sigma'3:FS (sort_update d nat
      (sort_update r
nat
QuicksortSORT)
      )]
pred (sigma'3 d)))))) sigma))

∧

     Lt (suc (lookup Erd
(STATE_update Erd d
(eval Erd sigma
  (coerce_expression' QuicksortVAR_DecSetoid d
     (sort_update d nat
(AccSort QuicksortSORT
(sort_update r nat PrepareLSORT) (Env_add E r)
))
     (AccSort QuicksortSORT
(sort_update d nat
(sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
     (AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
     (coerce_expression' QuicksortVAR_DecSetoid d
(sort_update d nat
(sort_update r nat
  (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
(AccSort QuicksortSORT
  (sort_update r nat PrepareLSORT)
  (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT
     nat E PrepareLSORT r) nat
d)
(coerce_expression' QuicksortVAR_DecSetoid d
(sort_update d nat
  (sort_update r nat QuicksortSORT))
(sort_update d nat
  (sort_update r nat
     (AccSort QuicksortSORT PrepareLSORT E)))
(coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d)
([sigma'3:FS (sort_update d nat
(sort_update r nat
 QuicksortSORT))]

     pred (sigma'3 d)))))) sigma) d) -
   lookup Erd
   (STATE_update Erd d
      (eval Erd sigma
        (coerce_expression' QuicksortVAR_DecSetoid d
 (sort_update d nat
   (AccSort QuicksortSORT
      (sort_update r nat PrepareLSORT) (Env_add E r)))
 (AccSort QuicksortSORT
   (sort_update d nat (sort_update r nat PrepareLSORT))
   (Env_add (Env_add E r) d))
 (AccSort_update QuicksortSORT nat (Env_add E r)
   (sort_update r nat PrepareLSORT) d)
 (coerce_expression' QuicksortVAR_DecSetoid d
   (sort_update d nat
      (sort_update r nat
        (AccSort QuicksortSORT PrepareLSORT E)))
   (sort_update d nat
      (AccSort QuicksortSORT
        (sort_update r nat PrepareLSORT) (Env_add E r)))
   (coerce_sort_update (AccSort_update QuicksortSORT nat
 E PrepareLSORT r) nat d)
   (coerce_expression' QuicksortVAR_DecSetoid d
      (sort_update d nat
        (sort_update r nat QuicksortSORT))
      (sort_update d nat
        (sort_update r nat
 (AccSort QuicksortSORT PrepareLSORT E)))
      (coerce_sort_update (coerce_sort_update Prepare_coerce'
   nat r) nat d)
      ([sigma'3:FS (sort_update d nat
     (sort_update r nat QuicksortSORT))]
        pred (sigma'3 d)))))) sigma) c) t));

...

Discharge t;

Module Prepare_vc3 Import quicksort_spec;
[t:nat];
[vexch : sort_update d nat (sort_update r nat PrepareLSORT) exch];
(* corresponds to the case x[c]>r and x[d]<= r *)
Goal Pvc3 :
   SubRel ([Z:qsSTATE']
     [sigma:STATE QuicksortSORT
       (sort_update exch nat (sort_update d nat (sort_update r nat PrepareLSORT)))]
     (Fr exch
       ([Z'3:qsSTATE']
[sigma'4:STATE QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))
  ]
(Spec_Prepare_inv ([sigma'5:qsSTATE]local sigma'5 d) Z'3.1.1.1
  (drPrepareLSORT2qsSTATE sigma'4) ∧
  is_true (eval Erd sigma'4
     (coerce_expression QuicksortVAR_DecSetoid
       (sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
  (Env_add E r)))
       (AccSort QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
       (AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
       (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
     (Env_add E r)))

(coerce_sort_update (AccSort_update QuicksortSORT nat E
      PrepareLSORT r) nat d)
(coerce_expression QuicksortVAR_DecSetoid
 (sort_update d nat (sort_update r nat QuicksortSORT))
 (sort_update d nat
   (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
 (coerce_sort_update (coerce_sort_update Prepare_coerce' nat r)
   nat d)
 ([sigma'5:FS (sort_update d nat
(sort_update r nat QuicksortSORT))]
   le (sigma'5 c) (sigma'5 d)))))) ∧
 Eq (suc (lookup Erd sigma'4 d) - lookup Erd sigma'4 c) t ∧
 is_false (eval Erd sigma'4
     (coerce_expression QuicksortVAR_DecSetoid
       (sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
  (Env_add E r)))
       (AccSort QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
       (AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
       (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
     (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT nat E
       PrepareLSORT r) nat d)
(coerce_expression QuicksortVAR_DecSetoid
  (sort_update d nat (sort_update r nat QuicksortSORT))
  (sort_update d nat
     (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
  (coerce_sort_update (coerce_sort_update Prepare_coerce' nat r)
     nat d)
  ([sigma'5:FS (sort_update d nat
 (sort_update r nat QuicksortSORT))]
     le (sigma'5 X (sigma'5 c)) (sigma'5 r)))))) ∧
 is_false (eval Erd sigma'4
     (coerce_expression QuicksortVAR_DecSetoid
       (sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
  (Env_add E r)))
       (AccSort QuicksortSORT
(sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
       (AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
       (coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
     (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT nat E
       PrepareLSORT r) nat d)
(coerce_expression QuicksortVAR_DecSetoid
  (sort_update d nat (sort_update r nat QuicksortSORT))
  (sort_update d nat
     (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
  (coerce_sort_update (coerce_sort_update Prepare_coerce' nat r)
     nat d)
  ([sigma'5:FS (sort_update d nat
 (sort_update r nat QuicksortSORT))]
     lt (sigma'5 r) (sigma'5 X (sigma'5 d))))))))) vexch Z sigma ∧
      Eq (coerce (sort_update_lookup exch nat
  (sort_update d nat (sort_update r nat PrepareLSORT)))
  (local sigma exch))
      (eval Erd (STATE_remove_local exch vexch sigma)

(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r)))
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d)
(coerce_expression QuicksortVAR_DecSetoid
(sort_update d nat
  (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
  (AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT nat E PrepareLSORT r) nat
  d)
(coerce_expression QuicksortVAR_DecSetoid
  (sort_update d nat (sort_update r nat QuicksortSORT))
  (sort_update d nat
(sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
  (coerce_sort_update (coerce_sort_update Prepare_coerce' nat r) nat d)
  ([sigma'3:FS (sort_update d nat (sort_update r nat QuicksortSORT))]
sigma'3 X (sigma'3 c))))))))
([Z:qsSTATE']
[sigma:STATE QuicksortSORT
  (sort_update exch nat (sort_update d nat (sort_update r nat PrepareLSORT)))]
Assertion_update (Env_add Erd exch) X
(coerce_expression' QuicksortVAR_DecSetoid X
  (sort_update exch nat
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT)) Erd))
  (AccSort QuicksortSORT
(sort_update exch nat (sort_update d nat (sort_update r nat PrepareLSORT)))
(Env_add Erd exch))
  (AccSort_update QuicksortSORT nat Erd
(sort_update d nat (sort_update r nat PrepareLSORT)) exch)
  (coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r))))
(sort_update exch nat
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d)))
(coerce_sort_update (AccSort_update QuicksortSORT nat (Env_add E r)
  (sort_update r nat PrepareLSORT) d) nat exch)
(coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat
(sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E))))
(sort_update exch nat
(sort_update d nat
  (AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r))))
(coerce_sort_update (coerce_sort_update (AccSort_update QuicksortSORT nat E
PrepareLSORT r) nat d) nat exch)
(coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat (sort_update d nat (sort_update r nat QuicksortSORT)))
(sort_update exch nat
  (sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E))))
(coerce_sort_update (coerce_sort_update (coerce_sort_update Prepare_coerce' nat r
  ) nat d) nat exch)
([sigma'3:FS (sort_update exch nat
(sort_update d nat (sort_update r nat QuicksortSORT)))]
  update (sigma'3 X) (sigma'3 d) (sigma'3 exch))))))
(Fr exch
  (Assertion_update Erd c
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r)))
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r) (sort_update r nat PrepareLSORT) d)
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat

(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT nat E PrepareLSORT r) nat d)
(coerce_expression' QuicksortVAR_DecSetoid c
(sort_update d nat (sort_update r nat QuicksortSORT))
(sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(coerce_sort_update (coerce_sort_update Prepare_coerce' nat r) nat d)
([sigma'3:FS (sort_update d nat (sort_update r nat QuicksortSORT))]
suc (sigma'3 c)))))
(Assertion_update Erd d
(coerce_expression' QuicksortVAR_DecSetoid d
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r)))
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d))
(AccSort_update QuicksortSORT nat (Env_add E r) (sort_update r nat PrepareLSORT)
d)
(coerce_expression' QuicksortVAR_DecSetoid d
(sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E)))
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r)))
(coerce_sort_update (AccSort_update QuicksortSORT nat E PrepareLSORT r) nat d)
(coerce_expression' QuicksortVAR_DecSetoid d
(sort_update d nat (sort_update r nat QuicksortSORT))
(sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E))
) (coerce_sort_update (coerce_sort_update Prepare_coerce' nat r) nat d)
([sigma'3:FS (sort_update d nat (sort_update r nat QuicksortSORT))]
pred (sigma'3 d)))))
([Z'3:qsSTATE']
[tau:STATE QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))]
(Spec_Prepare_inv ([sigma'5:qsSTATE]local sigma'5 d) Z'3.1.1.1
(drPrepareLSORT2qsSTATE tau) ∧
Lt (suc (lookup Erd tau d) - lookup Erd tau c) t)))) vexch) Z
(STATE_update (Env_add Erd exch) X
(eval (Env_add Erd exch) sigma
(coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT)) Erd)
)
(AccSort QuicksortSORT
(sort_update exch nat (sort_update d nat (sort_update r nat PrepareLSORT)))
(Env_add Erd exch))
(AccSort_update QuicksortSORT nat Erd
(sort_update d nat (sort_update r nat PrepareLSORT)) exch)
(coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r))))
(sort_update exch nat
(AccSort QuicksortSORT (sort_update d nat (sort_update r nat PrepareLSORT))
(Env_add (Env_add E r) d)))
(coerce_sort_update (AccSort_update QuicksortSORT nat (Env_add E r)
(sort_update r nat PrepareLSORT) d) nat exch)
(coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat
(sort_update d nat (sort_update r nat (AccSort QuicksortSORT PrepareLSORT E))
))
(sort_update exch nat
(sort_update d nat
(AccSort QuicksortSORT (sort_update r nat PrepareLSORT) (Env_add E r))))
(coerce_sort_update (coerce_sort_update (AccSort_update QuicksortSORT nat E
PrepareLSORT r) nat d) nat exch)
(coerce_expression' QuicksortVAR_DecSetoid X
(sort_update exch nat (sort_update d nat (sort_update r nat QuicksortSORT)))
(sort_update exch nat
(sort_update d nat
(sort_update r nat (AccSort QuicksortSORT PrepareLSORT E))))
(coerce_sort_update (coerce_sort_update (coerce_sort_update Prepare_coerce'
nat r) nat d) nat exch)
([sigma'3:FS (sort_update exch nat
 (sort_update d nat (sort_update r nat QuicksortSORT)))]

  update (sigma'3 X) (sigma'3 c) (sigma'3 X (sigma'3 d)))))))) sigma));

...

Discharge t;

Module Prepare_vc4 Import quicksort_spec;
[vr,vd:nat];
Goal Pvc4 :
  SubRel ([Z:qsSTATE']
   [tau:STATE QuicksortSORT
      (sort_update d nat (sort_update r nat PrepareLSORT))]
   (Spec_Prepare_inv ([sigma:qsSTATE]local sigma d) Z.1.1.1
      (drPrepareLSORT2qsSTATE tau) ∧
      is_false (eval Erd tau
([sigma:FS (AccSort QuicksortSORT
      (sort_update d nat
        (sort_update r nat PrepareLSORT))
      (Env_add (Env_add E r) d))]
  le (sigma c) (sigma d)))))
  (Fr d (Fr r Spec_Prepare_post vr) vd);

...

Discharge vr;

Module Prepare_vc5 Import quicksort_spec;
[vexch:PrepareLSORT exch];
Goal Pvc5 :
  SubRel ([Z:qsSTATE']
                     [sigma:STATE QuicksortSORT (sort_update exch nat PrepareLSORT)]
                     (Fr exch
                       ([Z'3:qsSTATE'][sigma'4:STATE QuicksortSORT PrepareLSORT]
                        (Spec_Prepare_pre Z'3 sigma'4 ∧
                         is_true (eval E sigma'4
                                     ([sigma'5:FS (AccSort QuicksortSORT PrepareLSORT E)]
                                      lt (sigma'5 X (sigma'5 b)) (sigma'5 X (sigma'5 a))))))
                       vexch Z sigma ∧
                      Eq (coerce (sort_update_lookup exch nat PrepareLSORT)
                           (local sigma exch))
                       (eval E (STATE_remove_local exch vexch sigma)
                         ([sigma'3:FS (AccSort QuicksortSORT PrepareLSORT E)]
                          sigma'3 X (sigma'3 a)))))
             ([Z:qsSTATE'][sigma:STATE QuicksortSORT (sort_update exch nat PrepareLSORT)]
              Assertion_update (Env_add E exch) X
               ([sigma'3:FS (AccSort QuicksortSORT (sort_update exch nat PrepareLSORT)
                                (Env_add E exch))]
                update (sigma'3 X) (sigma'3 b) (sigma'3 exch))
               (Fr exch
                 ([Z'3:qsSTATE'][sigma'4:STATE QuicksortSORT PrepareLSORT]
                  Assertion_update (Env_add E r) c
                   ([sigma'5:FS (AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
                                     (Env_add E r))]suc (sigma'5 a))
                   ([Z'5:qsSTATE']
                     [sigma'6:STATE QuicksortSORT (sort_update r nat PrepareLSORT)]
                     Spec_Prepare_inv ([sigma'7:qsSTATE]local sigma'7 d) Z'5.1.1.1
                      (drPrepareLSORT2qsSTATE (STATE_add_local (Env_add E r) sigma'6 d
                                                   ([sigma'7:FS (AccSort QuicksortSORT
                                                                     (sort_update r nat
                                                                       PrepareLSORT) (Env_add E r)
                                                                   )]pred (sigma'7 b))))) Z'3
                   (STATE_add_local E sigma'4 r
                      ([sigma'5:FS (AccSort QuicksortSORT PrepareLSORT E)]
                       sigma'5 X (sigma'5 a)))) vexch) Z
               (STATE_update_internal QuicksortSORT (sort_update exch nat PrepareLSORT)

                 (Env_add E exch) X
                 (eval (Env_add E exch) sigma
                   ([sigma'3:FS (AccSort QuicksortSORT (sort_update exch nat PrepareLSORT)
                                     (Env_add E exch))]
                     update (sigma'3 X) (sigma'3 a) (sigma'3 X (sigma'3 b)))) sigma));

...

Discharge vexch;

Module Prepare_vc6 Import quicksort_spec;
Goal Pvc6 :
  SubRel ([Z:qsSTATE'][sigma:STATE QuicksortSORT PrepareLSORT]
                     (Spec_Prepare_pre Z sigma ∧
                      is_false (eval E sigma
                                  ([sigma'3:FS (AccSort QuicksortSORT PrepareLSORT E)]
                                   lt (sigma'3 X (sigma'3 b)) (sigma'3 X (sigma'3 a))))))
             ([Z:qsSTATE'][sigma:STATE QuicksortSORT PrepareLSORT]
              Assertion_update (Env_add E r) c
               ([sigma'3:FS (AccSort QuicksortSORT (sort_update r nat PrepareLSORT)
                                (Env_add E r))]suc (sigma'3 a))
               ([Z'3:qsSTATE']
                [sigma'4:STATE QuicksortSORT (sort_update r nat PrepareLSORT)]
                Spec_Prepare_inv ([sigma'5:qsSTATE]local sigma'5 d) Z'3.1.1.1
                 (drPrepareLSORT2qsSTATE (STATE_add_local (Env_add E r) sigma'4 d
                                               ([sigma'5:FS (AccSort QuicksortSORT
                                                                (sort_update r nat PrepareLSORT)
                                                                (Env_add E r))]pred (sigma'5 b)))
                  )) Z
               (STATE_add_local E sigma r
                 ([sigma'3:FS (AccSort QuicksortSORT PrepareLSORT E)]sigma'3 X (sigma'3 a))
                ));

...

Module Prepare_correct

  Import quicksort_spec Hadmiss
         Prepare_vc1 Prepare_vc2 Prepare_vc3
      Prepare_vc4 Prepare_vc5 Prepare_vc6;
[Gamma : HContext QuicksortSORT PrepareLSORT];
(*  Corresponds to Lemma 4.15 on page 121. *)
Goal Prepare_correct :
  HD S0 E Gamma Spec_Prepare_pre (coerce_prog Prepare_coerce' Prepare)
           Spec_Prepare_post;
Refine HDSeq;
Refine -0 HDNew_LC; intros -0 vr;
Refine -0 HDSeq;
Refine -0 HDNew_LC;
Intros -1 z sigma;
  Refine Spec_Prepare_inv ([sigma:qsSTATE]sigma.local d) z.1.1.1;
  Refine drPrepareLSORT2qsSTATE sigma;
intros -0 vd Erd; Refine -0 HDConr;
Refine -0 HDWhile;
  (** termination measure **)
  Refine -2 complete_induction;
  Intros -1 sigma;
     Refine (suc (lookup Erd sigma d)) - (lookup Erd sigma c);

intros -0 Then
  Repeat (Refine -0 HDIfthenelse Else Refine -0 HDSeq Else
           Refine -0 HDNew Else Refine -0 HDAssign);

Refine HDAssign_lemma; Refine +1 HDAssign_lemma;
Refine Pvc1; Refine Pvc2;
intros vexch; Refine HDSeq; Refine -0 HDAssign;
Refine HDAssign_lemma;
Refine Pvc3;
Expand coerce_expression coerce_expression';
Refine Pvc4;
Refine HDAssign;
Repeat (Refine HDIfthenelse Else Refine HDSeq Else
           Refine HDNew Else Refine HDAssign);
intros vexch; Refine HDSeq; Refine -0 HDAssign;
Refine HDAssign_lemma;
Expand coerce_expression coerce_expression'; Expand STATE_update;
Refine Pvc5;
Refine HDInv_lemma;
Expand coerce_expression coerce_expression';
Refine Pvc6;
SaveFrozen Prepare_correct;
(*Show*)
Discharge Gamma;
Module quicksort Import lib_nat_rels Prepare_correct;
[eq = QuicksortVAR_DecSetoid.DecSetoid_eq]
[E':Env QuicksortVAR_DecSetoid]
[EQS [x:QuicksortVAR_DecSetoid.DecSetoid_carrier] =
  andalso (inv (eq x Pa)) (andalso (inv (eq x Pb)) (andalso (inv (eq x X))
  (E' x)))]
[SQP = [z,sigma:qsSTATE]
        Spec_Quicksort_post (z.global Pa) (z.global Pb)
                               (sigma.global X) (z.global X)];
Freeze Prepare;
(*  Corresponds to Lemma 4.14 on page 120. *)
Goal quicksort_correct :
  HD  S0 EQS (HNone ??) (Spec_Quicksort_pre EQS) (call ?) SQP;

...

B.6 Additional Background Theory

Module lib_eq Import lib_logic;
[T|Type];
(*  Corresponds to Definition A.3 on page 135. *)
[Eq = [x,y:T]{P:T->Prop}(P x)->P y : T->T->Prop];
[Eq_refl = [t:T][P:T->Prop][h:P t]h : refl Eq];
(*  Corresponds to Axiom 1 on page 135. *)
$[Eq_subst : {x,y|T}{p:Eq x y}{C:T->Type}(C(x)) -> C(y)];

DischargeKeep T;
$[Eq_subst_comp: {C:T->Type}{a:T}{b:C a}Eq (Eq_subst (Eq_refl a) C b) b];
[[T|Type][C:T->Type][a:T][b:C a]
 (Eq_subst (Eq_refl a) C) b ==> b
];
(*  Corresponds to Axiom 2 on page 136. *)
$[Pr_Ir:{P|Prop}{x,y:P}Eq x y];
(*  Corresponds to Axiom 3 on page 137. *)
$[Extensionality_dep :
  {A|Type}{B|A->Type}{f,g:{x:A}B x}
  ({x:A}Eq (f x) (g x)) -> Eq f g]
[Extensionality [A,B|Type][f,g:A->B] = Extensionality_dep|A|([_:A]B) f g];
Discharge T;
(* Make Theorems as powerful as possible *)
[trivType={T|Type}T->T];
[emptyType={T|Type}T];
Configure Theorems trivType emptyType Eq_subst;

Module tms_rel Import lib_rel;
[T|Type];
[reflclose [R:Rel T T]
  = [tau,sigma:T](R tau sigma) ∨ (Eq tau sigma) : Rel T T];
Discharge T;

Module decsetoid Import lib_bool_thms;
[P|Type(0)][eq:P->P->bool]
[Eqchar  = {x,y:P}iff (Eq x y) (is_true (eq x y))];
Goal Eqchar' : Eqchar -> {x,y:P}iff (not (Eq x y)) (is_false (eq x y));

...

Discharge P;
(*  Corresponds to Definition 2.1 on page 10. *)
Record [DecSetoid : Type(0)]
Fields [DecSetoid_carrier|Type(0)]
        [DecSetoid_eq|DecSetoid_carrier->DecSetoid_carrier->bool]
        [DecSetoid_char:Eqchar DecSetoid_eq];
[decsetoid:DecSetoid];
Goal DecSetoid_eq_refl :
  {x:decsetoid.DecSetoid_carrier}is_true (decsetoid.DecSetoid_eq x x);

...

Goal DecSetoid_eq_sym :
  {x,y:decsetoid.DecSetoid_carrier}
  Eq (decsetoid.DecSetoid_eq x y) (decsetoid.DecSetoid_eq y x);

...

Discharge decsetoid;

Module DepUpdate Import lib_eq decsetoid;
Goal diagsubstlem :
  {A|Type(1)}{B:A->Type(1)}{a:A}{p:Eq a a}{b:B a}Eq (Eq_subst p B b) b;

...

Unfreeze Eq;
(*  Corresponds to Lemma A.4 on page 136. *)
Goal depsubst :
  {A:Type}{B:A->Type}{C:{a:A}(B a)->Type}
  {a,a':A}{p:Eq a a'}{b:B a}(C a' (Eq_subst p B b)) -> C a b;
intros ______;
Refine Eq_subst ? [a:A]({p:Eq a a'}{b:B a}(C a' (Eq_subst p B b))->C a b);
Refine +1 Eq_sym; Assumption +1;
intros p'; Qrepl Pr_Ir p' (Eq_refl a');
intros; Refine Eq_subst; Refine +1 Eq_subst_comp;
Immed;
Save depsubst;
Goal Eq_elim :
  {t|Type}{P:{x,y:t}(Eq x y)->Type}({x:t}P x x (Eq_refl x))->
  {x,y:t}{p:Eq x y}P x y p;

...

Goal Eq_subst_trans :
  {T|Type}{x,y|T}{xy:Eq x y}{yx:Eq y x}{C:T->Type}{b:C ?}
  Eq (Eq_subst xy C (Eq_subst yx C b)) b;

...

[A|Type][B,C|A -> Type][x,y|A][c1:Eq x y][c2:Eq (C x) (B x)];
Goal Eq_subst_trans_internal : Eq (C x) (B y);

...

DischargeKeep x;
Goal Eq_subst_trans' :
  {v:C x}
  Eq (Eq_subst c1 B (Eq_subst c2 (Id|Type(0)) v))
      (Eq_subst (Eq_subst_trans_internal c1 c2) (Id|Type(0)) v);

...

Discharge A;
[VAR|DecSetoid];
[SORT_internal = {x:VAR.DecSetoid_carrier}Type(0)]
[sort:SORT_internal]
[FS = {x:VAR.DecSetoid_carrier}sort x];
Goal sort_update :
  {y:VAR.DecSetoid_carrier}{T:Type(0)}{f:SORT_internal}SORT_internal;
Intros; Refine if (VAR.DecSetoid_eq x y) T (f x);
Save sort_update;
Goal sort_update_lookup :
  {y:VAR.DecSetoid_carrier}{T:Type(0)}{f:SORT_internal}
  Eq ((sort_update y T f) y) T;

...

Goal sort_update_lemma' :
  {x,y|VAR.DecSetoid_carrier}{different:is_false (VAR.DecSetoid_eq x y)}
  {T:Type(0)}{f:SORT_internal}Eq ((sort_update y T f) x) (f x);

...

[x:VAR.DecSetoid_carrier][t:sort x][sigma:FS];
Goal FS_update_internal : FS;
intros; Intros y;
Refine bool_elim [c:bool](Eq (VAR.DecSetoid_eq y x) c)->sort y;

  Refine -0 Eq_refl;
  (* x=y *)
  intros xEQy; Refine Eq_subst (Eq_sym (snd (VAR.DecSetoid_char ??) xEQy));
  Refine t;
  (* x<>y *)
  intros; Refine sigma y;
Save FS_update_internal;
Goal FS_update_lemma : Eq (FS_update_internal x) t;
Expand FS_update_internal;
[B [b:bool][p:Eq (VAR.DecSetoid_eq x x) b]
  = Eq (bool_elim ([c:bool](Eq (DecSetoid_eq VAR x x) c)->sort x)
               ([xEQy:Eq (DecSetoid_eq VAR x x) true]
                Eq_subst (Eq_sym (snd (DecSetoid_char VAR x x) xEQy)) sort t)
               ([_:Eq (DecSetoid_eq VAR x x) false]sigma x)
             b p) t];
Equiv B ? ?;
Refine depsubst ??;
Refine true; Refine DecSetoid_eq_refl;
Refine diagsubstlem;
Save FS_update_lemma;
Goal FS_update_lemma' :
  {y:VAR.DecSetoid_carrier}{different:not (Eq x y)}
  Eq (FS_update_internal y) (sigma y);
intros; Expand FS_update_internal;
[B [b:bool][p:Eq (VAR.DecSetoid_eq y x) b]
  = Eq (bool_elim ([c:bool](Eq (DecSetoid_eq VAR y x) c)->sort y)
               ([xEQy:Eq (DecSetoid_eq VAR y x) true]
                Eq_subst (Eq_sym (snd (DecSetoid_char VAR y x) xEQy)) sort t)
               ([_:Eq (DecSetoid_eq VAR y x) false]sigma y)
              b p) (sigma y)];
Equiv B ??; Refine depsubst;
Refine +1 fst (Eqchar' ? VAR.DecSetoid_char ??);
Intros _; Refine different; Refine Eq_sym; Immed;
Expand B; Refine Eq_refl;
Save FS_update_lemma';
Discharge sort;
Goal FS_lookup_update :
  {sort|SORT_internal}{y:VAR.DecSetoid_carrier}{sigma:FS sort}
  Eq (FS_update_internal sort y (sigma y) sigma)
      sigma;

...

Goal FS_update_dyn
  : {sort|SORT_internal}{y:VAR.DecSetoid_carrier}
     {T|Type(0)}{t:T}{sigma:FS sort}
     FS (sort_update y T sort);
Intros; Expand sort_update;
Induction (DecSetoid_eq VAR x y);
  (** x=y **)
  Refine t;
  (** x<>y **)
  Refine sigma x;
Save FS_update_dyn;
Goal FS_update_dyn'
  : {sort|SORT_internal}{y:VAR.DecSetoid_carrier}{T|Type(0)}{t:sort y}
     {sigma:FS (sort_update y T sort)}FS sort;

...

Goal FS_update_dyn'_update_dyn :
  {sort|SORT_internal}{y:VAR.DecSetoid_carrier}{t:y.sort}{U|Type(0)}
  {u:U}{sigma:FS sort}
  Eq (FS_update_dyn' y t (FS_update_dyn y u sigma))
      (FS_update_internal sort y t sigma);

...

Goal coerce : {T,U|Type}{eq:Eq T U}{a:T}U;
  intros; Qrepl Eq_sym eq; Immed;
Save coerce;
Goal coerce2 : {T,U|Type(2)}{eq:Eq T U}{a:T}U;
  intros; Qrepl Eq_sym eq; Immed;
Save coerce2;
Goal ifcoerce :
  {b,c|bool}{T,U|Type}{eq:Eq b c}Eq (if b T U) (if c T U);

...

Goal  FS_update_dyn'_lookup :
  {vardecl | (DecSetoid_carrier VAR)->Type(0)}{T | Type(0)}
  {x : DecSetoid_carrier VAR}{v : vardecl x}
  {sigma : FS (sort_update x T vardecl)}
  Eq (FS_update_dyn' x v sigma x) v;

...

[sort|SORT_internal][y:VAR.DecSetoid_carrier]
  [U|Type(0)][v:U][sigma:FS sort]
  $[c = sort_update_lookup y U sort];
Goal FS_update_dyn_lookup
  : Eq (FS_update_dyn y v sigma y)
        (coerce (Eq_sym (sort_update_lookup y U sort)) v);

...

Goal FS_lookup_update_dyn
  : Eq (coerce (sort_update_lookup y U sort) (FS_update_dyn y v sigma y)) v;

...

[x|VAR.DecSetoid_carrier];
Goal FS_update_dyn_lemma':
  {different:is_false (VAR.DecSetoid_eq y x)}
  Eq (FS_update_dyn x v sigma y)
      (Eq_subst (Eq_sym (sort_update_lemma' different U sort))
        (Id|Type(0)) (sigma y));

...

Discharge sort;
Goal FS_update_dyn_dyn' :
  {vardecl | (DecSetoid_carrier VAR)->Type(0)}{T | Type(0)}
  {x : DecSetoid_carrier VAR}{v : vardecl x}
  {sigma : FS (sort_update x T vardecl)}
  Eq sigma
     (FS_update_dyn x (coerce (sort_update_lookup x T vardecl) (sigma x))
                       (FS_update_dyn' x v sigma));

...

[EXTEQ [sortA,sortB:SORT_internal]
  = {x:DecSetoid_carrier VAR}Eq (sortA x) (sortB x)];
Goal EXTEQrefl : {sort:SORT_internal}EXTEQ sort sort;

...

Goal sort_dupdate :

  {y:DecSetoid_carrier VAR}{sort:SORT_internal}{T:Type(0)}
  EXTEQ (sort_update y (sort y) (sort_update y T sort)) sort;

...

[lsortA|SORT_internal];
Goal FS_coerce :
  {lsortB|SORT_internal}
  {coerce:EXTEQ lsortA lsortB}{f:FS lsortA}FS lsortB;
Intros; Refine Eq_subst (coerce1 x) [T:Type]T; Refine f x;
Save FS_coerce;
Goal FS_coerce_redundant :
  {f:FS lsortA}Eq (FS_coerce (EXTEQrefl ?) f) f;

...

Discharge lsortA;
[y:VAR.DecSetoid_carrier][T:Type(0)][sort:SORT_internal];
[v:sort y][sigma:FS (sort_update y T sort)];
Goal FS_update_dyn'2dyn
  : Eq (FS_update_dyn' y v sigma)
        (FS_coerce (sort_dupdate y sort T) (FS_update_dyn y v sigma));

...

[FS_update [sort|SORT_internal] = FS_update_internal sort];
Discharge VAR;
[SORT [VAR:DecSetoid] = SORT_internal|VAR];
Goal sort_update_can :
  {VAR|DecSetoid}{C:(SORT VAR)->Prop}{sort|SORT VAR}
  {x,y|VAR.DecSetoid_carrier}{Tx,Ty:Type(0)}
  {sort_update_refl : C (sort_update x Tx sort)}
  {sort_update_sym  : C (sort_update y Ty (sort_update x Tx sort))}
  C (sort_update x Tx (sort_update y Ty sort));

...

(* Transitive Closure of a Relation *)
Module TransClosure Import lib_rel;
Inductive [TransClosure : T->T->Prop]
Relation NoReductions Inversion
Parameters [T|Type][R:Rel T T]
Constructors
  [TransClosure_emb : {x,y:T}(R x y) -> TransClosure x y]
  [TransClosure_plus: {x,y,z:T}(R x y) -> (TransClosure y z) ->
                                          TransClosure x z];
Goal TransClosureTrans : trans TransClosure;

...

Discharge T;

Bibliography

Abrial, J.-R. (1996), The B Book - Assigning Programs to Meanings, Cambridge University Press.

Aczel, P. (1982a), A note on program verification. Unpublished, see also (Jones 1986).

Aczel, P. (1982b), A system of proof rules for the correctness of iterative programs – some notational and organisational suggestions. Unpublished.

Ah-kee, J. A. (1990), ‘Proof obligations for blocks and procedures’, Formal Aspects of Computing 2, 312–330.

America, P. & de Boer, F. (1990), ‘Proving total correctness of recursive procedures’, Information and Computation 84(2), 129–162.

Apt, K. R. (1981), ‘Ten years of Hoare’s logic: A survey – part I’, ACM Transactions on Programming Languages and Systems 3(4), 431–483.

Apt, K. R. & Meertens, L. G. L. T. (1980), ‘Completeness with finite systems of intermediate assertions for recursive program schemes’, SIAM Journal on Computing 9(4), 665–671.

Apt, K. R. & Olderog, E.-R. (1991), Verification of Sequential and Concurrent Programs, Texts and Monographs in Computer Science, Springer, New York.

Bergstra, J. A. & Tucker, J. V. (1982), ‘Some natural structures which fail to prossess a sound and decidable Hoare-like logic for their while-programs’, Theoretical Computer Science 17, 303–315.

Burstall, R. & McKinna, J. (1993), Deliverables: a categorical approach to program development in type theory, in A. M. Borzyszkowski & S. Sokołowski, eds, ‘Proceedings of MFCS ’93’, Vol. 711 of Lecture Notes in Computer Science, Springer-Verlag, pp. 32–67. An earlier version appeared as LFCS technical report ECS-LFCS-92-242 in 1992.

Caplan, J. E. (1995), Formalizing Hoare logic, in U. S. Reddy, ed., ‘Proceedings of SIPL ’95’, University of Illinois at Urbana-Champaign, pp. 1–18. Available as Technical Report UIUCDCS-R-95-1900. *http://acsl.cs.uiuc.edu/˜ caplan/hllf/sipl95/sipl95.dvi

Clarke Jr., E. M. (1979), ‘Programming language constructs for which it is impossible to obtain good Hoare axiom systems’, Journal of the ACM 26(1), 129–147.

Clarke, Jr., E. M., German, S. M. & Halpern, J. Y. (1983), ‘Effective axiomatizations of Hoare logics’, Journal of the ACM 30(3), 612–636.

Clint, M. (1981), ‘On the use of history variables’, Acta Informatica 16, 15–30.

Cook, S. A. (1978), ‘Soundness and completeness of an axiom system for program verification’, SIAM Journal on Computing 7(1), 70–90.

Coq (1998), ‘The Coq project’.

*http://pauillac.inria.fr/coq/

Coquand, T., Nordström, B., Smith, J. M. & von Sydow, B. (1994), ‘Type theory and programming’, EATCS Bulletin 52, 203–228.

Cousot, P. (1990), Methods and logics for proving programs, in J. van Leeuwen, ed., ‘Handbook of Theoretical Computer Science’, Vol. B: Formal Models and Semantics, Elsevier, chapter 15, pp. 841–993.

Dahl, O.-J. (1992), Verifiable Programming, International Series in Computer Science, Prentice Hall.

Dahl, O.-J., Dijkstra, E. & Hoare, C. A. R. (1972), Structured Programming, Academic Press.

de Bakker, J. (1980), Mathematical Theory of Program Correctness, Prentice Hall.

Dershowitz, N. & Manna, Z. (1979), ‘Proving termination with multiset orderings’, Communications of the ACM 22(8), 465–475.

Dijkstra, E. W., ed. (1990), Formal Development of Programs and Proofs, University of Texas at Austin Year of Programming Series, Addison-Wesley.

Floyd, R. (1967), Assigning meanings to programs, in J. T. Schwartz, ed., ‘Proc. Symp. in Applied Mathematics’, Vol. 19, pp. 19–32.

Foley, M. & Hoare, C. A. R. (1971), ‘Proof of a recursive program: QUICKSORT’, The Computer Journal 14(4), 391–395.

Gehani, N. & MacGettrick, A., eds (1986), Software Specification Techniques, Addison-Wesley.

Goguen, H. (1994), The metatheory of UTT, in P. P. Dybjer, B. Nordström & J. M. Smith, eds, ‘Types for Proofs and Programs, International Workshop (TYPES ’94); Bastad, Sweden’, Vol. 996 of Lecture Notes in Computer Science, Springer-Verlag, pp. 60–82.

Gordon, M. J. (1989), Mechanizing programming logics in higher order logic, in G. Birtwhistle & P. Subrahmanyam, eds, ‘Current Trends in Hardware Verification and Automated Theorem Proving (Banff, Alberta)’, number 15 in ‘Workshops in Computing’, Springer-Verlag, pp. 387–439.

Gorelick, G. A. (1975), A complete axiomatic system for proving assertions about recursive and non-recursive programs, Technical Report 75, Department of Computer Science, University of Toronto.

Grabowski, M. (1985), On the relative incompleteness of logics, in Parikh (1985), pp. 118–127. Extended Abstract.

Gries, D. (1981), The Science of Computer Programming, Springer.

Gries, D. (1982), ‘A note on a standard strategy for developing loop invariants and loops’, Science of Computer Programming 2, 207–214.

Gries, D. & Levin, G. M. (1980), ‘Assignment and procedure call proof rules’, ACM Transactions on Programming Languages and Systems 2(4), 564–579.

Harel, D. (1980), ‘Proving the correctness of regular deterministic programs: A unifying survey using dynamic logic’, Theoretical Computer Science 12, 61–81.

Harper, R., Honsell, F. & Plotkin, G. (1993), ‘A framework for defining logics’, Journal of the ACM 40(1), 143–184. Preliminary version appeared in Proc. 2nd IEEE Symposium on Logic in Computer Science, 1987, 194–204.

Hoare, C. A. R. (1961), ‘Algorithm 63, partition; algorithm 64, quicksort; algorithm 65, find’, Communications of the ACM 4(7), 321–322.

Hoare, C. A. R. (1969), ‘An axiomatic basis for computer programming’, Communications of the ACM 12, 576–580. Also in (Hoare & Jones 1989).

Hoare, C. A. R. (1971), Procedures and parameters: An axiomatic approach, in E. Engeler, ed., ‘Symposium on Semantics of Algorithmic Languages’, Vol. 188 of Lecture Notes in Mathematics, Springer-Verlag, pp. 102–116. Also in (Hoare & Jones 1989).

Hoare, C. A. R. & Jones, C. B., eds (1989), Essays in Computing Science, International Series in Computer Science, Prentice Hall.

Hofmann, M. (1995), Extensional concepts in intensional type theory, PhD thesis, Laboratory for Foundations of Computer Science, University of Edinburgh. *http://www.dcs.ed.ac.uk/lfcsreps/EXPORT/95/ECS-LFCS-95-327/

Hofmann, M. (1997), ‘Semantik und Verifikation’. Lecture Notes for a course held in Winter term 1997/98 at the University of Marburg. In German. *http://www.mathematik.tu-darmstadt.de/˜ mh/skript.ps.gz

HOL (1998), ‘The HOL system’.

*http://www.cl.cam.ac.uk/Research/HVG/HOL/

Homeier, P. V. (1995), Trustworthy Tools for Trustworthy Programs: A Mechanically Verified Verification Condition Generator for the Total Correctness of Procedures, PhD thesis, University of California, Los Angeles. *http://www.cis.upenn.edu/˜ homeier/phd.html

Homeier, P. V. & Martin, D. F. (1996), Mechanical verification of mutually recursive procedures, in M. A. McRobbie & J. K. Slaney, eds, ‘Automated Deduction – CADE-13’, Vol. 1104 of Lecture Notes in Artificial Intelligence, Springer-Verlag, New Brunswick, NJ, USA, pp. 201–215. 13th International Conference on Automated Deduction.

Huet, G. & Plotkin, G. D., eds (1990), Electronic Proceedings of the First Annual BRA Workshop on Logical Frameworks (Antibes, France). *http://www.dcs.ed.ac.uk/lfcsinfo/research/types_bra/proc/index.html

Ireland, A. & Stark, J. (1997), On the Automatic Discovery of Loop Invariants, in ‘Proceedings of the Fourth NASA Langley Formal Methods Workshop – NASA Conference Publication 3356’. Also available from Dept. of Computing and Electrical Engineering, Heriot-Watt University, Research Memo RM/97/1.

Jones, C. B. (1986), Systematic program development, in Gehani & MacGettrick (1986), pp. 89–109.

Jones, C. B. (1990), Systematic Software Development Using VDM, International Series in Computer Science, 2 edn, Prentice Hall.

Jones, C. B. & Shaw, R. C. (1990), Case Studies in Systematic Software Development, Prentice Hall.

Knuth, D. E. (1986), The TEXbook, Addison-Wesley.

Lauer, P. E. (1971), Consistent formal theories of the semantics of programming languages, Technical Report TR.25.121, IBM Lab, Vienna.

Lego (1998), ‘The LEGO proof assistant’.

*http://www.dcs.ed.ac.uk/home/lego

Liskov, B. H. & Berzins, V. (1986), An appraisal of program specifications, in Gehani & MacGettrick (1986), pp. 3–23.

London, R. L., Guttag, J. V., Horning, J. J., Lampson, B. W., Mitchell, J. G. & Popek, G. J. (1978), ‘Proof rules for the programming language Euclid’, Acta Informatica 10, 1–26.

Luo, Z. (1990), An extended Calculus of Constructions, Thesis ECS-LFCS-90-118, Laboratory for Foundations of Computer Science, University of Edinburgh.

Luo, Z. (1994), Computation and Reasoning: A Type Theory for Computer Science, Oxford University Press.

Manna, Z. & Pnueli, A. (1974), ‘Axiomatic approach to total correctness of programs’, Acta Informatica 3, 243–263.

Martin-Löf, P. (1984), Intuitionistic Type Theory, Bibliopolis·Napoli.

Mason, I. A. (1987), Hoare’s logic in the LF, Technical Report 32, Laboratory for Foundations of Computer Science, University of Edinburgh.

McBride, C. (1998), Inverting inductively defined relations in LEGO. Still to appear in the proceedings of TYPES’96. *ftp://ftp.dcs.ed.ac.uk/pub/lego/McBrideTypes96.ps

McKinna, J. H. (1992), Delivarables: A Categorical Approach to Program Development in Type Theory, PhD thesis, Laboratory for Foundations of Computer Science, University of Edinburgh. *ftp://ftp.dcs.ed.ac.uk/pub/jhm/thesis.ps.gz Morgan, C. (1988), ‘Procedures, parameters, and abstraction: separate concerns’,

Science of Computer Programming 11, 17–27.

Morris, J. H. (n.d.), Comments on “procedures and parameters”. Undated and unpublished.

Nipkow, T. (1996), Winskel is (almost) right: Towards a mechanized semantics textbook, in V. Chandru & V. Vinay, eds, ‘Proceedings of FSTTCS ’96’, Vol. 1180 of Lecture Notes in Computer Science, Springer-Verlag, pp. 180–192. *http://www4.informatik.tu-muenchen.de/˜ nipkow/pubs/fsttcs96.dvi.gz

Nipkow, T. (1998), ‘Winskel is (almost) right: Towards a mechanized semantics textbook’, Formal Aspects of Computing ? To appear. This is an extended version of (Nipkow 1996). *http://www4.informatik.tu-muenchen.de/˜ nipkow/pubs/winskel.html

Norrish, M. (1996), Derivation of verification rules for C from operational definitions, in J. von Wright, J. Grundy & J. Harrison, eds, ‘Supplementary Proceedings of the 9th International Conference on Theorem Proving in Higher Order Logics: TPHOLs’96’, number 1 in ‘TUCS General Publications’, Turku Centre for Computer Science, pp. 69–75.

Norrish, M. (1997), An abstract dynamic semantics for C, Technical Report 421, Computer Laboratory, University of Cambridge. *http://www.cl.cam.ac.uk/ftp/papers/reports/TR421-mn200-c-semantics.dvi.gz

Olderog, E.-R. (1981), ‘Sound and complete Hoare-like calculi based on copy rules’, Acta Informatica 16, 161–197.

Olderog, E.-R. (1983), ‘On the notion of expressiveness and the rule of adaptation’, Theoretical Computer Science 24, 337–347.

Pandya, P. & Joseph, M. (1986), ‘A structure-directed total correctness proof rule for recursive procedure calls’, The Computer Journal 29(6), 531–537.

Parikh, R., ed. (1985), Logics of Programs, Vol. 193 of Lecture Notes in Computer Science, Springer-Verlag.

Paulson, L. C. (1990), ml for the Working Programmer, Cambridge University Press.

Paulson, L. C. & Nipkow, T. (1998), Isabelle.

*http://www.cl.cam.ac.uk/Research/HVG/isabelle.html Plotkin, G. (1981), A structural approach to operational semantics, Report DAIMI FN-

19, Computer Science Department, Aarhus University.

Pollack, R. (1992), Implicit syntax. An earlier version of this paper appeared in (Huet & Plotkin 1990). *ftp://ftp.dcs.ed.ac.uk/pub/lego/ImplicitSyntax.ps.Z

Pollack, R. (1994), The Theory of Lego, A Proof Checker for the Extended Calculus of Constructions, PhD thesis, Laboratory for Foundations of Computer Science, University of Edinburgh.

Pollack, R. (1998), How to believe a machine-checked proof, in G. Sambin & J. M. Smith, eds, ‘Twenty-Five Years of Constructive Type Theory’, Oxford University Press. To appear. *ftp://ftp.dcs.ed.ac.uk/pub/lego/pollack-belief.ps.gz

Reynolds, J. C. (1981), The Craft of Programming, International Series in Computer Science, Prentice Hall.

Reynolds, J. C. (1982), Idealized Algol and its specification logic, in D. Néel, ed., ‘Tools & Notions for Program Construction’, Cambridge University Press.

Rushby, J. (1998), ‘SRI-CSL-PVS’.

*http://www.csl.sri.com/sri-csl-pvs.html

Schildt, H., American National Standards Institute, International Organization for Standardization, International Electrotechnical Commission & ISO/IEC JTC 1 (1990), The annotated ANSI C standard: American National Standard for Programming Languages C: ANSI/ISO 9899-1990, Osborne/McGraw-Hill, Berkeley, CA, USA.

Schreiber, T. (1997), Auxiliary variables and recursive procedures, in M. Bidoit & M. Dauchet, eds, ‘Proceedings of TAPSOFT ’97’, Vol. 1214 of Lecture Notes in Computer Science, Springer-Verlag, Lille, France, pp. 697–711. *http://www.dcs.ed.ac.uk/home/tms/lego/tapsoft97/

Sieber, K. (1981), A new Hoare-calculus for programs with recursive parameterless procedures, Technical Report A 81/02, Fachbereich 10 – Informatik, Universität des Saarlandes, Saarbrücken.

Sieber, K. (1985), A partial correctness logic for procedures, in Parikh (1985), pp. 320– 342.

Sokołowski, S. (1977), Total correctness for procedures, in J. Gruska, ed., ‘Sixth Mathematical Foundations of Computer Science (Tatranská Lomnica)’, Vol. 53 of Lecture Notes in Computer Science, Springer-Verlag, pp. 475–483.

Takeuti, G. (1975), Proof Theory, North-Holland.

Tarlecki, A. (1985), ‘A language of specified programs’, Science of Computer Programming 5, 59–81.

Trakhtenbrot, B. A., Halpern, J. Y. & Meyer, A. R. (1984), From denotational to operational and axiomatic semantics for ALGOL-like languages: An overview, in E. Clarke & D. Kozen, eds, ‘Logics of Programs 1983’, Vol. 164 of Lecture Notes in Computer Science, Springer-Verlag, pp. 474–500.

Underwood, J. (1997), ‘Computer aided formal reasoning’. Lecture Notes. *http://www.dcs.ed.ac.uk/home/lego/html/CAFR/96-97/lecture.html

Vickers, S. (1991), Formal implementation, in J. A. McDermid, ed., ‘Software engineer’s reference book’, Butterworth-Heinemann, London, chapter 25.

Wall, L., Christiansen, T. & Schwartz, R. L. (1996), Programming Perl, second edn, O’Reilly & Associates, Inc.

Winskel, G. (1993), The Formal Semantics of Programming Languages, MIT Press.

Index

Π, 5, 133 ∘, 21 λ, 133 ↦_{E}, 89

AccSort, 89 adaptation

rule of, 70–73 address, 78, 84, 88, 110 aliasing, 110 array, 15, 114–117

sorted, 116 assertion, 3, 36, 42, 49–50, 90

begin , 15 binding

dynamic, 85, 87, 99 static, 85, 87, 99, 109

block, see local program variable

C, 55, 77 classical logic, 37, 38 completeness, 2, 46–50, 90

of Hoare Logic, 32, 39–42, 65–69,

83, 103–107 of VDM, 26–29, 75, 85, 110 relative, 2, 47–50

completeness step, 67, 104 computation rule, 134 confidence, 3, 130 context, 59–61, 64 correctness

partial, 18, 30, 51, 73

total, 9, 18, 62–64, 73 correctness formula, 1, 20, 34, 35, 38, 52, 53, 74, 87, 98, 100, 102

dependent function

updating, 137–139 dependent type, 4–5, 51 determinism, 16–17, 19, 27, 55

Edinburgh Logical Framework, 51 elimination principle, 134 embedding

deep, 10, 52, 54, 55, 129 shallow, 10, 14, 49, 52, 119

end, 15 environment, 89, 98, 100 equality, 135–137

decidable, 10–11, 15, 137 extensional, 137 Leibniz, 135 exponentiation, 17, 23 expression, 13–14 expressiveness, 27, 46–50, 56

factorial, 35, 58, 69

freezing, see variable, program, freezing the value of

Gödels’s incompleteness theorem, 47 Gentzen, 59 global, 88 global program variable backup of, 88, 93 Hilbert, 59, 60 Hoare Logic, 29–46, 59–75, 81–84, 98–

110 semantics, 30, 36, 47

HOL, 51

induction, 6 inductively defined data type, 5 inductively defined relation, 5–7, 61 interpretation, 12–13, 47–50, 52

Herbrand-definable, 48 standard, 48, 49, 55 with finite domains, 48

invariant, 22, 24, 47, 50–51, 121 inversion, 6–7 Isabelle, 51

Java, 77

left-constructive, 70 left-maximal, 71 Lego, 133 lexicographical binding, see binding, static local, 88 local program variable, 75–112

preserving the value of a, 95, 100 uninitialised, 77, 119

lookup, 89

MGF, see Most General Formula model theory, 47 Most General Formula, 27, 39–42, 55,

107 mutually recursive procedures, 73–75

non-interference, 53

operation decomposition, 20 ordered list, 114–115

parameter, 110–112 call-by-name, 58, 110, 112, 114, 117,

119 call-by-value, 58, 110–112, 117, 119

partition, 115–118

Perl, 77

permutation, 116

pivot element, 118, 120

Prepare, 117, 120, 121, 125–128

procedure header, 74 non-recursive, 107 recursive, 35, 57–75, 85–110

procedure call graph, 74 proof irrelevance, 136 proof theory, 47 pseudo-induction hypothesis, see com-

pleteness step

quantification, 64 explicit, 35, 53 implicit, 7, 35, 38, 81

Quicksort, 113–129

recursion, 134–135 recursive depth, 66 preserving, 96 reference point, 42–44, 102

S_{0}, 58

scoping, 88, 91, 100 scoping intrusion, 99 semantics

axiomatic, 1

denotational, 17, 59, 65–66

structural operational, 16–17, 59, 80,

91–98 side-effect, 54–55 software reuse, 35 soundness, 2 of Hoare Logic, 32, 39, 65, 81, 102 of VDM, 25–26, 75, 85, 110

specification entrance, 74 input/output, 17, 29

specification logic, 53 stack

explicit, 78, 88 implicit, 79, 85, 88, 110

standard proof, 60 state space, 11–12, 78–79, 88 stepwise-refinement, 124 substitution, 135–136

dependent, 136 first-order, 12 in assertion, 10, 14, 31, 52–55, 64,

99 in program, 99

substitution lemma, 31, 55 syntax-directed methodology, 60

termination, 9, 17–18, 24, 27, 30–31,

62, 74, 114, 118, 122, 127, 135 type coercion, 93, 137

uL, 89, 100 updating, see substitution

variable

auxiliary, 3–4, 19, 34–43, 50–51,

62, 71, 72, 131 domain, 38, 41, 42, 100, 102

existential, 129 global, see global program variable implicit, 133 local, see local program variable program, 10–11

freezing the value of, 34, 46, 50, 87, 100

hooked, 20 VCG, see Verification Condition Gen-

erator VDM, see Vienna Development Method Verification Condition Generator, 56, 113,

122 Vienna Development Method, 19–29, 42– 46, 50, 51, 75, 84–85, 110

semantics, 22

weakest precondition, 32, 40, 47, 56, 73 well-founded induction, 135 well-founded relation, 135