February 1980
Stanford Verification Group
Report No. 16
Computer Science Department
Report No. STAN-CS-80-789
ADA EXCEPTIONS: Specification and Proof Techniques
bY
D. C. Luckham and W. Polak
Research sponsored by
Advanced Research Projects Agency
and
Rome Air Development Center
COMPUTER SCIENCE DEPARTMENT
Stanford University
February 1980
Stanford Verification Group
Report No. 16
Computer Science Department
Report No. STAN-CS-80-789
ADA EXCEPTIONS: Specification and Proof Techniques
bY
0. C. Luckham and W. Polak
ABSTRACT
a
A method of documenting exception propagation and handling in Ada programs is
proposed. Exception propagation declarations are introduced as a new component of
Ada specifications. Thi permits documentation of those exceptions that can be
propagated by a subprogram. Exception handlers are documented by entry assertions.
Axioms and proof rules for Ada exceptions are given. These rules are simple
extensions of previous rules for Pascal and define an axiomatic semantics of Ada
exceptions. As a result, Ada programs specified according to the method can be
analysed by formal proof techniques for consistency with their specifications, even if
they employ exception propagation and handling to achieve required results (i.e. non
error situations). Example verifications are given.
This research was supported by the Advanced Research Projects Agency of the Department of
Defense under Contract MDA 903-80-C-0159 and Rome Air Development Center under Contract
F 30602-80-C-0022. The views and conclusions contained in this document are those of the
authors and should not be interpreted as necessarily representing the oflcial policies, either
expressed or implied, of Stanford University, or any agency of the U. S. Government.
ADA EXCEPTIONS : SPECIFXCATION AND PROOF TECHNIQUES
D, C. Luckham and W. Polak
Abrttect:
A method of documenting ezception propagation and handling in Ada
programs is proposed. Ezception propagation declarations are introduced aa a new
component o j Ada specifications. This permite documentation of those ezceptiona
that can be propagated bg a subprogram. Ezception handlers are documented bg entry
aesertions. Azioms and proof rules for Ada ezceptions are given. These rulea are
simple eztenaions of previous rules for Parcal and define an aziomatic semantica of
Ada ezceptions. As a result, Ada programs specified according to the method can be
analgsed by formal proof technique8 for consirtencg with their rpecijications, euen
if they employ ezception propagation and handling to achieue required result8 (i.e.
non error rituations). Ezample verification8 are given.
This research wad supported by the Aduanced Research Projects Agenw of the Department of Defense under Contract M DA 905 -- 80 - - C -- 0150 and Rome Air
Development Center under contract F 50602 -- 80 -- C -- 0022
1
1. Introduction
a
Exceptions in the Ada language [I] are intended to provide a facility for
dealing with errors and exceptional situations arising during program execution.
An exception is an event that causes suspension of normal program execution called raising an exception. Actions in response to the occurrence of an exception
are coded in special units called handlers. When an exception is raised, execution of a handler replaces execution of the current unit. The choice of handlers
is dynamic - i.e., determinable only at runtime. Ada exceptions are clearly a
powerful programming facility and a study of their semantics and proof is needed.
The obvious semantics of raising an exception is that of a jump to the corresponding handler. In fact, the conventional go to rule [lo, ll] is sufficient to
describe exceptions with static association of handlers. Problems arise in the case
where an exception is not handled locally in a subprogram and the association of
the exception with the handler is dynamic. The answer lies in requiring documentation for exceptions that are propagated by procedures or functions; it is then
possible to modify the procedure call rule to describe the dynamic association of
handlers with exceptions at call time.
We propose a method of specifying exceptions in Ada programs. Axioms and
proof rules based on these specifications are given. These define an axiomatic
semantics of Ada exceptions, and make possible the verification (proof of consistency with specifications) of Ada programs containing exceptions. Our specifications and proof rules apply to programs with exceptions irrespective of whether
exceptions are used only for error situations or as a method of programming normal program behavior (e.g., the program is intended to continue running when
exceptions are raised and handled). The proof rules are simple extensions of rules
of conventional axiomatics e.g. those of Pascal [7]. Our results show that if the
programmer follows a simple specification discipline, then the use of exceptions as
a normal programming tool does not greatly complicate the analysis of programs
for consistency with specifications.
We restrict presentation of our method to simple cases. In particular, the
semantics assumes call by reference implementation for out and tn out parameters.
Similar proof rules can be given for other implementations of parameter passing.
Extensions to more complicated programs, e.g. Ada packages or Ada programs
in which exceptions are propagated beyond their scope, are outlined in section 5
and the appendix. We will not deal with the task failure exception propagated
between tasks or with suppression of exceptions.
Throughout this paper we will use the notation and terminology of the Ada
report [l), except in logical assertions, where we use o[i] to denote the i-th element of array u to improve readability. The word subprogram will be used for a
2
function or procedure.
A statement of the form arrert <assertion> is not the assert statement
of Ada; <assertion> is a logical assertion and is not intended to be executed.
Logical assertions are more general specification statements than Ada conditions,
but they obey the normal scope rules: all free variables are visible program variables.
2. Specifying Exceptionti
The exception facility of Ada includes
- declarations of user defined exceptions
- a raise statement that causes an exception, and
- the definition of exception handlers, i.e. code that is to be executed whenever
a particular exception is raised.
We now de%ne the formal documentation that has to be provided for exceptions.
(1) No documentation is required for exception declarations and raise statements.
(3) For each exception handler the programmer will be required to provide an
assertion that is to be true whenever one of the exceptions in the when clause is
raised. Following Ada we will use the syntax:
exception-handler ::=
when exception (1 exception }
ar,ert assertion => sequence,ol,statements
(3) An exception that is raised in a subprogram and is not handled locally is
said to be propagated by this subprogram ([I] 11.34 We introduce an exception propagation declaration and require that each exception propagated by a
Hubprogram be specified in the subprogram’s propagation declaration. Analogous
to a handler, a propagation declaration has to provide an assertion that is to be
true whenever one of the exceptions in the propagate clause is propagated.
Propagation declarations are placed at the end of the Ada subprogram specification ([1]6.2). We consider propagation declarations as part of a (to be defined)
specification language for Ada. As such, they are of the same nature as global,
entry, and exit specifications for procedures and functions of Pascal Plus [II].
We use the syntax:
3
propagation,declaration :: =
propagate exception (1 exception }
assert assertion
subject to the normal scope rules. Note, that a call to a procedure that propagates
an exception is a statement that Van raise an exception”. We restrict such calls
to the scope of the exception; the use of propagate declarations now makes this
restriction compiler checkable. In section 5 we show how these specification requirements may be relaxed by use of the others clause. The appendix contains an
example of how our specifications will require modifications of an Ada program
that propagates an exception out of the scope of the declaration of that exception.
In a scope S where an exception E can be raised either by a raise statement
or a subprogram call the assertion arrociated with E in this scope is defined as
follows:
- If S is a block with a handler for E then the assertion of this handler is the
associated assertion,
- If S is a block without a handler for E then the assertion associated with E
in S is the assertion associated with E in the scope immediately enclosing S.
- - If S is a subprogram, then the assertion of the propagation declaration for E
is associated with E.
Remark: An exception has an associated assertion in every scope where it can be
raised.
For example, consider the following program.
4
procedure p(. . . )
propagate E errcrt A ir
begin
begin
...
raire E;
- B is associated assertion
exception
when E arrert B => . . . .
end;
begin
...
- A is associated assertion
raise E;
...
end
end p
3. Axiomatic Semanticr of Exceptionr
3.1. Raising Exceptions
Wherever the statement raise Ei occurs, the above specification principles
guarantee that there is an assertion Ai associated with Ei at that point. The
semantics of raire Ei is described by the raise axiom:
{ Ai} raise Ei (fd8e).
3.2. Blocks
The rule of exceptions given below requires that any exception handler achieves
the exit assertion of the block in which it is contained. The handler may assume
.
the truth of the assertion in the when clause.
Let C be the block
5
exception
when El arrert Al => S1
when . . .
...
when E, arrert A,, => S,
end
then we have the following rule ol exceptions:
3.3. Procedure calls
-
Rules given in 3.1 and 3.2 are sufficient to describe the effect of exceptions
handled in the subprogram in which they are raised. We now propose a modified
procedure call rule that describes the effect of an exception propagated by a procedure. We will restrict our attention to procedures only; a similar treatment for
functions is immediate.
Let p be a procedure that propagates the exceptions El.. . E, and let Al.. . A,
be the corresponding assertions in the propagate declaration of p. Let Bj be the
assertion associated with Ej in the calling environment according to the principles
in 2. The procedure call rule will require that Aj + Bj for all exceptions Ej
propagated by p with proper substitutions for in parameters and quantification
for out and in-out parameters.
The procedure call rule presented below is taken from [sI], adapted for Ada
and exception handling. Let p be a procedure with ji, jO, and jia being the formal
in, out, and in out parameters of p respectively. We assume that the correctness
of the body of p has been established with respect to the input condition I( ji, jio)
and output condition O( ji, jO, jio). Let ui, uO, and uio be the corresponding actual
1 parameters of a call to p, then this call is described by the rule:
P + I(ui, uio)
A
Vuop uio*(O(ui, ~0, uio) + Q )
P + I(Ui, Qio) A VU09 Qo*(Al(%, Qo9 (ho) + Bl)
...
P --+ I(Ui, Qio) A V&J, aioe(An(oi, 00, Uio) + &)
6
where the clause P + I(ai, oio) A Vuo, uio.(Aj(ui, uo, uio) + Bj) has to be proven
for each propagated exception Ej with propagate assertion Ai and Bj associated
with Ej in the calling environment. Note that the treatment of exceptions is
similar to that of the normal output condition; raising exceptions may be viewed
as just another exit of p.
The above rule assumes p to be a restricted procedure without any global
variables. It may be extended to provide for global variables. Furthermore, we
assume an implementation of procedure calls using call by reference for out and
in out parameters. Our rules can be adapted for other procedure call mechanisms.
Also, other procedure call rules (e.g. [2,5,7]) can be extended to include the
conditions Ai + Bi.
4. Example1
4.1.
Consider the. procedure seurch with in parameters CL, n, and z and out
parameter i with the entry assertion true and the exit assertions 1 s i 5 n A
Z= u[i]. In case that the value z is not in u, seurch will raise the exception
notfound. For this exception eeurch contains a propagation with the assertion
V&(1 5 j 5 n + z # u[j]). Altogether we have the procedure declaration
type taurruy ir array (1. .n) of integer;
procedure seurch(u: in nurruy; w in integer; z: in integer; i: out integer)
entry true
exit 15 is nAz=u[i]
propagate not found arrert Vj.(l 5 j 5 n + 5 # ub]) ir
begin
...
raire notf ound;
...
end
A proof of the body of search may use the axiom
{Vj.(l 5 j 5 YJ --) z # u[j] )} raire notfound {j&e}
Procedure search could be used in the following context (m, y, and b are
declared in some global scope):
7
areert true;
begin
search(b, m, y, k);
exception
w h e n notfound arrert Vj.(l < j < m + p # bcil, =>
k.a- 0
end;
arrcrt {y = b[k] v (V&(1 5 j 5 m + g # b[j]) A k = 0))
Consistency of this block with its specifications can be shown by
Vk (1 5 k ,< mAy =b[k])+
v= b[k] v
(Vj.(l 5 j 5 m
‘YZWhk=0))
Vk(Vj.(l 5 j 5 m ~~# ~[j])- ,Vi.(l~ ilm+k# ~Cjl))
Vj.(l < j <
- m --+ y # b[j]) +
v= b[O] V(Vj(l,< j< m+y # b[j])AO =O )
(1)
(2)
(3)
Formula (1) proves that the exit assertion of the call to eeurch implies the
exit assertion of the block (note, that P and I of the procedure call rule are both
true). This proves consistency if the call to search terminates normally.
Formula (2) proves that the assertion associated with notfound inside search
implies the assertion associated with notjound in the calling environment (see
call rule). This proves that notjound is propagated by the call to search only in
situationa that the handler Uexpectsn.
Finally, (3) proves that the handler does indeed establish the exit condition
of the block (i.e. the handler “handles” correctly).
4.2.
The methods presented in this paper are also useful in proving that an exception will never be raised. If we have an exception associated with the assertion
false, then the corresponding handler will never be executed since its precondition
is never satisfied. Consider the example:
8
assert 3j.l 5 j < mA y = &‘I;
begin
seurch(b, m, v, k);
exception
when not found arrert false => < empty >
end;
arrert {y = b[k] }
The corresponding verification conditions are (numbering aa in previous example):
3j.(l 2 j< mAg=b[j])- ,
(1)
Vk.((l ,< k 5 mAy = b[k]) -by= 01)
3j.(l <
- j < mA y = b[j]) + Vk.(Vj.(l 5 j 3 m --) y # b[j]) + false) (2)
fulee -+ y = b[k]
(3)
5. Extennionr
5.1. Use of the “others” construct
Our method also applies if an exception handler contains the otherr clause.
If a block contains such a handler,
exception
when otherr arrert A => a am
-
then in this block A is the assertion associated with all exceptions that are raised
in the block and are not contained in another when clause in the handler. All
proof rules and the raise axiom apply unchanged.
The situation is different in propagate declarations. If we allow an others
1 clause in propagate declaration8 then any exception that is not explicitly mentioned in this propagate assertion can potentially be propagated via the otherr
clause. Our method can be extended to handle this case if we accept a more
complicated procedure call rule.
The general principle of the call rule remains the same: for each exception
that can potentially be propagated by the called procedure we have to prove that
the assertion specified for this exception in the propagate declaration implies the
assertion associated with the exception in the calling environment.
9
First, note that a scope containing a call to a procedure with an otherr clause
in the propagation delcaration must define an associated assertion for every exception that may be raised in it; thus in general its handler or propagate declaration also has to have an otherr clause. Given the above situation we can classify
all exceptions as follows.
so - set of exceptions explicitly named in handlers of propagate declarations
for calling environment.
.
Si - set of exceptions explicitly named in propagate declaration for called procedure.
Bj - associated assertion for each Ej E So.
Aj - assertion specified in propagate declaration for each Ej E Si.
&c-- assertion for other8 clause in handler (or propagate declaration) of calling
environment.
&---- assertion for others clause in propagate declaration of called procedure.
To prove that a procedure call is correct we have to verify that:
a.) Ai --+ Bj for all Ej E Sin So,
b.) Aj --+ B, for all Ej E S; - So,
t.) A, + Bj for all Ej E SO - Si, and
d . ) A , + &,.
The procedure call rule has to be augmented by these caaea, where proper
substitutions have to be preformed as described in section 3.
A better proposal to reduce the number of necessary propagate declaration8
is to introduce a single global exception, error. If a user does not want to include each propagated exception with a corresponding assertion in a propagate
declaration he may include the handler
exception
when other8 arrert A => raise error;
in any subprogram and provide a propagate declaration for error. This conver- sion has been discussed in 12.5. of the Ada rational but was rejected on efficiency
considerations.
5.2. Application to Packager
The method described in this paper is aleo applicable to exceptions propagated
from within modules (or Ada packages). It applies directly to calls of module
procedure8 but requires some changes when applied to module bodies. Let us
10
briefly outline the key idea without going into the detail8 of a particular method
of specification and verification of modules,
Let us assume that to the using program a module represents an abstract
object together with certain operations (module procedures). Some of these may
propagate exceptions, overAow in a stack for instance. Part of the visible specification of module procedures are exception propagation declaration8 using abstract
specifications. Then the procedure di rule of section 3.3 applies to module procedure calls as well.
For example, the package in [l] 12.3, could be specified aa
generic (size: integer; type elem)
package stuck ir
over flow, under flow: exception;
procedure push(E: in elem)
propagate over flow arrert f ull(etuck);
procedure pop(E: out elem)
propagate underflow arrcrt empty(atack);
end stuck
-
This additional documentation is valuable to the programmer even if a formal
verification is not attempted. The Ada syntax merely states that any of the
module procedures may raise any one of the listed exceptions. The propagate
declarations give the extra information that, for example, pop will never raise the
exception over flow.
The extra problem cornea in verifying that exceptions are raised correctly in
the stack body. Internal assertions associated with exceptions may refer to local
variables of the module body in addition to parameters of procedures. Thus the
condition f ull(shzck) may internally correspond to a condition length = size for
some variable fen&h local to the module body. Consequently, for ouerjlow the
following raise axiom is valid within the stack body:
{length = size} raire over flow {fulse}.
A “module declaration rule” has to enforce that the visible propagate assertion for an exception is equivalent to the assertion in the body that describes the
internal state in which the exception is propagated.
5.3. Predeflned exceptions
It is conceivable that predeflned exceptions can be handled w ith our method.
The si,tuation here is complicated by the fact that predefined exceptions are
11
usually raised implicitly by numerous different statements and expressions. Therefore, all proof rules have to be changed to account for exceptions. For example,
the assignment z + y/z in a scope where A is the assertion associated with
diuide,error is not described by the standard assignment axiom {P l&}z +
y/z(P) but rather by the rule
{&I 2 + Y/Z (P)
This rule differs from the standard assignment axiom in having an extra
premiss requiring proof that the associated assertion, A, is true when divide-error
is raised. Similar extra premisses must be added to the proof rules for many
other language constructs leading to an exponential increase in the number of
subproblems such as & /\ z = 0 + A. If, however, A is the assertion true, then
the additional subproblems are trivial. In many casea associating true with an
exception handler will be sufficient to verify that a program continues correctly
after an exception has been raised.
Since predeflned exceptions frequently correspond to “runtime erroram, an alternate approach towards predeflned exceptions is to verify that for certain sets of
input values these exception8 will never be raised. Positive results in this direction
are reported in [4].
6. Conclusion
-
We have given an axiomatic semantics for Ada exceptions based on a method
of formally specifying exceptions; the method extends to packages in a natural
way. The required documentation appears very natural and should not be too
demanding for the programmer. The propagation declaration is a helpful specification even in cases where a formal verification is not desired as is shown by the
documentation of the stack module in 5.1. The additional information provided
- by propagate declarations can be used to check for certain semantic restrictions
at compile time, e.g. to check that no exception is propagated out of its scope, or
that all exceptions that may be raised within a subprogram and not propagated
are handled within that subprogram.
The question of how the task failure exception can be treated in a multi task
program and whether this can be done consistently with our method has to be
investigated in future research.
12
Acknowledgement
We are greatful for helpful conversations with Jean Ichbiah and Steve German.
Reference1
1. Preliminary Ado reference manual and r&mule; Sigplan Notices 14,6 (1979)
2. Cartwright, R.S., Oppen, D.C.: ‘I;he Logic of Akzsing; Proc. 5th. ACM
Symp. on Principles of Programming Languages, Tucon (1978)
3. Department of Defense: STEELM AN requirements for high order computer
progrumming lunguugee; (June 1978)
4. German, KM.: Automating proofs ojthe ubsence oj common runtime errors;
Proc. 5th. ACM Symp. on Principles of Programming Languages, Tucon (1978)
pp 105-118
5. Grier, D., Levin, G.: A procedure cull proof rule (with u dimple ezplunutbon);
Tech. Report 79-379, Cornell University (1979)
6. von Eenke F. W., Luckham D, C,: Automatic Program Verification III:
A Methodology for Verifying Progrums; Memo AIM-256, Stanford Artiflcail
Intelligence Laboratory (1974)
7. Hoare, C. A. R.: Procedures and parameters: an uziomutic approach;
Lecture Notes in Mathematics 188, Springer (1971)
8. Hoare, C. A. R.: An Axiomatic Basis of Computer Programming; Comm
ACM 12,lO (October 1969) pp 576-580
9. Igararhi, S., London, R. L., Luckham, D. C.: Automatic Program Verification
1: Logicul &bsis and Its Implementation; Acta Informatica, Vol 4 (1975) pp
145-182
10. Knuth, D.E.: Structured Programming with go to etutements;
Surv., 6,4, (1974)
Comp.
11. Stanford Verification Group: Stanford Pascal Verifier, User Manual; Report
No. STAN-CS-79-731, Computer Science Department, Stanford University (1979)
13
Appendix
Propagating cxceptionr out of their scope
The Ada rationale [l]gives the following example of exceptions propagated
out of their scope (12.5.2):
package D ir
procedure A;
procedure B;
end
- Outside is declared outside the scope
of error
procedure Outside ir
begin
e..
D.A;
...
end
- D.A may propagate error in which case Outside
will propagate “something” that cannot be
named syntactically in the declaration of O utside
.
package body D ir
error: exception;
procedure A iI
begin
-
scope of error-exception
-
call to Outside may propagate ‘something” which
the Ada implementation will recognize as error
. . . raire error;
end;
procedure B 1s
begin
...
Outside;
...
exception
when error => .
end;
l .
end D
In this example the scope of the exception error is the body of D. The
call D.A in outside may propagate error and cause error to be propagated from
Outside. Since Outside is declared outside the scope of error, this exception
cannot be named explicitly in a handler for Qutside. It can be handled only by
14
.
an others clause. The programmer has essentially lost the ability to distinguish
error from other exceptions and to handle it distinctly. In programs, exceptions
propagated out of their scope become anonymous. Interestingly, the Ada implementation will maintain the identity of error when it is propagated outside
its scope. Therefore, if it is propagated back into its scope again - as in this example when Outside is called from B - it will be handled by a handler for error
and not by others. The implementation thus behaves as though all exception
propagations are within the scope of the exception.
If Ada is extended to require propagate declarations (without an others
clause), propagation of exceptions beyond their scope is syntactically illegal. For
example the package D must be modified. Since A can propagate error and A
is a visible procedure of D, the exception error has to be declared in the visible
part of D. Then the exception, D.error, is visible to all scopes which can call
D.A. Using our documentation, the above example could be written as:
package D is
- D.error exception declaration
error: exception;
- all calls to D.A are within the sco~~~-.~~f
procedure A
propagate error arrert P; D.error
procedure B;
end
procedure Outside
propagate D.error assert &
ir
begin
...
D.A;
-
...
...
- & is the assertion associated with D.error
- a handler for D.error could also be
included in outside
end
If one chooses to allow others clauses in propagate declarations as indicated
in section 5, then bropagation of exceptions out of their scope becomes possible.
In this case our method will work correctly according to the Ada semantics.
15
Proof of file tramfer example
We consider a slightly modifled version of the file transfer example given
in the Ada rationale in 12.3.3. In addition to propagate and handler assertions,
the program is documented with entry, exit, initial, and invariant assertions as
introduced for Pascal Plus in [II].
We assume that the system routines get and put have the following specifi-
cations:
generic(type t)
procedure get(j: in out flle of t; z: out t)
initial j = j0
entry true
exit lempty(jO) /\ j0 = uppend(ht(z), j)
propagate end-o j- file arrert empty( fo) A j = j0;
generic(type t)
procedure put( j:in out flle of t; z: in t)
- initial j = j0
entry true
exit j = append( jO,list(z));
We prove the correctness of the following transfer procedure; get and put are
assumed to be appropriate instances of the generic procedures declared above:
procedure tram jer(in j: In out file of t; outj: out fllc of t)
initial inj = info
entry empty(out j)
exit outj = in j0
ir
c:t;
begin
invariant append(out j, in j) = in j0
loop
!74w, 4;
put(outj, 4
end loop;
exception
when end-o j- file assert empty(inj) A outj = in j0 =>;
end;
The correctness is established by proving the following verification conditions.
Note, that instead of introducing universal quantifiers we use new variables to
16
construct formulas logically equivalent to the quantified formulas in the procedure
call rule.
1) Correctnerr of the handler: The handler code (empty statement) has to achieve
the exit condition of the block:
(empty(in j) A out j = in j0 +
outf = info)
This verification condition is trivially true.
1) Corrcctnerr of iairing the exception: The propagate assertion for end-o j- file
in get must imply the handler assertion. Since get is called within the loop, it
may be assumed that the loop invariant is true when a propagation occurs; the
verification condition has the form
invariant /\ propagate assertion + handler assertion:
inf -0 is a new variable denoting the value of the actual parameter in j of the call
to get when propagation occurs.
(append(out j, inf) = info A in j = inj,0 A empty(in j)
ezpty(inf -0) A out f = info)
This verification condition simplifies to true, observing that append(outj, inf) =
outf whenever empty&n j).
3) Correctnerr of entering and leaving the loop: The entry condition of procedure transfer must imply the loop invariant; when the loop terminates the loop
invariant and the negation of the loop test (-true) must imply the exit condition
of transfer. outj,0, in j- 0 are new variables denoting the final values of outj
and in j on exit from the loop.
- info I\
e’ipt y(out j)
.
-
(
f
azpend(outj, in j) = dnjoA
(append(outj,0, in j- 0) = info A
-rtrue
-b
outf-0
= in j0))
Propositional reasoning reduces this condition to
( inf = in j0 /\ empty(outf) +
17
append(outf, in j0) = info)
which can be seen to be true by properties of append.
4) Correctnerr of loop: The loop invariant must be preserved by executing the
loop body:
(append(out j, in j) = in j0 A
-rempty(in j) A
inj = append(ldat(c,O), in j-1) I\
out j- 1 = append(out j, list(c,0))
aTpend(outj-I, inj-1) = info)
After some substitutions this becomes
append(out j, append(list(c,O), in j-l)) = in j0
aGpend(append(out j, list(c,O)), in j-1) = in j0
which is true by associativity of append.
18