Exits in the refinement calculus

23
Formal Aspects of Computing (1995) 7:54-76 @ 1995 BCS Formal Aspects of Computing Exits in the Refinement Calculus Steve King and Carroll Morgan Programming Research Group, Oxford University Computing Laboratory, Oxford, UK Keywords: Exceptions; Refinement calculus; Exception handling; Weakest pre- conditions Abstract. Although many programming languages contain exception handling mechanisms, their formal treatment - necessary for rigorous development - can be complex. Nevertheless, this paper presents a simple incorporation of exit commands and exception blocks into a rigorous program development method. The refinement calculus, chosen for the exercise, is a method of developing imperative programs. It is based on weakest preconditions, although they are not used explicitly during program construction; they merely justify the general method. In the style of the refinement calculus, program development laws are given that introduce and allow the manipulation of exits. The soundness of the new laws is shown using weakest preconditions (as for the existing refinement calculus laws). The extension of weakest preconditions needed to handle exits is a variation on earlier work of Cristian; the variation is necessary to handle nondeterminism. 1. Introduction Many programming languages contain an exception mechanism of some kind - a mechanism to determine the action to be taken when an unusual set of cir- cumstances arises, such as an arithmetic overflow or a division by zero. Although these mechanisms may make programs much shorter, they often make them more complex in behaviour, and therefore more difficult to construct correctly. For this reason, much of the recent work on the formal development of imperative programs has not dealt with exception handling 1. This paper seeks to rectify that omission by incorporating some of the ideas of Cristian [Cri84] into the Correspondence and offprint requests to : Steve King, Programming Research Group, Oxford University Computing Laboratory, Wolfson Building, Parks Rd, Oxford OX1 3QD, UK. 1 See [Spi90] for work on exceptions in functional languages.

Transcript of Exits in the refinement calculus

Page 1: Exits in the refinement calculus

Formal Aspects of Computing (1995) 7:54-76 @ 1995 BCS Formal Aspects

of Computing

Exits in the Refinement Calculus

Steve King and Carroll Morgan Programming Research Group, Oxford University Computing Laboratory, Oxford, UK

Keywords: Exceptions; Refinement calculus; Exception handling; Weakest pre- conditions

Abstract. Although many programming languages contain exception handling mechanisms, their formal treatment - necessary for rigorous development - can be complex. Nevertheless, this paper presents a simple incorporation of exit commands and exception blocks into a rigorous program development method. The refinement calculus, chosen for the exercise, is a method of developing imperative programs. It is based on weakest preconditions, although they are not used explicitly during program construction; they merely justify the general method. In the style of the refinement calculus, program development laws are given that introduce and allow the manipulation of exits. The soundness of the new laws is shown using weakest preconditions (as for the existing refinement calculus laws). The extension of weakest preconditions needed to handle exits is a variation on earlier work of Cristian; the variation is necessary to handle nondeterminism.

1. Introduction

Many programming languages contain an exception mechanism of some kind - a mechanism to determine the action to be taken when an unusual set of cir- cumstances arises, such as an arithmetic overflow or a division by zero. Although these mechanisms may make programs much shorter, they often make them more complex in behaviour, and therefore more difficult to construct correctly. For this reason, much of the recent work on the formal development of imperative programs has not dealt with exception handling 1. This paper seeks to rectify that omission by incorporating some of the ideas of Cristian [Cri84] into the

Correspondence and offprint requests to : Steve King, Programming Research Group, Oxford University Computing Laboratory, Wolfson Building, Parks Rd, Oxford OX1 3QD, UK. 1 See [Spi90] for work on exceptions in functional languages.

Page 2: Exits in the refinement calculus

Exits in the Refinement Calculus 55

refinement calculus [Mor94] style of development. Cristian's paper also contains an interesting discussion on how exception handling can simplify the structure of some programs.

We introduce the refinement calculus, and then treat two familiar program- ming constructs: the exit statement and the exception block. In the refinement calculus style, we will give laws for introducing, manipulating and eliminating these constructs; but the easiest way to understand an exit statement informally is, as before, to think of it as a jump to the end of the smallest enclosing excep- tion block. The laws for exception blocks will show that they may not overlap, although nesting is allowed: this ensures that the informal operational semantics for exit is meaningful - - the 'smallest enclosing exception block' is uniquely de- fined. These constructs will be introduced in Section 3, after the brief introduction to the refinement calculus, together with some laws for their introduction and elimination, and a small example.

When developing real programs, however, we want to do more than simply introduce and eliminate exits: we need to be able to manipulate programs con- taining exits. In Section 4 we introduce an abbreviation for a commonly-occurring program fragment containing an exit. Rules for program development are given in terms of this abbreviation, and the example is re-worked.

Recursion is discussed in Section 5, allowing us to give laws for the use of exits in do...od loops. We also introduce the unguarded loop...end construct, as found in Modula-2.

In Section 6, we show how the standard techniques of weakest precondition semantics can be adapted to give a semantics for this language with exceptions, thus justifying the laws of the preceding sections. We show that the method originally proposed by Cristian [Cri84] does not deal with nondeterminism, which we regard as an essential part of a specification language. A slight adaptation of Cristian's ideas solves this problem, and a semantics for the language is given.

Finally, we show how a simple scheme of exception handling can be defined, using procedures and the exit and exception block constructs that we have introduced. We have made a deliberate decision to separate out the issue of the interrupted flow of control that it is central to exceptions from the handling of exceptions once they have been detected. This decision was guided by a consideration of the many mechanisms for exception handling, some of them very complicated, which can be found in existing implementations.

We were motivated to consider exits by their widespread use in practical programming and by the fortuitous discovery that extending Cristian's work into the more modern framework was very cheap. The effective use of formality in program design depends crucially on having a 'light touch', not losing the issue behind the form: we hope to show that exits can be treated in that style.

The following notational conventions are used:

�9 names consisting of a single letter repeated three times, aaa, bbb etc., represent programs; and

�9 single Greek letters e,/~ etc., represent predicates, that is, sets of states. (We are not considering the question of expressibility, and thus we sometimes blur the distinction between a Boolean function on a state space and the corresponding set of states.)

Page 3: Exits in the refinement calculus

56 S. King and C. Morgan

2. The Refinement Calculus

