Научная статья на тему 'Logic of non-interacting programs and reactive systems'

Logic of non-interacting programs and reactive systems Текст научной статьи по специальности «Математика»

CC BY
134
30
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
ЛОГИКА ПРОГРАММЫ / ТОТАЛЬНАЯ КОРРЕКТНОСТЬ ПРОГРАММЫ / ДЕДУКТИВНАЯ ВЕРИФИКАЦИЯ / ФОРМАЛЬНАЯ СЕМАНТИКА ЯЗЫКА ПРОГРАММИРОВАНИЯ / РЕАКТИВНАЯ СИСТЕМА / PROGRAM LOGIC / TOTAL PROGRAM CORRECTNESS / DEDUCTIVE VERIFICATION / FORMAL SEMANTICS OF A PROGRAMMING LANGUAGE / REACTIVE SYSTEM

Аннотация научной статьи по математике, автор научной работы — Shelekhov Vladimir I., Tumurov Erdem G.

The notion of a program logic is introduced to denote a set of predicates which are true in different program points. The program logic can be easily constructed for different kinds of statements of an imperative or a functional language with data types except pointers. For a non-interacting program, a total correctness formula based on the program logic is defined. The rules of program correctness proof have been developed for proving the statements of various kinds. For a reactive system, the program logic is defined as a predicate on program state variables which is true after executing the current action of a trace constructed as a sequence of interleaved actions from different parallel interacting processes. An action is a maximal program fragment (without loops) of a process source code for which the program logic can be easily constructed. To analyze the behavior of a reactive system, the notion of a meta-state is introduced to denote an approximation of logic for a set of traces.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Logic of non-interacting programs and reactive systems»

УДК 004.415.52

©В.И. Шелехов, Э.Г. Тумуров ЛОГИКА НЕВЗАИМОДЕЙСТВУЮЩИХ ПРОГРАММ И РЕАКТИВНЫХ СИСТЕМ1

Логика программы - предикат (логическое утверждение), определяющий логику решения задачи и являющийся точным эквивалентом программы. Программа - это реализация логики решения в конструкциях языка программирования. Понятие логики программы обозначает также набор предикатов, истинных в разных точках программы. Эффективно и просто строится логика императивных и функциональных программ для различных выражений, операторов и типов, за исключением указателей. На базе логики программы определена формула тотальной корректности программы относительно спецификации в виде предусловия и постусловия. В целях дедуктивной верификации разработана система правил доказательства корректности программ для различных видов операторов.

Для реактивной системы логика программы - набор предикатов на переменных состояния программы. Предикат из этого набора истинен после исполнения некоторой очередной акции трассы, составленной перемешиванием акций параллельных взаимодействующих процессов. Акция - максимальный фрагмент кода программы процесса (без циклов внутри), для которого логика легко строится. Содержательное описание алгоритма функционирования реактивной системы формализуется в виде спецификации, представленной машиной конечных метасостояний как аппроксимации логики программы на множестве трасс. Разрабатываемый аппарат ориентирован на разработку, тестирование, моделирование и верификацию программной и аппаратной части встроенных систем аэрокосмической отрасли, энергетики, медицины и других приложений, где необходима предельная надежность систем.

Ключевые слова: логика программы, тотальная корректность программы, дедуктивная верификация, формальная семантика языка программирования, реактивная система.

V.I. Shelekhov, E.G. Tumurov LOGIC OF NON-INTERACTING PROGRAMS AND REACTIVE SYSTEMS

Abstract. The notion of a program logic is introduced to denote a set of predicates which are true in different program points. The program logic can be easily constructed for different kinds of statements of an imperative or a functional language with data types except pointers. For a non-interacting program, a total correctness formula based on the program logic is defined. The rules of program correctness proof have been developed for proving the statements of various kinds.

For a reactive system, the program logic is defined as a predicate on program state variables which is true after executing the current action of a trace constructed as a sequence of interleaved actions from different parallel interacting processes. An action is a maximal program fragment (without loops) of a process source code for which the program logic can be easily constructed. To analyze the behavior of a reactive system, the notion of a meta-state is introduced to denote an approximation of logic for a set of traces.

Keywords: program logic, total program correctness, deductive verification, formal semantics of a programming language, reactive system

1. Introduction