The refinement calculus arose out of a simple extension of Dijkstra's language of guarded commands [Dij75]. A specification, here written

w ' [~,fl]

comprises a frame w, and two predicates: the precondition c~ and the postcon- dition ft. It is a command in the programming language which, like the others, describes the intended effect of a computation. Unlike conventional programming commands however, it does not necessarily suggest a mechanism for the com- putation: it gives the what, but not the how. In the refinement calculus world, we do not distinguish specifications from programs: every specification is also a program (but not vice versa).

In the specification w �9 [e, fl], the frame w is a (possibly empty) list of variables that the specification (command) may alter. When the precondition e is true initially, the specification is guaranteed to terminate in a state satisfying the postcondition ft. On the other hand, when e is not true initially, no guarantees can be made about the behaviour of the specification: it might terminate in an arbitrary state or it might not terminate at all.

For example:

�9 y �9 [true, y2 = x] is a specification which states that a value should be assigned to y to make the predicate y2 = x true (thus assigning to y a square root of x);

�9 e �9 [s ~ {},e E s] is a command which chooses an element e from a set s, provided s is non-empty; if s is empty initially, then this command might not terminate, or it might assign an arbitrary value to e; and

�9 x " [b 2 > 4 a c , ax 2 q- bx q- c = 0] is a command which solves the quadratic equation for x, provided the discriminant is non-negative; if the discriminant is less than 0, then its behaviour is arbitrary.

The meaning of a specification statement can be given in terms of Dijkstra's weakest precondition semantics [Dij76]"

wp(w : [~, fl], 4 ) ~ ~ / ~ (V w �9 fl ~ 4 ) ,

which means that, for example,