A program logic is a set of program properties expressed in the form of logical assertions (predicates) used by a programmer in the process of program development. These assertions originate from a mathematical problem solution and should be true in some points of the program execution; see [9] for details. More formally, the program logic at a program point is true for some values of the program variables if and only if there exists an execution reaching the point with those values.

The program logic differs from the specification of a program. It is the property of the program source code only, whereas specification is supplied by a programmer or customer. In deductive verification, the program logic can be extracted from a program with the help of formal semantics of a programming language and used in correctness condition formulas.

In Section 2, the notion of a program logic is defined for a subclass of programs not interacting with the external environment. This subclass includes programs for discrete and computational mathematical problems. The program logic can be easily constructed for different kinds of statements of an imperative or a functional language with data types except pointers. The logic of a loop statement can be defined as

1 Работа выполнена при поддержке РФФИ, грант 12-01-00686.

the least fixed point of a recursive equation. The notion of (over)approximation of a program logic is defined. There are some interrelations between new notions and well-known ones. A loop invariant is a quite strong approximation of the program logic at the beginning of the loop. The program logic is exactly the strongest postcondition relative to the totally true precondition.

The method of deductive verification of non-interacting programs is described in Section 3. Formula (7) defines the total correctness of a program relative to its specification in the form of a precondition and a postcondition. To simplify generation of correctness formulas, specialization of formula (7) in the form of proving rules for different kinds of statements is introduced. The verification method is illustrated by an example program. An extended description of deductive verification with complete proofs of theorems, lemmas, and rules can be found in [9].

The condition of total correctness (7) is not valid for an interacting program, because the program may not finish or may have no resulting data and, as a consequence, will not have a postcondition. Nevertheless, the concept of a program logic may be extended over interacting programs. Section 4 introduces the first attempt to do it for the ordinary reactive systems which is only a subclass of various interacting programs.

The related work is presented in Section 5. An experience of deductive verification for about 30 small example programs is described in Conclusion.

2. Program logic for non-interacting programs

Let a program be the statement S(x, y) in an imperative or a functional language with the arguments x and results y, where x and y are variable sets. There are no interactions (inputs, outputs, etc.) between the program and its external environment. Thus S(x, y) implements a function from x to y.

The program logic is a predicate L(S(x,y)) which is true for some values of the variables x and y if and only if there exists an execution reaching the end of the program S(x, y) with that values of variables x and y. The notion of program logic can be similarly defined for any construct (statement or expression) of a programming language. For example, the logic of the expression a lb is b^O under the assumption that the language semantics does not restrict the representation of very large and small values.

Let us try to define the logic of different statements. Below, for modified variables of a statement, the primed variables correspond to the program state after executing the statement and unprimed ones represent the program state before executing the statement.

For the assignment statement a :—E, where E is an expression, its logic L{a :=E) is the predicate a'=E . Here, a and a1 denote the initial and final values of the variable a, respectively.

Let us consider the sequential statement B; C defining the execution of the statement C after the execution of B . Let us define L(B; C) using the logics IX B) and L(C). The case when the execution of C does not depend on the execution of B should be considered separately. We use the denotation A(x:y) for the statement A with the arguments x and results y.

Instead of the statement B\, C we introduce the superposition statement B(x .z); C(z:y) and the parallel statement B(x:y) \\C(x:z). The third statement in our consideration is the conditional statement if(Ii)K(x :y) else C(x :y), where E is an expression that can depend on x Under the assumptions that the variable sets x, y, and z are disjoint and the set x can be empty, let us define the logics of the statements:

L(B(x : z); C(z:y) = 3z. L(B(x : z)) & L(C(z : y)) (1)

L{B{x : y) || C(x : z) = L{B{x : yj) & L{C{x : z)) (2)

L(ii{E)B(x \y)elseC(x \y)) = E& L(B(x :y)) v —E& L(C(x :y)) (3)

The equivalent formula if(E)L(B(x:y)) else L(C(x:y)) can also be used for the logic of the condi-

tional statement.

Below, for the statement B(u,x ) with modified variables x and arguments u, we will also use the denotation of the form B(u,x :x'). Let the statement while E do B(u,x) end is denoted by W(u,x) with the results x and arguments u and x It can be expressed in the form of the following recursive procedure:

proc W(u,x) {if (E) i B(u,x); W(u,x)}}

The logic of the while statement can be constructed by applying (3) and (1) to the procedure body and should be presented as the least fixed point of the following recursive equation:

L(W(u,x:x')) = if(E)3z.L(B(u,x\z) &L(W(u,z:x')) elsex' =x (4)

Here, u, x, x\ and z are disjoint variable sets.

In a similar way, the program logic can be defined for each point of a program. The program logic LP(S(z)) of a program S at its point p is true for some values of the program variables z if and only if there exists an execution reaching the point p with the same values of z. At each point, the program logic can be constructed if there exists an algorithm constructing the logic of any statement.

LP(S) is the strongest formula which is true when the execution reaches the point p. So, in deductive verification, to establish the truth of a property X at the point p of the program S, it is sufficient to prove that the formula Lp(S) => X is true. To use a program logic L in deductive verification of programs, one should formally prove that the definition of the logic L is correct. First, the formal operational semantics should be developed for a programming language. Next, the definition of the program logic L should be formalized. For any statement in the programming language, one should prove that its program logic is consistent with the formal operational semantics.

For a point p in a program S, a predicate R is called an (over) approximation of the logic LP(S) if

Lp(S) => R holds. For a property X at the point p, if one can prove the formula R then obviously

LP(S) => X, and the property X is true. In deductive verification, one can use an approximation R if it

easier to construct than the logic LP(S) or it has a simple structure allowing SMT solvers to be applied in

proving. The approximation R should be strong enough in order for it be possible to prove X from R. The predicate true is obviously an approximation of any program logic. But it is useless, because no property can be proved from it. However, the approximation true is safe and can be used as an initial state in constructing an appropriate approximation.

In Hoare deductive verification [2] of imperative programs, loop invariants are used in generating the verification condition to be proved. The automated construction of a loop invariant is a challenge. Note that a loop invariant is an approximation of the program logic at the beginning of the loop. This approximation should be strong enough in order that the conditions generated for loops be proved.

For a statement S and predicates P and Q, a. Hoare triple i Fix) { S(x: y) {Q(x,y)} [2] is true if the following assertion holds: if P(x) is true before execution of S then Q(x,y) is true after it. The predicates P(x) and Q(x,y) are called a precondition and postcondition, respectively. The postcondition Q (denoted as sp(S,P)) is called the strongest postcondition of S with respect to P if the triple {P}S {Q} is true and for all Q such that {P}S {Q} is true, Q=> Q" holds. The program logic IXSix: y)) is exactly the predicate sp(S, true).

3. Deductive verification of non-interacting programs

Let a program be a statement Six :y ), where x and y are disjoint sets of variables. During the execution of the statement Six :y ), no interactions with the external environment are possible. We suppose

that all inputs take place before executing Six \y) and all outputs are moved behind S(x :y).

In deductive verification of a non-interacting program, it is sufficient to check that the specification of the program holds after program execution. The specification of the program Six :y) can be represented in the form [/j(a'), Q(x,y)\, where the predicate P(x) is a precondition and Q(x,y) is a postcondition. Correctness of the program is defined by the following correctness conditions:

• the precondition P(x) should be true at the beginning of the program;

• the postcondition Q(x,y) should be true at the end of program;

• a program execution always terminates.

The third condition should hold because infinite execution of a non-interacting program is useless. It can be expressed by the predicate 3y. L(S(x : y)) , according to the definition of a program logic.

It is impossible to prove the truth of the precondition P(x) because P(x) does not depend on the program S(x :y). Thus, it is sufficient to prove the second and third correctness conditions under the assumption that P(x) is true. The correctness conditions are expressed by the formulas:

P(x) &L(S(x:y)) => Q(x,y) (5)

P(x)=>3y.Z0S'(x:;)')) (6)

Formula (5) is called a condition of partial correctness. Formula (6) is called a condition of terminating a program. Their conjunction defines the condition of total correctness of the program Six :y) relative to the specification \P(x),Q(x, y)\:

Corr(S(x: y ), P(x), Q(x,y)) = (7)

P(x)=> [L(S(x:y)) =>Q(x,y) ] & 3\y.L(S(x :y))

Further the term “correctness” will be used in the sense of total correctness. Let us reformulate (7) as the following rule of program correctness:

TO. PW&Z№:.y))l~ Q(x,yX ^(x)\-3y.L(S(x:y))

Corr(S(x :y), P(x), Q(x,y))

The well-known notions of a total function and a single-valued function can be extended to predicates.

The notions “the program S(x :y) is single-valued”, “the specification [/j(a'), Q(x,y)\ is totaF, and

“the specification is single-valued” are defined by the following formulas:

P(x)&L(S(x:y1))&L(S(x:y2)) =>yx=y2; (8)

T(P(x), Q(x, y)) = P(x) => 3y. Q(x, y); (9)

SV P x ,Q x,y =P x &Q x,y1 &Q(x,y2)^y^y2 (10)

Note. A program totality property is precisely the correctness condition (6).

Non-single-valued program is nondeterministic. If the statement B(x .z) is not single-valued, it is possible for the superposition statement B(x .z); C(z:y) to terminate nondeterministically or not to terminate for some x. The notion of general correctness [8] has been developed for that case. The question arises if there exists a useful non-interacting program with this behavior in practice.

Lemma 1. If the program S(x:y) is correct relative to the specification [/j(a'), Q(x,y)\, then the specification is total.

The subformula L(S(x:y)) =>Q{x,y) of the total correctness formula (7) defines the inference of the specification from the program logic. There are approaches (e.g. program synthesis) with a backward inference of a program from its specification2. The following theorem defines the conditions under which the program correctness can be proved by means of backward inference.

Theorem 1 of identity between specification and program.

Let the program S(x: y ) be single-valued, the specification [/j( a;), Q(x,y )\ be total, and the following formula be true:

P(x) &Q(x, y) =>L(S(x:y)) . (11)

Then the program S(x :y ) is correct relative to the specification.

Lemma 2. Under the conditions of Theorem 1, the following formula holds:

P(x)^(L(S(x:y)) = Q(x,y)) .

Corollary. Under the conditions of Theorem 1, the specification [/j( a;), Q(x,y )\ is single-valued.

If a specification is not single-valued, then formula (11) is false and therefore unprovable. Thus, formula (11) is applicable in verification for a single-valued specification only.

2 In program synthesis, the program is extracted from a constructive proof of the theorem T(P(x), Q(x,y))

The first condition of Theorem 1 is usually true. It is sufficient to prove that a program is single -valued if all basic operators used in the program are single-valued. This fact should be formally proved in the development of the formal operational semantics of a programming language.

Using the program logic for a programming language, we can construct automatically the correctness condition formula for the program S(x :y) on the base of formula (7) or (11). The resulting formula will be complicated and long even for short programs. Specialization of formulas (7) and (11) for different kinds of statements allows us to decompose a long correctness formula into a set of short and simple correctness formulas.

Below we present specialization of formula (7) in the form of the correctness proof rules for the superposition statement B(x :z);C(z :y) , parallel statement B(x :y)\\C(x:z), and conditional statement ii{E)B(x:y) else C(x :y) assuming the variable sets x, y, and z are disjoint.

Corr(B(x: y),P(x),Q(x,y)); Corr(C(x: z),P(x), R(x, z))

QP' Corr(B(x :>') ||C(x:z),P(x),Q(x,y) &R(x,z))

QC: oorr(B(x: y), P(x) &E, Q(x, y)); Corr(C(x: y), P(x) & —E, Q(x, y))

Corr(ii(E)B(x: y) elseC(x: y\ P(x), Q(x, >■))

P(x) \-3z.L(B(x\z)y, Corr(C(z :y), P(x) &L(B(x:z)), Q(x,y))

...............................................

Corr(B(x :z):('(z :y). P(x). (J(x.y))

Using these rules, one can reduce the correctness proof of superposition, parallel, and conditional statements to the correctness proofs of the substatements B and C. These rules can be repeatedly applied until the elementary statements are reached.

We also need to consider another case when there are specifications for the substatements B and C. The rules for that case are presented below.

Corr*(B(x:y), PB(x), QB(x,y)); Corr (C(x :z), Pc(z), Qc(x,z))

P(x) [ P*(x) &P*(x); QB(x,y) &Qc(x,z) \-Q(x,y,z)

RP- Corr(B(x:y) 11C(x:z),P(x).(J(x.y.z))

Corr (B(x \z), PB(x), QB(x:z)); Corr*(C(z :y), Pc(z), Qc(z,y));

RS: P(x)\-P*(x); P(x)&QB(x,z)\-P*(z);

P(x) &QB(x,z) &Qc(z,y) 1~Q(x,y)

('orr( B( x :z):('(z: y). P( x ).(J( x.y))

Corr * (B(x: y\PB {x\QB (*,>’)); Corr *(C(x: y ),Pc {x\Qc(x,>’));

P(x) &E |-i^(A:); P(x) E \ P*(x);

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

RC: P(x) 8lE &QB(x,y) 1-Q(x,y); P(x) &^E &Qc(x,y) \Q(x,y)

('orr( //'(I'.)B(x.y) else C(x :y), P(x). (J(x.y))

Corr*(B(x :z),PB(x),QB(x,z));Corr*(C(z :y),Pc(z),Qc(z,y));

SV(PB(x),QB(x,z));

RB. 8lP<,(B(x)): P(x) 8cQc(B(x),y)\-Q(x,y)

Corr(C(B(x):y).P(x).Q(x.y))

In the above rules, if the substatement B is the recursive call of a procedure, the premise Corr* (B,...)

is omitted and Pg(x) is replaced by PB(x) &m(x)<m(z), where z denotes the arguments of the recur-

sive procedure and in is a measure function (of nat type) defined on arguments. If the substatement C is

a recursive call, the denotations Corr* and P* have the same meaning. When the substatement B (or

C) is not a recursive call, Corr* and P* simply denote Corr and P.

The last rule RB is a specialization of RS. Here, C(B(x):y) is equivalent to B(x :z); C(z :y). Because z = B(x), z is replaced by B(x) in the rule RB.

The rules QS and TO include premises of two forms R(x,y) => 3y. L(S(x :y)) and R(x,y) 8cL(S(x: y)) => H(x,y ). where R(x,y) and H(x,y) are arbitrary predicates. The next problem is to develop the rules for decomposing the occurrence L(S(x:y)) in these forms. Let us present such rules for the superposition statement only.

fe R(x) \-3z.L(B(x :z)); R(x) &L(B(x:z)) \-3y.L(C(z:y))

R(x)\-y.L(B(x :z); C(z:y))

FL: L(B(x:z))&L(C(z:y))\-H(x,y)

L(B(x :z); C(z\y))\-H(x,y)

Let us illustrate application of the rules by an example program of multiplication of two natural numbers a and b with the result c using only the addition and subtraction operators. To obtain the algorithm in a tail-recursion form for transforming it automatically to an efficient while statement at the compilation stage, we need to consider a more general problem mult(a,b,d: c ) with the specification [a>0 &b>0 &d>0, c=a*b + d] and the following program:

procmul^nata^b^d : natc) (12)

{if (a = 0)c = d elsemult{a -1 ,b,d + b.c) } measure a;

First, we introduce the definitions:

formulaPmult(nata,b,d) = a>0 &b>0 &d>0; formula Qmuhi nata, b, d, c) = c=a*b + d; functionm(nata: nat)=a;

Application of the rule QC to the program produces two formulas for proving:

Corr(c=d, Pmult(a,b,d)8ca=0 , Qmult(a,b,d,c))

Corr(mult(a-l,b,d + b :c),Pmult(a,b,d)8ca = 0, Qmult(a,b,d,c)) Application of the rule TO to the first formula above produces the lemma:

lemma Pmult(a,b,d) & a=0 & c=d^>Qmult(a,b,d,c);

To decompose the second formula above, let us replace the statement mult(a—\,b,d+b: c) by an equivalent one

\a\d'\=\a —\,d+b |; mult(a\b,d': c) and then apply the rule RB. Because the call of mult is recursive, the rule RB is modified to

Corr(B(x :z\ Pb(x\ Qb(x:z)); SV(Pb(x\ Qb(x,z));

P(x) 1~PB(x) 8cPc(B(x)) & m(B(x))<m(x);

P(x) &Qc(B(x\y) [ Q(x,y)

RIS Corr(C(B(x): y\P(x), (J(x. y))

Here Pb =a—1>0 and Qb =a'=a— 1 & d'=d+b. For the premises RB*.3 and RB*.4, the following lemmas are generated: lemma Pmult(a, b, d) & —. a = 0 ^>a-l>0 & m(a -1) < m{a) lemma I^nuhi a, b, d ) & —,a = 0 ScOmuhi a -1, b, d + b, c )

=> Qmult{a,b,d,c)

The correctness of program (12) can be ensured by proving the above three lemmas.

One can compare the described verification of program (12) with the classic Hoare verification of the following imperative program obtained from program (12) by transforming the tail-recursion to the while statement:

c := d; while a^O doa := a-1; c :=c + b end

The superposition, parallel, and conditional statements can be used as the basis for construction of any programming language. The predicate programming language P [3], which is at the boundary of the functional and logical languages, has been developed on the base of these three kinds of statements. Its formal semantics, consisted of the operational and logical semantics, and the rules for proving the program correctness have been developed for the full language P [4, 6, 7]. An algorithm of generating the correctness condition formulas is implemented as a back-end in the compiler from P to C++.

An extended description of deductive verification with complete proofs of theorems, lemmas, and rules can be found in [9].

4. Logic of reactive systems

The condition of total correctness (7) is not valid for an interacting program, because the program may not terminate or may have no resulting data and, as a consequence, will not have a postcondition. Nevertheless, the concept of a program logic can be extended over interacting programs. Our consideration is restricted within the set of ordinary reactive systems which is only a subset of various interacting programs. An illustration of notions defined below is given on an example of the Alternating Bit Protocol in Appendix A.

A reactive system consists of several processes executing in parallel and interacting among themselves and with the external environment by means of sending and receiving messages via unbounded FIFO channels. The following new kinds of statements are used in reactive systems:

loop{B} sendmie) receivem(x){B}elseC with{y){B} i' |GHcrc. B and C are statements, x and y are variable lists, e is an expression list, in is a message name, F\\G is a parallel statement for processes F and G, with(y){B} is a protected statement, blocking the access to variables y for other processes until terminating B.

When the statement loop{B} executes infinitely, the logic after the loop is false. For the case when the loop statement may terminate by executing the break statement inside B, it is problematically to define the logic of this loop adequately because the definition in the form the least fixed point like (4) would be wrong; an existence of other useful definitions is doubtful.

An action is a maximal program fragment of a source code not including loop statements. Logic of any action can be easily constructed using formulas (l)-(3). For a nested loop structure of the source code for a process, it is possible to construct an action graph where each edge defines transition from one action to another. A trace is an action sequence which can be produced by some path in the action graph. An execution of a process can be defined as the execution of the action sequence of some its trace.

For the parallel statement /' \\G. a state is a set of variables with their values. The state includes global variables modified by either process. A channel between F and G belongs to the state for F\\G. We assume, the protected statements are inserted in a program so that the simultaneous access to each state variable is impossible for parallel processes. Note that a channel is implicitly protected while executing the send or receive statement for it. For actions a from F and b from G, the parallel composition a | b can be replaced by the sequential one either a; b or b; a producing the same modification of the state. This assertion is true under condition that for each action, none variable of the state can be modified more than once. In the case of multiple modifications within an action, the replacement of a parallel composition to the sequential one will be correct after applying the protected statements to some pieces of the action.

Let FT and (}j be the sets of traces for the processes F and G, respectively. The set (F \ \G)T can be defined as the set of traces FT * GT constructed by all possible interleaving the traces from FT and (}j . Now we can state the following assertion. A parallel execution of the statement F\\G is equivalent to the sequential execution of some its trace.

A program logic of the parallel statement F \ \ G for a trace from the set FT*GT is the program logic at the end of this trace. The trace is reachable if its logic is not false.

A specification of the parallel statement F \\G is a description of the behavior of its execution. Formal specification of F ||G can be given in the form of a finite meta-state machine (FMSM). A state (a node) of a FMSM is called a meta-state. It is attributed with the predicate on the state variables which

should be true when the execution of the FMSM reaches this meta-state. Below we just assume that the predicate is the value of this meta-state. A transition (an edge) in a FMSM is called a meta-transition. It consists of the sequence of the actions interleaved from F and G. A meta-transition may be supplied with a condition that should be true at the exit of the meta-transition.

The parallel statement F\\G should satisfy its FMSM specification. This correctness condition is expressed as the following assertion. For each reachable trace t for F \ \G, there exists the path p}. p2.

pn in the FMSM, where Pj (j = 1, ...,n) are meta-states, and there exist subtraces /,. t2, I n. so that t — tlt2...tn and for each k<n, L(t1t2...tk)pk.

The FMSM specification can be used for detailed analysis of a reactive system. After proving that the specification correctly corresponds to the reactive system, it may be also used for proving the safety and liveness properties of the reactive system. The presented approach may be also used to develop a compiler back-end that automatically generates the correctness condition formulas for model checking and deductive verification of reactive systems.

5. Related work

The term “program logic” has been used as Hoare-style logic since 2003. Hoare logic defines the logic of a program implicitly by means of the rules for Hoare triples. The predicate transformers, like wp, sp, etc, are other implicit definitions of the program logic. We use an explicit definition of the program logic as predicates that are true in some program points.

The notion of a program logic introduced in this paper coincides with Floyd’s interpretation of a program in his seminal paper [1]. An interpretation is a set of predicates at entries and exits of program constructs. In the flowchart language [1], each edge of a flowchart is supplied with a predicate. The language semantics is described by verification conditions for all kinds of constructs. Parameters of the verification condition for a construct are predicates at its entry and exit. According to the language semantics, the predicates of an interpretation should meet the verification conditions. For comparison, the program logic should be consistent with the formal operational semantics.

In verification based on the Hoare-style logic [2], only partial correctness conditions are verified whereas in our approach, total correctness of a program is defined by formula (7) based on the program logic. Another difference is that a measure function for a recursive procedure is easier to construct than an appropriate invariant for the loop obtained by replacing the tail-recursion.

The notion of a program logic is applicable in different fields of program verification. Some examples are presented below.

Logical formulas extracted from a program in the process of its reengineering are called a business logic. These formulas, of course, are an approximation of a program logic. The result of a static analysis of a program is some approximation of a program logic although usually it may be not represented in the form of logical formulas.

In the counterexample-guided abstraction refinement (CEGAR) method of software model checking [10, 11], abstract predicates, constructed for nodes of a program path to decide whether the path is reachable, are a lazy approximation of the program logic. An edge between two nodes corresponds to an elementary operator. In paper [11], an edge corresponds to a whole program fragment excluding loops called a large block. The large block encoding method [11] is based on constructing the program logic of large blocks used for calculation of abstract predicates.

Instead of extracting a logic from a program, logical formulas themselves can be considered as a program if they are executable. For the automatic proof system PVS [5], the PVS ground evaluator consists of a translator from an executable subset of the PVS specification language into Common Lisp [13]. The modeling, simulation and verification language MSVL for concurrent systems is an executable subset of the projection temporal logic [12].

The program logic differs from the specification of a program. It is the property of the program source code only, whereas specification is supplied by a programmer or customer. The program logic can be extracted from a program. But it is impossible to extract specification from the program source code as declared in [14, 15], especially for the case when specification is not single-valued. In fact, an extraction of the program logic is described in [14, 15]. In the best case, the program logic extracted can serve as some under-approximation of specification.

Conclusion

The notion of a program logic is introduced for non-interacting programs and reactive systems to denote a set of predicates which are true in different program points. The program logic can be easily constructed for different kinds of statement of an imperative or a functional language with data types except pointers. For a non-interacting program, the total correctness formula (7), which uses the program logic, is defined. The rules of program correctness proof have been developed for proving the statements of various kinds. The notion of a program logic gives a proper understanding in various fields of program verification.

The superposition, parallel, and conditional statements can be used as the basis for constructing any programming language. The predicate programming language P |3], which is at the boundary of functional and logical languages, has been developed on the basis of these statements. Its formal semantics consisted of the operational and logical semantics has been developed for the full language P [4]. Based on formula (7), the proving rules have been developed for all constructs of the language P. On the basis of these rules, the detailed algorithm of generating the correctness condition formulas has been developed. It translates the correctness formulas to the language of the automatic proof system PVS [5]. It is implemented as a back-end in the compiler from P to C++.

The algorithm of generating the correctness formulas has been tested for about 30 small programs. The most valuable are Ling adder program [6] and the effective programs for the standard functions floor, isqrt, and ilog2 [7]. The generated formulas for these programs have been proved in the PVS system. Proving the formulas on PVS was nontrivial and time-consuming. Twenty accidental errors were found in programs and specifications; only one error found in the floor program was dangerous.

The rules developed for proving the program correctness are also suitable for program synthesis [6, 7]. For the development of a program in the style of synthesis, the proof of the correctness formulas can be easier than in deductive verification.

Correctness formula (11) for the case of a single-valued specification looks simpler than general formula (7). Two years ago, the verification algorithm was developed on the basis of formula (11) only. Surprisingly, the proof of the correctness formulas appears to be more complicated and time-consuming than the proof of analogous formulas based on formula (7). The reason is that, along with the existence theorem T{P(x\ Q(x,y)), we often need additionally to prove the unicity theorem. The important consequence of this fact is that the classic methods of program synthesis based on the existence theorem also suffer the same drawback.

References

1. Floyd R.W. Assigning meaning to programs // Proc. Symposium in Applied Mathematics, 1967. - Vol. 19. P. 19-32.

2. Hoare C. A. R. An axiomatic basis for computer programming // Communications of the ACM. 1969. Vol. 12 (10). P. 576-585.

3. Karnaukhov N.. Perchine D.. and Shelekhov V. The predicate programming language P. - Preprint 153. Institute of Informatics Systems, Novosibirsk, 2009. - 44 p. (In Russian).

4. Slielekhov V. The language of calculus of computable predicates as a minimal kernel for functional languages // BULLETIN of the Novosibirsk Computing Center. Series: Computer Science. IIS Special Issue, 29, 2009. P. 107-117.

5. PVS Specification and Verification System. - SRI International. - URL: http://pvs.csl.sri.coin/

6. Shelekhov V. Verification and synthesis of addition programs under the rules of statement correctness // Modeling and analysis of informations systems. - Yaroslavl, 2010. - Vol.17, No. 4. P. 101-110. (In Russian).

7. Shelekhov V. Verification and synthesis of effective programs for the standard functions floor, isqrt, and ilog2 under predicate programming technology // Complex System: Control and Modelling Problems; XII International Conference. -Samara, 2010. P. 622-630. (In Russian).

8. 8 Dawson J. E. Fonnalising General Correctnes // Electronic Notes in Theoretical Computer Science, Vol. 91, 2004. P. 21-42.

9. Shelekhov V. Rules of correctness proof for programs with simple logic // Modem Problems of Mathematics, Informatics and Bioinformatics, 2011. - 17 p. (in Russian). URL: ittp://conf.nsc.ru/files/conferences/Lvap-

100/fulltext/74974/75473/Shelekhov prlogic.pdf

10. Beyer D„ Henzinger T., Jliala R., Majumdar R. The Software Model Checker Blast: Applications to Software Engineering // Int. Journal on Software Tools for Technology Transfer, 9, 2007. P. 505-525.

11. Beyer, D., Cimatti, A., Griggio, A., Keremoglu, M.E., Sebastiani, R. Software model checking via large-block encoding // Fonnal Methods in Computer-Aided Design, 2009. P. 25-32.

12. Mo D„ Wang X., Duan Z. Asynchronous Communication in MSVL// ICFEM’2011, LNCS 6991, 2011. - P. 82-97.

13. Crow J., Owre S., Rushby J., Shankar N., and Stringer-Calvert D. Evaluating, Testing, and Animating PVS Specifications // CSL Tech. Report, 2001. - 55 p.

14. Dallmeier V., Knopp N., Mallon C., Hack S., Zeller A.: Generating test cases for specification mining // ISSTA'10, 2010.-P. 85-96.

15. Nguyen A. C., Khoo S.-C. Extracting Significant Specifications from Mining through Mutation Testing // ICFEM'2011, LNCS 6991, 2011. - P. 472-488.

Шелехов Владимир Иванович, канд. техн. наук, зав. лаб. системного программирования Института систем информатики, г. Новосибирск, тел. (383)330-27-21, e-mail: [email protected]

Тумуров Эрдэм Гармаевич, младший научный сотрудник Института систем информатики, г. Новосибирск, тел. (383)330-27-21, e-mail: [email protected]

Vladimir Shelekhov, candidate of technical sciences, laboratory head in A.P.Ershov Institute of Informatics Systems, Novosibirsk, Russia.

Erdem Tumurov, junior researcher in A.P.Ershov Institute of Informatics Systems, Novosibirsk, Russia.

i Надоели баннеры? Вы всегда можете отключить рекламу.