w p ( e ' [ s @ { } , e E s ] , q S ) = s @ { } A ( V e � 9

(--~ is used for definitions, and quantified predicates are enclosed in parentheses with the quantified variables separated from the predicate by a large dot ..)

Apart from the specification statement, the second essential ingredient of the refinement calculus is a relation, called refinement, between programs. We write

aaa ~ bbb ,

for two programs aaa and bbb, to say that aaa is refined by bbb; and that, in turn, means informally that any client who has asked for the program aaa will be happy if given bbb instead. Formally, the definition of the refinement relation between programs is given by weakest preconditions:

aaa U bbb ~- wp(aaa, O) ~ wp(bbb, (2) for all postconditions q~

Page 4: Exits in the refinement calculus

Exits in the Refinement Calculus 57

For example, the first specification mentioned above, y : [true, y2 = x], could be refined by the program y := ,/s On the other hand, it could also be refined by the program., y ..'= -~.x.x" any client who had agreed that their needs, were. met by the original specification would have no grounds for complaint, whichever program they were given.

Program development in the refinement calculus is usually carried out via a series of so-called refinement steps, starting from a specification aaa, say, and ending with an executable program z z z . In between might occur a num- ber of 'hybrid' programs, containing both specifications and executable frag- ments:

aaa E . . . E Ill E m m m E nnn E ooo E_ . . . E z zz .

The overall desired result aaa E_ zzz follows from the transitivity of E. Historically, Back was the first to embed specifications in programs, using

the weakest precondition calculus [Bacg0, Bac88], although his specifications contained only a single predicate. More recently, both Morris [Mor87] and Morgan [Mor88] have extended Back's work by using separate pre- and post- conditions. All three authors have the same refinement relation. The refinement calculus continues the tradition of Hoare [Hoa69] and Dijkstra [Dij76]; for ex- ample, the meanings of the specification statement and the refinement relation were deliberately chosen to make true the following theorem (Theorem 3 of [Mor88]):

Taking w to be all program variables, and aaa to be an executable program,

w : [pre,post] E aaa

has exactly the same meaning as

(Vw �9 pre ~ wp(aaa , p o s t ) ) .

This theorem allows us to check the validity of the laws of the refinement calculus, such as this law, for decomposing a specification into the sequential composition of two specifications:

w "[pre, post] ~ w "[pre, m i d ] ; w "[mid ,pos t] .

There is an extensive collection of laws such as the above, some with side- conditions to be proved, which are used to justify the refinement steps in a program development. A tutorial introduction to these laws may be found in [Mor94], while a collection of more theoretical papers may be found in [MoV93]. In this paper we introduce the necessary laws as we go along.

The constructs of the language that we use here are summarised below, to- gether with their usual meanings in terms of standard weakest preconditions (later, however, we will need to extend the notion of weakest preconditions to cover exits):

Page 5: Exits in the refinement calculus

58 s. King and C. Morgan

Syntax Semantics

assignment x := E sequential aaa ; bbb

composition alternation i f ( l / ~ ~i --" aaai ) fi2

recursion mu aaa �9 N ( a a a ) um

specification w : [c~,fl]

skip skip abort abort nondeterministic aaa I bbb

choice naked guarded c~ ~ aaa

command

wp(x := ~:,05) = 05Ix\E] wp(aaa; bbb, 05) =

wp(aaa, wp(bbb, 05)) w p ( i f ( l i * ~i ~ aaai)fi, O) =

(V i �9 ~i) A (A i ~ ai ~ wp(aaai, 05))3

given by least fixed point: see Section 6.3

wp(w : [~,f l1 ,05)= ~ A ( V w .fl =~ 05)

wp(skip, 05) = 05 wp(abort, 05) = fa l se wp(aaa [] bbb, 05) =

wp(aaa, qS) A wp(bbb, 05) wp(c~ ~ aaa, 05) =

c~ ~ wp(aaa, 05)

3. Exits and Exception Blocks

The language which we will use for the development of programs with exits will have all the constructs of the refinement calculus mentioned above, together with two further constructs:

�9 an exit statement; and �9 braces [ ] denoting the scope of an 'exception block'.

The meaning of these constructs will be explained in three ways:

�9 operationally, using a picture; �9 by algebraic laws; and �9 by a weakest precondition semantics (see Section 6).

The operational way to describe the meaning of these constructs is that an exit statement should be seen as a 'jump' to just after the smallest enclosing exception block (see Fig. 1).

Although apparently simple, this view of the meaning of the constructs is very difficult to reason with--but we can use it as the intuition behind some simple algebraic properties of the constructs. All of the following laws can be proved using the semantics given in Section 6.

Law 1. A program following exit has no effect.

exit; aaa = exit (Equality of programs means semantic equivalence, that is, mutual refinement.)

2 This is a shorthand for

if ~1 -~ aaal I--- 1 ~n ~ aaan fi .

The number of branches must be finite, but may be zero. 3 V and A denote distributed disjunction and conjunction, respectively.

Page 6: Exits in the refinement calculus

Exits in the Refinement Calculus 59

f [ ' . . exit "" 1

Fig. 1. The flow of control when an exit is encountered.

Law 2. An exit at the end of an exception block has no effect.

l a a a ; exitl = [ a a a l

Law 3. Exception blocks have no effect on exit-free programs.

[ [aaa l = a a a provided a a a is exit-free

Section 6 contains a weakest precondit ion characterisation o f the idea o f a p rogram being exit-free, but for now we can think of it as 'syntactically wi thout occurrences o f exits'.

With these three laws alone (and the usual laws of the refinement calculus) we can show the equivalence o f some simple code fragments. For instance,

if c~ then a a a f i ,

which is an alternative nota t ion for

i f ~ ~ a a a I - ~ - - ' skip fi ,

can be shown to be equivalent to the following, which is often used when a a a is long, and c~ tests for some error condit ion:

[ if ~ then exit fi; a a a ]] .

(The equivalence only holds when a a a is exit-free.) Al though the following algebraic derivation m a y seem daunt ing in length for an essentially simple result, we set it out in full simply to illustrate clearly the nature o f such reasoning.

if ~ then a a a f i

= "Law 3, provided a a a is exit-free"

[ i f ~ then a a a fi]]

= " L a w 2"

[ if c~ then a a a fi; exit]l

= "definition o f if..then..fi"

[ if e ~ a a a

I ~ ~ skip

fi;

exit

= "distr ibution of ; exit into if "

[[ if ~ --, a a a ; exit

I -~7 ---' skip; exit

fi

Page 7: Exits in the refinement calculus

60 S. King and C. Morgan

= "skip left identity of ; and Law 1"

[ if ~ ~ skip; a a a ; exit

[] - ~ ~ exit; aaa ; exit

fi

= "distribution out of if "

[ if ~ ~ skip

- ~ ~ exit

fi;

aaa ; exit

= "definition of if..then..fi and Law 2"

[ if ~c~ then exit fi; a a a

We have thus shown the equivalence as programs of

if ~ then aaa fi ,

and

[ i f - ~ then exit fi; aaa ]1 ,

using only the simple equivalences of Laws 1-3. In Section 4 we will be able to show a much shorter derivation.

4. Further Development Laws

The laws given in the previous sections are clearly not powerful enough to enable us to make all the development steps we would like. In particular they are all equations, whereas we would expect some laws which actually involve refinements; and there is no law for introducing an exit into a program, other than right at the end of an exception block. 4 Instead of immediately proposing a multitude of new laws involving exits, we first introduce an abbreviation for a construct that appears frequently in programs with exits: instead of

aaa ~ (bbb ; ex i t ) ,

where • denotes pure nondeterministic choice, we write

aaa > bbb ,

which we pronounce aaa 'else' bbb. The idiom describes a computat ion that, if it exits normally, will have executed a a a ; if it exits exceptionally (taking an exit), it will have executed bbb. The choice between the two is arbitrary and unpredictable.

4 One of the starting points for this research was an investigation into the use of a library of abstract data types within a refinement calculus development method- - i t was envisaged that a library module would specify an operation with exceptions, while a development would start from an exception-free specification. This was how we saw the need for laws which introduce exits.

Page 8: Exits in the refinement calculus

Exits in the Refinement Calculus 61

We can immediately give a few simple laws for the new else construct; although we do not prove their soundness here, such proofs are easy exercises given the semantics of Section 6.

Law 4. An 'else' construct can be implemented by taking the first branch uncon- ditionally.

aaa > bbb E aaa

Note that the two sides are not equal, since the left can execute bbb but the right cannot. Nevertheless the right refines the left, because it is something the left might have done.

Law 5. An 'else' construct can be implemented by taking the second branch unconditionally, followed by an exit.

aaa > bbb E bbb ; exit

Law 6. A nondeterministic choice between two programs which do not contain exits is equivalent to an exception block containing the programs as branches of an 'else' construct.

aaa I bbb = [aaa > bbb ]] provided aaa and bbb are both exit-flee

Law 7. An exit-exception pair can be introduced by offering the trivial choice between equal alternatives (corollary to Law 6).

aaa = [[aaa > aaa~ provided aaa is exit-free

We see that Laws 6 and 7 are the crucial ones which allow us to introduce the else construct (and thus an exit) into a program which previously did not contain one.

The laws above are reasonably straightforward. Since 'taking an exit' alters control flow in a program, however, we might expect the interaction of exit and sequential composition to be less obvious:

Law 8. Distribute sequential composition through 'else'.

aaa > bbb E (ccc > bbb) ; (ddd > eee) provided aaa U_ ccc; ddd

and bbb E_ ccc ; eee

The law is justified by examining the three possible behaviours of the right- hand side: the first is that ccc; ddd terminates normally, and refines the normal termination path aaa of the left-hand side; in the second we find that bbb terminates exceptionally, and equals the exceptional termination path of the left- hand side; finally ccc; eee terminates exceptionally, and refines the exceptional termination path of the left-hand side.

Before re-doing the example, we must look more closely at two specific features of the refinement calculus [Mot88]. The first is 'naked' guarded commands, which were originally described in [Ne189] and later brought into the refinement calculus: these are commands 'c~ --~ aaa" which do not necessarily occur within i f . . . fi or do. . . o& For any predicate c~ (the guard) and program aaa we define

wp(~ --', aaa, ~ ) ~- o~ ~ wp(aaa , ~ ) .

Page 9: Exits in the refinement calculus

62 S. King and C. Morgan

Though such commands are well-behaved, and even to some extent generally accepted, they do not satisfy the 'Law of the Excluded Miracle' [Dij76]; in particular,

wp(false ~ skip, qS) = true ,

for any postcondition q~ whatever. For that reason we give the guarded command fa l se --* skip the name magic, and note that aaa E magic for any aaa. It is also easily checked with wp that magic is left-absorptive: magic; aaa = magic for any program aaa.

The second feature is pure nondeterministic choice (in fact used earlier above). We have

wp(aaa [] bbb, (a) ~ wp(aaa, (o) A wp(bbb, 0 ) .

The two constructs interact nicely: one example is that aaa 0 magic = aaa = magic H aaa holds for all programs aaa. Another example is provided by the first step of the derivation below (easily verified using wp), in which we work through the example from the previous section. In this step, we remove the if..fi around an alternation, which is justified so long as the guards are exhaustive, leaving naked guarded commands. The development shows a method of layout often found in refinement calculus developments: certain lines of the development are labelled with numbers, and these labels are used to continue the derivation at a later stage. The complete program can eventually be found by collecting the code fragments from the branches of the resulting development tree.

if ~ then a a a f i

= "definition of if..then..fi, and removing if..fi" --~ aaa

D ~ - ~ skip = "Law 6, assuming aaa is exit-free"

[ ~ a a a > -~e ~ skip

(1) ___ "Law 8, justification below"

(~ --* skip > ~e -* skip);

aaa > magic

(2) __. "expand >, definition of if..then..fi" if ~ e then exit fi

(3) D "Law 4" a a a

(1)

(2) (3)

That concludes the development; collecting the code gives

[ i f - ~ then exit fi; aaa n

as before. The two side-conditions for the application of Law 8 are satisfied as follows:

we require first that e ~ aaa E (~ ~ skip); aaa (easily checked with wp; al- ternatively viewed as a sort of associativity of ~ and ; ). We also require that

Page 10: Exits in the refinement calculus

Exits in the Refinement Calculus 63

- ~ ~ skip E_ ~ --> skip; magic (not so obvious, but in fact the right-hand side simplifies to magic on its own).

This development is much shorter than the previous version, but it should be noted that we have only proved refinement, not equality as before. Notice also that magic has appeared in our development, though we have not needed to implement it (fortunately); we have used Law 4, finally, to discard it by choosing the left-hand side of the 'else' construct.

5. R e c u r s i o n

The laws presented above can in principle - convert any 'finitary' program that contains exits into an equivalent program that is exit-free. The same is not true for ('infinitary') programs which contain recursion, either explicitly or implicitly.

Explicit recursion is usually found in the form of recursive procedure calls, in which a given procedure A, say, contains a call to the same A within it. (For simplicity, we consider only the case where there are no parameters.) These two notions - - recursion and procedure call - - are not inextricably linked however; we separate them by using the recursion block

m u X � 9 ,

thus freeing us to deal with recursion on its own. Further details can be found in [Mor94].

The meaning of the above is the least fixed point of the program-to-program function ~. (For an example, see the treatment of loops below.) More precisely, we consider ~ to have type P T --+ P T , and to be monotonic. The type P T , in turn, is IPS --+ IPS" predicate transformers taking sets of (final) states to sets of (initial) states. The set S of states is fixed throughout the discussion, but in practice would be large enough to contain the standard and constructed types. Recursive procedures are no longer entities to be distinguished in their own right; but one might say that a procedure was recursive if its body were a recursion block.

Implicit recursion is that introduced by iteration. The do...od construction in the guarded command language is - for us - defined as follows:

do G --~ body od

is equivalent to the following recursion block (in which D is a fresh identifier):

m u D �9 if G then body; D fi

um .

The body of an iteration can be any program at all. For instance, taking a rather extreme case, it might be magic, so we could have an iteration

do true --~ magic od

This program can be simplified as follows:

do true --~ magic od

= "unwinding the recursive definition once"

if true then (magic; do true --~ magic od) fi

Page 11: Exits in the refinement calculus

64 S. King and C. Morgan

: "removing if t r u e "

magic; do t r u e ~ magic od

= "magic absorptive"

magic .

Thus magic even 'jumps out of infinite loops'.

5.1. Refining to Recurs ion

In order for recursion to appear in a program whose specification did not contain it, there must be a refinement step whose right-hand side introduces a recursion block. Temporarily ignoring the matter of termination, a law to justify such a step might be

If aaa E ~(aaa) , then aaa E_ mu D * ~(D) u m ,

given some monotonic program-to-program function ~. We can take termination into account - as we must, to avoid the absurd

'everything is refined by mu D * D urn' - by a small amount of trickery involving logical constants, assumptions, and a variant function. (These issues are explained in more detail in [Mor94].)

A logical constant plays the role of a 'ghost'-variable. It can be used for example to refer in post-conditions to values defined before a statement. In what follows, we will use the logical constant V to refer to the initial value of the variant expression. Logical constants are introduced by con, whose meaning is given as follows"

wp(l[ con x �9 aaa ]], 4)) ~- (3 x �9 wp(aaa, 4))) provided that 4) contains no f ree x

An assumption is written {e}, for some predicate c~, and in a sense conveys the claim that 'c~ is true here'. As a statement it acts as skip when e is true, abort otherwise. This means that it is different from an 'assert statement', as found in Algol-W for instance, which is guaranteed to terminate s when the formula is false. Unlike assert statements, assumptions are therefore useless for program instrumentation, but are intended for use during the development of programs, and are removed before the final code is collected. The meaning of an assumption is given by

w p @ } , 4)) 4) .

It should be noted that we usually omit the semicolon between an assumption and any following statement'

a a a = a a a

The variant function for a recursion is an integer expression that is bounded below, yet is strictly decreased on each recursive call.

Putting that all together, the law for recursion introduction [Mor94] is as follows:

s and to cause immediate program termination!

Page 12: Exits in the refinement calculus

Exits in the Refinement Calculus 65

Law 9. Let e be an integer-valued expression, V a logical constant, a a a a pro- gram, and N a monotonic program-to-program function, and assume that neither a a a nor N contains V. Then if

{ e = V } a a a r- ~ ( { 0 < e < V } a a a )

we can conclude

a a a E_ m u D � 9 u m .

In practice, ~ will always be built from the constructs of the language, and so it is guaranteed to be a monotonic function since the constructs themselves are monotonic.

We show in Section 6.3 below that the above law remains valid in the presence of exits; for now we proceed, on that assumption, with the presentation of recursion and iteration.

5 . 2 . I t e r a t i o n

Before showing how Law 9 can be used to introduce iteration, we mention another extension to the language of the refinement calculus, which simplifies the law for iteration. In the simple form of the refinement calculus, containing no exits, the specification statement contains a precondition and a single postcondition. We now consider a specification statement with a precondition and a pair of postconditions - - one for normal, and one for exceptional behaviour. Thus we write

w : [ ~ , / ~ > ~1

for a specification which, when e is initially true, is guaranteed either

�9 to terminate normally, satisfying/~; or

�9 to terminate exceptionally, satisfying 7.

As before, only variables in the frame w may be changed. The formal semantics of this extended specification statement will be given in

Section 6, when we have introduced the extended version of w p which is necessary to deal with nondeterminism. In fact, our extension of the specification statement arises naturally from the extension of the w p predicate transformer.

The new specification statement can also be viewed simply as an abbreviation:

w : [ ~ , f l > ~] = w : [ ~ , f l ] > w : [~ ,~1 .

The connection with the original specification statement, which has only a single postcondition, is given by taking f a l s e as the exceptional postcondition:

w : [c~, fl] = w : [c~, fi > f a l s e ] .

The exit command itself can be seen as a special case:

e x i t = : [ fa lse > t rue] ,

where the t r u e precondition has been omitted. It is sometimes useful (see Section 5.3) to be able to introduce an exception

block around a specification. The specification itself can then be further developed. Law 7 allows us to do this:

Page 13: Exits in the refinement calculus

66 S. King and C. Morgan

w �9 [~ , f l ]

= "Law 7"

w : [ ~ , # ] > w �9 [~ , f l ] l ]

= "abbreviation above"

[ w : [~, f l > f l ] l l

Returning to the iteration law, we recall that the law used in the refinement calculus for introducing iteration (without exits) is

w "[e, eA-~G] r- doG--->

w " [ G A ~ , e A ( 0 _ < e < e 0 ) ] od.

The conventional conditions for loop correctness appear in the above as follows, given that the invariant is e and the variant 6 is e: the invariant is true initially (indicated by the precondition of the left-hand side); its truth finally, and the negated guard, are sufficient to establish the desired result (shown by the posteondition of the left-hand side); the invariant is maintained by the loop body (it appears both in the pre- and postconditions of the loop body, on the right-hand side); the variant is strictly decreased (postcondition of the body); the variant is bounded below (postcondition of the body).

To incorporate exits into the above we can show first, using the techniques of earlier sections, that

{ e = V } w " [ e , c ~ A T a > f l ] r- if G t h e n

w " [ e A G , e A ( 0 < e < e 0 ) > f l ] ; { 0 < e < V} w "[~,~A-~G >i l l

fi ,

which matches the condition for recursion introduction in Law 9. The proof of this is not complicated; rather than give it formally, however, we sketch an argument as follows. We consider separately the two cases distinguished by whether ~G is true initially. If ~G holds initially, the left-hand side is refined by skip because the required postcondition of the normally-terminating branch holds already (~ in the precondition, ~G assumed). Given ~G initially, the right-hand side equals skip.

If G holds initially, then the right-hand side either

1. terminates normally having executed w " [ ~ A G , ~ A ( 0 < e < e 0 ) ] ; { 0 < e < V } w ' [ ~ , ~ A ~ G ] ; o r

2. terminates exceptionally having executed w : [~ A G , ~ A (0 < e < e0)]; w �9 [e, fi]; or

3. terminates exceptionally having executed w �9 [c~/x G, fl].

6 Zero-subscripted variables in a postcondition are used to refer to the values of those variables in the initial state. They are defined in terms of logical constants, and are a very convenient abbreviation for the sort of specifications we wish to write. A zero-subscripted expression, like e0 here, is an abbreviation for the expression with all variable occurrences zero-subscripted.

Page 14: Exits in the refinement calculus

Exits in the Refinement Calculus 67

In all three cases, the postcondition established by the right-hand side is appro- priate for the mode of termination (as given on the left-hand side): e A ~G normally and fl exceptionally. Thus we can conclude from Law 9 that

w : [ ~ , ~ / ~ ~ G > t~] E muD �9

if G then w : [ c ~ A G , ~ A ( O < _ e < e o ) > f l ]

fi;D urn,

which gives us the iteration law required:

Law 10.

w :[o~,eA~G >fl] D doG--->

w : [c~AG,c~A(0_<e<e0)>f l ] od

Compared to the iteration law for the refinement calculus without exits, the extended law simply contains '> fl' in both postconditions. It operates analogously to the exit-free version if the exit is not taken; however, the loop body is provided with the possibility of an exit that must establish the fl demanded of the exit overall. That exit may assume (additionally) G, since the loop body would not be executed if G were not true.

5.3. Loop/Exit /End

As an application of the above, consider the loop/exit/end construction found in Modula-2 (or equivalently the while/break construction of C). We proceed as follows to construct a rule for introducing loop into a program; note that the (extreme) strengthening of the postcondition for the non-exceptional case to false effectively forces exceptional termination, which is the way loop/exit/end behaves.

w �9 [~,/~] : "application of Law 7 above"

[w :%/~ >/~]~ _ "strengthen normal postcondition"

[[w " [c~,false > fl]]l E_ "Law 10"

do true

w " [ ~ A t r u e , ~ A ( O < _ e < e o ) > f l ]

od

Removing the superfluous ~ true' gives the following law:

Page 15: Exits in the refinement calculus

68 S. King and C. Morgan

Law 1l.

w �9 [~, f l ] r- loop

w " [ ~ , ~ A ( O - < e < eo) > fl] end

Thus the task of the non-exceptional part of the loop body is to maintain ~ (the invariant) while strictly decreasing the variant e on each iteration (but not below 0). Since that cannot continue indefinitely, eventually the exit must be taken, after establishing fl as the left-hand side requires.

Note that, like Law 7, this is a law which introduces an exit and a corre- sponding exception block - the extent of the exception block is taken to be the loop...end block, so an exit in the body of the loop will cause a jump to the bottom of the loop, just after the end statement.

E x a m p l e : To show this law in action, we develop a program which is intended to find the index of a particular value guaranteed to occur in an array. We make the following variable declarations:

a s ' a r ray [O..N - 1] of A a : A

i �9 O . . N

The development is as follows:

i " [a c a s [O..N - 1], a = a s [i]]

_E i : = 0 ;

i �9 [a E a s [ i . . N - - 1],a = a s [ i ] ] (1)

(1) E "Law 11, invariant a E a s [ i . . N - - 1]; variant N - - i "

loop

i �9 [a E a s [ i . . N - 1],a c a s [ i . . N - 1] A i0 < i _< N > a = a s [ i ] ] (2)

end

(2) ___ "splitting extended specification"

i " [a c a s [ i . . N - 1],a c a s [ i . . N - 1] A i0 < i < N ] (3)

I] i : [a E a s [ i . . N - - 1],a = a s [ i ] ] ; exit (4)

(3) ~ a =/= as [i] ~ i : = i + l

(4) ___ a = a s [ i ] ~ exit

Notice that the exceptional and the non-exceptional branches are refined to naked guarded commands which, combined by [], lead to the expected alternation in the loop body. The justifications for these final refinements to naked guarded commands are omitted, but they can easily be checked with w p .

We can collect the code to give:

i : = 0 ; loop

a ~ = a s [ i ] - * i : = i + 1 [] a = a s [i] ~ exit

end ,

Page 16: Exits in the refinement calculus

Exits in the Refinement Calculus 69

which we may rewrite, using the definition of if..then..fi and rules from Section 3, as

i : = 0 ; loop

if a = as[i] then exit fi ; i : : i + 1

end .

6. Semantics

In order to provide a sound basis for the laws for the refinement calculus with exits, given in previous sections, we need to give a semantics to the constructs of our language. We achieve that with a small generalisation of the predicate transformer technique. First, we show why the method proposed by Cristian [Cri84], for a deterministic programming language with exits, cannot handle nondeterminism. Then we explain our generalised predicate transformer semantics, and give a formal meaning to each construct.

6.1. Cr i s t ian ' s E x c e p t i o n S e m a n t i c s

In [Cri84], Cristian showed how a semantics could be given to a deterministic programming language with exceptions. His technique involved viewing programs as multi-exit structures, and thereby giving their meaning with sets of predicate transformers. Writing wpc for Cristian's wp, we have in his notation,

�9 wpc(P,;,c~) denotes the weakest precondition under which program P is guaranteed to terminate normally, satisfying the predicate c~. This is simply the usual Dijkstra predicate transformer wp(P, cQ; and

�9 wp~(P,e, cQ similarly denotes the weakest precondition under which P is guaranteed to terminate at exit point e, satisfying cc

According to Cristian, the meaning of a program P was given by the predicate transformers Wpc(P, ; , e) and wpc(P, e, c~) for all possible exit points e.

If we try to use Cristian's technique on our language with exception blocks and an exit command, we find that the meaning of a program P is given by the two predicate transformers

wpc(P, ; , ~) and wpc(P, exit, ~).

Let us now consider the program

Q ~ skip ~ exit

in which [] denotes nondeterministic choice. 7 Since we cannot guarantee that Q will terminate successfully, wp~(Q, ;,cQ = false. Similarly, since we cannot guarantee that the exit will be taken, wp~(Q,exit, c~) =false.

Now consider instead the program [Q~. We know that, for successful termi- nation,

wpc(iQll, ; , ~ ) =

7 The language for which Cristian gave a semantics in [Cri84] did not include this operator.

Page 17: Exits in the refinement calculus

70 S. King and C. Morgan

Syntax Semantics

x : = E aaa; bbb i f ( l / �9 o~i ~ aaai)fi

m u aaa �9 ~ ( a a a ) u m

w : [~ , f l > 7]

aaa [] bbb c~ ~ aaa

wp(x := E , v , e ) = v [ x \ E ] wp(aaa ; bbb, v, e) = wp(aaa, wp(bbb, v, e), e) wp(if([li �9 ~i --* aaai)fi, v ,e) =

(V i �9 o~i) A (A i �9 o~i ~ wp(aaai, v,e)) given by least fixed point: see Section 6.3 wp(w : [~, fl > 7], v, e) =

~A(VW � 9 A(Vw � 9 wp(aaa Ibbb, v, e) = wp(aaa, v, e) A wp(bbb, v, e) wp(c~ ~ aaa, v, e) = ~ ~ wp(aaa, v, e)

Fig. 2. Weakest precondition semantics.

since the exit in Q will be caught by the exception block. This exposes the problem with Cristian's approach: we cannot give the meaning of [Q]] in terms (only) of the meaning of Q, since c~ does not appear in the latter.

6.2. Semantics of Exceptions With Nondeterminism

Rather than using sets of predicate transformers to give meaning to nondeter- ministic programs with exceptions, we will instead use a predicate transformer that transforms a pair of predicates to a single predicate. We use the nota- tion

wp(aaa, v, e)

where aaa is a program, and v and e are predicates, to denote

the weakest precondition from which aaa is guaranteed either:

�9 to terminate normally satisfying v (for normal); or �9 to terminate exceptionally satisfying e (for exceptional).

Now we can give a compositional semantics to our language, using this notation. For any construct which was in the language before we added exits, say ppp, the corresponding new weakest precondition definition is given by

wp(ppp, v, e) ~ wp(ppp, v)

(where the wp on the left is our new version, and the wp on the right is the standard Dijkstra wp). That is because the 'original' constructs terminate normally by definition - - they contain no exits. Since they cannot terminate exceptionally, the right-hand side is independent of e. For instance, the commands skip and abort are given meaning thus:

wp(skip, v, e) = v wp (abort, v, e) = false

The other constructs of the language (apart from recursion) have defining equa- tions very similar to the usual (Dijkstra) wp equations: these are given in Fig. 2.

Page 18: Exits in the refinement calculus

Exits in the Refinement Calculus 71

Notice that, in Fig. 2, we have given a wp definition to the extended specifi- cation statement w : [cr fl > 7]. As we remarked earlier, the simple specification statement is a special case:

w : [o:, fi] = w : [a, fl > false] .

We can therefore calculate its weakest precondition:

wp(w : [~, ill, v, e) = w p ( w : [o:, fl > fa lse] , v, e)

= ~ A (Vw �9 fi ~ v) A (Vw . f a l s e ~ e)

= T A ( V w �9 fl => v) ,

which agrees with the definition in [Mor88]. More interesting are the equations for the exit command and the exception

block:

w p ( e x i t , v, e) = e wp(l[aaa]l, v, e) = wp(aaa , v, v) .

The equation for the exception block captures the fact that any exit inside aaa will be caught by the exception block.

Using these weakest precondition definitions, we can verify the laws which were given earlier, and justified at that stage only in terms of the informal operational semantics. For instance, Law 2 states

[ a a a ; e x i t ~ = [ aaa 1] �9

Taking weakest preconditions, we find

wp([aaa ; exitl], v, e)

= wp(aaa ; exit, v, v)

= wp(aaa , wp(exit, v, v), v)

= wp(aaa , v, v)

= wp([[aaail, v, e) .

Many of the laws given earlier have side-conditions stating that certain compo- nents must be exit-free, where an obvious test for exit-freeness is syntactic. But we can be more precise if we use a weakest precondition characterisation of the concept:

aaa is exit-free iff wp(aaa , v, e) = wp(aaa , v, e') for any e, e'

Now we can verify Law 6, for instance. We need to show that

aaa fl bbb = [ aaa > bbb 11

given that aaa and bbb are both exit-free. Taking the weakest precondition on the right, we obtain

w p ( [ aaa > bbb ]],v,e)

= wp(aaa > bbb, v , v )

= wp(aaa [] (bbb ; exit), v, v)

= wp(aaa , v, v) A wp(bbb ; exit, v, v)

= wp(aaa , v, v) A wp(bbb , wp(exit, v, v), v)

Page 19: Exits in the refinement calculus

72 s. King and C. Morgan

= wp(aaa, v, v) A wp(bbb, v, v)

= "since aaa and bbb are both exit-free"

wp(aaa, v, e) A wp(bbb, v, e)

= wp(aaa [] bbb, v, e) .

Many of the other laws which were given earlier involve refinements, rather than just equalities. In order to verify these laws, we need a weakest precondition definition of refinement. Following [Mor94] and other writers on the refinement calculus, our definition is as follows:

For any programs aaa and bbb, we say that aaa is refined by bbb, written aaa E bbb, exactly when for all postconditions v and e,

wp(aaa, v, e) => wp(bbb, v, e) .

6.3. Semantics of Recursion

As usual, the semantics of recursion is given by a least fixed point construction. In general, given a program-to-program function ~ we write # N for its least fixed point, and take that to be the meaning of the syntax

m u X ~ um

given in Section 5. Rather than proving the recursion law directly (Law 9 in Section 5 above),

we will instead give a lemma from which it is easy to derive the law. We will give an outline proof of it.

Reeursion Lemma. Let an ordinal-indexed family of programs aaa~, be such that for any ordinal

aaa~ E_ ~(Ufl I fl < c~ ~ aaafl) ,

for some monotonic program-to-program function ~, where U denotes least upper bound in the refinement ordering given above. Then

aaa~ r- #

for all e.

Proo f outline: By transfinite induction on ~, with all three cases together:

aaa~

E "assumption"

~ ( U fl I fl < ~ ~ aaafl) _E , ,U,~ monotonic; inductive hypothesis"

F- "even when ~ : 0" ~ ( ~ )

= "# ~' fixed point" # ~ . []

Page 20: Exits in the refinement calculus

Exits in the Refinement Calculus 73

Now we must show how to obtain the recursion law from this lemma. Let us define a family of programs aaa~ by

aaa~ = {e = ~}prog .

We may assume, from the statement of the recursion law, that, for any e,

{e = e}prog E ~({0 _< e < e}prog).

But

{0 _< e < c~}prog

= {(V ]~ I/~ < ~ ,, e = f i)}prog = "by wp"

( U f l l f l < e " {e = B } ) ; p r o g = "left-distribution of ; into LJ"

(Lif t 1f3 < c~ �9 {e = f l}prog)

= ( l i f t I fl < o : . aaa~) .

So we have

aaa~ G ~(] l fl ] fl < o : . aa@ )

as required for the recursion lemma above, and we may conclude that, for any cr

aaa~ G # ~ .

In other words,

{e = a}prog G # N for all ~ .

Since this holds for any e, we conclude that prog _ # N simply by letting c~ range over all possible values of e permitted by our original choice of state space S.s

7. Exceptions and Handlers

Due to the variety of possible mechanisms for generating 'exceptions', and 'han- dling' them, we have abstracted to the point of considering only the interrupted 'flow of control': the exit and its enclosing block. To give the flavour of exception handling, we outline one possible scheme below (perhaps the most basic).

This mechanism involves the use of procedures to associate fragments of code with the raising of particular exceptions. Thus, in order to declare a handler, we write

handler H ~ hhh

as an abbreviation for

procedure H ~- hhh ; exit

8 In fact, we also need the equivalence

( V i ' x i GY) =- (Ui ' x i )E - -Y .

Page 21: Exits in the refinement calculus

74 S. King and C. Morgan

The raising of an exception uses the abbreviation

raise H

which is simply a call of procedure H. So, when exception H is raised, the associated code hhh is executed, and control passes to the end of the smallest enclosing exception block. This scheme has the advantage that it can deal with 'multiple' exits without any further complication, simply by using several handlers.

One application of this mechanism would be a structure like aaa <1 bbb of [LdS93], which either executes aaa successfully, or leaves aaa via an exit and continues with bbb. This structure is easily modelled as

[l- handler H -~ bbb �9 aaa t

ll

where, rather than 'exit', the body aaa' uses 'raise H', but is othercdse identical to aaa.

Of course, the benefits of a '<l-calculus', so nicely explored in [LdS93], are not so accessible when the relatively heavy mechanism of procedures is used. But the procedures are perhaps more adaptable to the sometimes perverse demands of existing practice, and thus might be necessary anyway.

8. Conclusion

The work presented in this paper has filled a small gap in the theory of rigorous (imperative) program development: we have shown how the language of the refinement calculus can be extended to include an exit command and exception blocks, and we have given laws to show how programs containing these constructs can be manipulated. Using those laws and the usual laws of the refinement calculus, we are able to develop programs which contain exits.

The meaning of the new constructs is given using a version of weakest precondition semantics, in fact a slight variation of a technique proposed by Cristian, adjusted so that it can deal with nondeterminism. The validity of the laws of the calculus is shown with respect to that semantics.

At the start of this work, we were aware of Cristian's approach; but since then we have encountered two others, one dating from 1983 and the other contemporaneous with our own work.

In an unpublished report [BaK83], Back and Karttunen discussed how Dijk- stra's weakest precondition predicate transformer [Dij75] could be generalised: instead of giving the semantics of a language by a function

w : Star --~ (Cond ~ Cond)

(where Stat is the set of all statements of the language, and Cond denotes the set of all possible pre- and postconditions), they introduced the idea of a multiple-argument predicate transformer:

w :S tar --* (Cond • -+ Cond) .

They used this notion to give semantics for statements with multiple exit points:

w ( S ) ( Q t . . . . . Qm) is the weakest precondition which guarantees that execution of S will terminate at one of the exit points of S, such that, if exit hi is reached, then condition Qi will hold, for i = 1,. . . , m.

Page 22: Exits in the refinement calculus

Exits in the Refinement Calculus 75

After showing how Dijkstra's so-called 'healthiness conditions' may be gener- alised to multiple-argument predicate transformers, they defined a simple and elegant language for multi-exit statements, and used this to give semantics to a language with goto statements, by transforming it to the language with multi-exit statements.

Their work is slightly more general than we report, in that it allows for statements with any number of exit points rather than just the two, normal and exceptional, that we deal with. We prefer however to deal with multiple exits using a procedure mechanism, thus keeping the extra semantic structure to a minimum. They also do not treat recursion. More significant is the fact that they deal only with a programming language, rather than a refinement calculus, and so there is no notion of refinement of programs containing multi- exit statements.

In another report, published slightly later, [MAN84], Manasse and Nelson give a similar definition of a weakest precondition of two arguments, though their work is not primarily concerned with exception handling, but the transformation of high-level control structures into low-level instruction sequences.

The second related piece of Work [LdS93] has been carried out at approxi- mately the same time as our own. Following the work of Lukkien [Luk92], which gave an operational (trace-based) semantics for the guarded command language, Leino and van de Snepscheut give a similar semantics for a language with excep- tions, by adding a state variable to indicate whether a statement has terminated normally or exceptionally, a construction isomorphic to our taking a pair of postconditions. From this trace semantics, they derive a weakest precondition semantics, with a definition, very similar to ours, of wp(S) as a function of two arguments, for normal and exceptional termination.

They explore the algebraic properties of wp in terms of arbitrary functions of two arguments, but are not concerned, as we are, with the refinement of programs containing exits. Indeed, the use of explicit specifications is a significant advantage when considering rules for rigorous program development that must refer to 'assertions established within a program fragment'. With specifications available, those assertions are explicit parts of the program; without them, rules for reasoning about a complete block must refer to the reasoning employed with respect to its constituents, rather than simply to the constituents themselves. (A similar effect occurs in the treatment of proper - - rather than 'tail' - - recursion [Mor94].)

exit, pursued by a bear The Winter's Tale

William Shakespeare

Acknowledgments

We are grateful to IBM Hursley Park (and in particular Peter Collins) for encouraging us to look at the original problem of the library of abstract data types; and for financial support of the first author.

We are also grateful to the following for helpful comments and suggestions: Richard Bird, Tony Hoare, Peter Lupton, Jane Sinclair, Jan van de Snepscheut, Jim Woodcock, and to the anonymous referees who suggested where clarifications would be desirable.

Page 23: Exits in the refinement calculus

76 S. King and C. Morgan

References

[Bac80]

[Bac88]

[BaK83]

[Cri84]

[Dij75]

[Dij76] [Hoa69]

[Luk92]

[LdS93]

[MAN841

[Mor87]

[Mor88]

[Mor94]

[MoV93]

[Ne189]

[Spi90]

Back, R.-J. R.: Correctness preserving program refinements: Proof theory and applica- tions. Tract 131, Mathematisch Centrum, Amsterdam, 1980. Back, R.-J. R.: A calculus of refinements for program derivations. Acta Informatica, 25:593-624, 1988. Back, R.-J. R. and Karttunen, M.: A predicate transformer semantics for statements with multiple exits. University of Helsinki, unpublished MS, 1983. Cristian, F.: Correct and robust programs. IEEE Trans. Soft. Eng., SE-10(2):163-174, March 1984. Dijkstra, E. W.: Guarded commands, nondeterminacy and formal derivation of pro- grams. Comm. ACM, 18(8):453-457, August 1975. Dijkstra, E. W.: A Discipline of Programming. Prentice-Hall, Englewood Cliffs, 1976. Hoare, C. A. R.: An axiomatic basis for computer programming. Comm. ACM, 12(10):576-580, 583, October 1969. Lukkieu, J. J.: An operational semantics for the guarded command language. In R. Bird, C.C. Morgan, and J.C.R Woodcock, editors, Mathematics of Program Construction, number 669 in Lecture Notes in Computer Science. Springer-Verlag, 1992. Leino, K. R. M. and van de Snepscheut, J. L. A.: Semantics of exceptions. Technical Report Caltech-CS-TR-93-94, Computer Science Department, California Institute of Technology, 1993. Manasse, M. S. and Nelson, C. G.: Correct compilation of control structures. Technical report, AT&T Bell Laboratories, September 1984. Morris, J. M.: A theoretical basis for stepwise refinement and the programming calculus. Science of Computer Programming, 9(3):287-306, December 1987. Morgan, C. C.: The specification statement. Trans. Prog. Lang. Sys., 10(3), July 1988. Reprinted in [MoV93]. Morgan, C. C. : Programming from Specifications. Prentice-Hall International series in computer science / C.A.R. Hoare, series editor. Prentice-Hall International, Englewood Cliffs, N.J. ; London, 2nd edition, 1994. Morgan, C. C. and Vickers, T. N.: editors. On the Refinement Calculus. FACIT Series in Computer Science. Springer, 1993. Nelson, C. G.: A generalization of Dijkstra's calculus. Trans. Prog. Lang. Sys., 11(4) :517- 561, October 1989. Spivey, J. M.: A functional theory of exceptions. Science of Computer Programming, 14(1):25-42, 1990.

Received March 1993 Accepted in revised form February 1994 by J. van de Snepscheut