• No se han encontrado resultados

Assertion-based debugging of higher-order (C)LP programs

N/A
N/A
Protected

Academic year: 2020

Share "Assertion-based debugging of higher-order (C)LP programs"

Copied!
11
0
0

Texto completo

(1)

Assertion-based Debugging of

Higher-Order ( C ) L P Programs

Nataliia Stulova

1

Jose F. Morales

1 I M D E A Software Institute

{nataliia.stulova josef.morales,manuel.hermenegildo}Simdea.org

Manuel V. Hermenegildo

1,2 2School of Computer Science

Technical University of Madrid (UPM)

[email protected]

Abstract

Higher-order constructs extend the expressiveness of first-order (Constraint) Logic Programming ((C)LP) both syn-tactically and semantically. A t the same time assertions have been in use for some time in (C)LP systems helping pro-grammers detect errors and validate programs. However, these assertion-based extensions to (C)LP have not been integrated well with higher-order to date. This paper con-tributes to filling t his g ap by e xtending t he assertion-based approach to error detection and program verification t o the higher-order context within (C)LP. We propose an exten-sion of properties and assertions as used in (C)LP in order to be able to fully describe arguments that are predicates. The extension makes the full power of the assertion language available when describing higher-order arguments. W e pro-vide syntax and semantics for (higher-order) properties and assertions, as well as for programs which contain such asser-tions, including the notions of error and partial correctness. We also discuss several alternatives for performing run-time checking of such programs.

1. Introduction

Higher-order programming adds flexibility to the software development process. Within the (Constraint) Logic Pro-gramming ((C)LP) paradigm, Prolog has included higher-order constructs since the early days, and there have been many other proposals for combining the first-order ker-nel of (C)LP with di erent higher-order constructs (see, e.g., [5, 6, 8, 22, 23, 30]). Many of these proposals are cur-rently in use in di erent (C)LP systems and have been found very useful in programming practice, inheriting the well-known benefits of code reuse (templates), elegance, clarity, and modularization.

Research supported in part by projects E U FP7 318337

ENTRA, Spanish MINECO TIN2012-39391 StrongSoft and

TIN2008-05624 DOVES, and Comunidad de Madrid TIC/1465

PROMETIDOS-CM. We would also like to thank the anonymous

reviewers for providing valuable comments and suggestions.

A number of extensions have also been proposed for (C)LP in order to enhance the process of error detection and program verification. In addition to the use of classical strong typing [15, 27], a number of other approaches have been proposed which are based on the dynamic and/or static checking of user-provided, optional assertions [3, 4, 10, 13, 16, 21, 24, 26]. The model of [13, 26] has the advantage that it allows stating in the assertions a very general class of properties that goes well beyond classical types, including for example argument sizes or variable sharing, and non-functional properties such as resource consumption bounds. These assertions are checked via a combination of static and dynamic checking in order to perform program verification or error detection. I n practice, di erent aspects of this model have been incorporated in a number of widely-used (C)LP systems, such as Ciao, SWI, and XSB [14, 20, 28]. A similar evolution is represented by the soft/gradual typing-based approaches in functional programming and the contracts-based extensions in object-oriented programming [7, 17–19, 29].

(2)

This paper contributes t o filling the existing gap between higher-order and assertions i n (C)LP. Our starting point is the Ciao assertion model [13, 26], since, as mentioned be-fore, i t has been adopted at least i n part i n a number of the most popular (C)LP systems. After some preliminaries and notation (Section 2) we start by extending the traditional notion of programs and derivations i n order t o deal w i t h higher-order calls and recall and adapt the notions of first-order conditional literals, assertions, program correctness, and run-time checking t o this type of derivations (Section 3). This part allows us t o revisit the traditional model i n this new, higher-order context, while introducing a different for-malization t h a n the original one of [26]. This forfor-malization, which w i l l be used throughout the paper, is more compact and gathers all assertion violations as opposed t o just the first one, among other differences. We then define an exten-sion of the properties used i n assertions and of the asser-tions themselves t o higher-order, and provide corresponding semantics and results (Section 4).

2. Preliminaries and Notation

We recall some concepts and notation from standard (C)LP theory. We denote by VS, FS, and PS the set of vari-able, function, and predicate symbols, respectively. Vari-ables start w i t h a capital letter. Each p G PS and / G FS is associated t o a natural number called its arity, w r i t t e n ar(p) or a r ( / ) . T h e set of terms TS is inductivelly defined as follows: VS C T S , i f / G FS and t\,...,tn G TS then f(ti, • • • ,tn) G TS where a r ( / ) = n. A n atom has the form p(ti,...,tn) where p G PS, ar(p) = n, and t\,...,tn G TS. A constraint is essentially a conjunction of expressions built

from predefined predicates (such as t e r m equations or i n -equalities over the reals) whose arguments are constructed using predefined functions (such as real addition). A literal is either an atom or a constraint. A goal is a finite sequence of literals. A rule is of the form H:-B where H, the head, is an atom and B, the body, is a possibly empty finite sequence of literals. A constraint logic program, or program, is a finite set of rules.

We use a t o represent a variable renaming and a(X) t o represent the result of applying the renaming a t o some syntactic object X (a t e r m , atom, literal, goal, etc.). T h e

definition of an atom A i n a program, defn(A), is the set

of variable renamings of the program rules such that each renaming has A as a head and has distinct new local vari-ables. We assume that all rule heads are normalized, i.e.,

H is of the form p(Xi, ...,Xn) where the X\,...,Xn are dis-tinct free variables. This is not restrictive since programs can always be normalized, and i t facilitates the presentation. However, for conciseness i n the examples we sometimes use non-normalized programs. Let 3L0 be the constraint 0 re-stricted t o the variables of the syntactic object L. We denote

constraint entailment by |=, so t h a t 0\ = 02 denotes t h a t 0\

entails 02. I n such case we say t h a t 02 is weaker than 0\. For brevity, we w i l l assume i n the rest of the paper that we are dealing w i t h a single program, so t h a t all sets of rules, etc. refer t o t h a t implicit program and i t is not necessary t o refer t o i t explicitly i n the notation.

2.1 O p e r a t i o n a l Semantics

The operational semantics of a program is given i n terms of its “derivations”, which are sequences of reductions between “states”. A state {G | 0) consists of a goal G and a constraint store (or store for short) 0. We use :: t o denote concatenation of sequences and we assume for simplicity that the

underly-ing constraint solver is complete. We use S -* S' to indicate that a reduction can be applied t o state S t o obtain state S'. Also, S -** S' indicates that there is a sequence of reduction steps from state S t o state S'. We denote by D^ the i-th state of the derivation. As a shorthand, given a non-empty derivation D, D[_i] denotes the last state. A query is a pair

(L, 0), where L is a literal and 0 a store, for which the ( C ) L P

system starts a computation from state {L \ 0). The set of all derivations from the query Q is denoted derivs(Q). The ob-servational behavior of a program is given by its “answers” to queries. A finite derivation from a query ( L , 0) is finished if the last state i n the derivation cannot be reduced. Note that derivs(Q) contains not only finished derivations but also all intermediate derivations from a query. A finished deriva-t i o n from a query ( L , 0) is successful i f deriva-the lasderiva-t sderiva-taderiva-te is of deriva-the form ( • | 0'), where • denotes the empty goal sequence. I n that case, the constraint ¯ L # ' is an answer t o S. We denote by answers(Q) the set of answers t o a query Q. A finished derivation is failed i f the last state is not of the form ( • | 0).

A query Q finitely fails i f derivs(Q) is finite and contains no

successful derivation.

3. First-order Assertions on Higher-order

Derivations

3.1 H i g h e r - o r d e r P r o g r a m s a n d D e r i v a t i o n s

We start by extending the definition of program, state re-duction, and derivations i n order t o deal w i t h the syntax and semantics of higher-order calls.1

D e f i n i t i o n 1 . Higher-order programs are a generalization

of constraint logic programs where:

• The set of literals LS is extended to include higher-order

literals X(t\,... ,tn), where X G VS and the ti G TS. • The set of terms TS is extended so that PS C TS (i.e.,

predicate symbols p can be used as constants).

I n the following we assume a simple semantics where when a call t o a higher-order literal X(ti,. .. ,tn) occurs, X has t o be constrained to a predicate symbol i n the store:2

D e f i n i t i o n 2. A state S = {L :: G | 0) where L is a literal

can be reduced to a state S , denoted S ^ S , as follows:

1. If L is a constraint and 0 A L is satisfiable, then S = (G | 0 A L).

2. If L is an atom of the form p(ti,... , tn), for some rule (L:-B) G defn(L), then S = (B :: G \ 0).

3. If L is of the form X(ti,... , tn), then S = (G \ 0) where either G' = p(ti,.. . ,tn) :: G if 3p h PS A 0 = (yY p) /\ Br(p) n, or (_x euninsi_can oinerwise. The concepts of answers and of finished and successful derivations carry over without change t o this notion of

(3)

higher-order derivations. The notion of (finitely) failed derivation is extended as follows:

D e f i n i t i o n 3. A finished derivation from a query (L,9)

is failed iff its last state is not of the form ( • | 9 ) or

\euninst_call \ U / .

Finally, we introduce the concept of floundered deriva-tions:

D e f i n i t i o n 4. A finished derivation from a query (L,9) is floundered iff its last state is of the form \euninst-caii \ &/.

3.2 F i r s t - o r d e r Pred Assertions

Assertions are linguistic constructions for expressing prop-erties of programs. They are used for detecting deviations of the program behavior (symptoms) w i t h respect to such as-sertions, or to ensure that no such deviations exist (correct-ness). Herein, we w i l l use the pred assertions of [25], given that they are the most frequently used assertions i n prac-tice, and they subsume the other assertion schemas i n that language. Thus, i n the following we w i l l use simply the t e r m assertion to refer to a pred assertion. Assertions allow spec-ifying certain conditions on the constraint store t h a t must hold at certain points of program derivations. I n particu-lar, they allow stating sets of preconditions and conditional

postconditions for a given predicate. A set of assertions for

a predicate is of the form:

:- pred Head : Prei => Posti.

:- pred Head : Pren => Postn .

where Head is a normalized atom that denotes the predicate that the assertions apply to, and the Pre,; and Posti refer to the variables of Head. We assume that variables i n assertions are renamed such that the Head atom is identical for all assertions for a given predicate. A set of assertions as above states that i n any execution state {Head :: G \ 0) at least one of the Prei conditions should hold, and t h a t , given the (Prei, Posti) pair(s) where Prei holds, then, if

Head succeeds, the corresponding Posti should hold upon

success. The following example illustrates the basic concepts involved:

E x a m p l e 1 . The procedure qsort(A,B) is the usual one that relates lists A and their sorted versions B. The following assertions:

:- pred qsort(A,B) : list(A) => sorted(B), list(B).

:- pred qsort(A,B) : list(B) => permutation(B,A), list(A).

state that (restrict the meaning ofqsort to):

qsort(A,B) should be called either with A constrained to a list or with B constrained to a list;

• ifqsort(A,B) succeeds when called with A constrained to a list then on success B should be a sorted list; and • if qsort(A,B) succeeds when called with B constrained

to a list then on success A should be a list which is a permutation ofB.

3.3 C o n d i t i o n s on t h e C o n s t r a i n t Store

The conditions on the constraint store used i n assertions are specified by means of special literals (e.g., list(A), sorted(B), list(B), and permutation(B,A) i n the previous ex-ample) that we w i l l herein call prop literals. More concretely, we assume the Prei and Posti to be D N F formulas of such literals.

We also assume t h a t for each prop literal Lp used i n some assertion there exists a corresponding predicate p defining i t . Then, we can define the meaning of prop literals as follows:

D e f i n i t i o n 5. The meaning of a prop literal Lp defined by predicate p, denoted \LP\, is the set of constraints given by

answers((Lp,true)).

Intuitively, the meaning of prop literals is the set of “weakest” constraints for which the literal holds:

E x a m p l e 2. Prop literals list/1 and sorted/1 can be de-fined by:

list([]). sorted([_]).

list([_|L]) :- list(L). sorted([X,Y|L]) :- X =< Y,

sorted([]). sorted([Y|L]).

Then, their meaning is given by \list(A)\ = {A = [],A = [B\C] Alist(C)} and \sorted(A)\ = {A = [],A = [B],A = [B, C\D] A B < C A E = [C\D] A sorted(E)}.

The following definition from [26] defines when the con-d i t i o n representecon-d by a prop literal (con-definecon-d by a program predicate) holds for a given store:

D e f i n i t i o n 6. A prop literal L succeeds t r i v i a l l y for 9 in

program P, denoted 9 =^p L, iff BO' G answers((L,9)) such that 6 = 9'. A DNF formula of prop literals succeeds trivially for 9 if all of the prop literals of at least one conjunct of the formula succeeds trivially.

Intuitively, a prop literal L succeeds t r i v i a l l y if L succeeds for 9 w i t h o u t adding new “relevant” constraints to 9: E x a m p l e 3. Consider prop literals list(A) and sorted(B)

and the predicate definitions of Example 2

• Assume that 9 = (A = f). Since W G |Zisi(A)| : 9 = 9 , as we would expect, 9 ^>p list (A).

• Assume now that 9 = (A = [ | X s ] ) . Though A is compatible with a list, it is not actually a (nil terminated) list. Again in this case W G |Zisi(A)| : 9 \/= 9' and thus again 9 ^>p list(A). The intuition behind this is that we cannot guarantee that A is actually a list given 9, since a possible instance of A in 9 is A= [ | / ] , which is clearly not a list.

• Finally, assume that 9 = (A = [B] A B = 1). In such case 30 = (A = [B\C] A C = []) such that 9 = = and

3c = (B = 1) such that (c A 9' \/= false) A (9' A c \= 9).

Thus, in this last case 9 =^p list(A).

This means that we are considering prop literals as

in-stantiation checks [12, 25]: they are true iff the variables

they check for are at least as constrained as their predicate definition requires.

D e f i n i t i o n 7. A prop literal L is a test iff ^6 either 9 =£>p L

or ( L , 9) finitely fails.

3.4 F i r s t - o r d e r A s s e r t i o n C o n d i t i o n s and t h e i r Semantics

We represent the different checks on the constraint store imposed by a set of assertions as a set of assertion conditions as follows.

D e f i n i t i o n 8. Given a predicate represented by a

normal-ized atom Head, if the corresponding set of assertions is A = {Ai ... An}, with Ai = “ ; - pred Head : Prei => Posti. ” the set of assertion conditions for Head is {Co, C\,... , Cn}, with:

Ci = calls(liead, ._ Prej) i = 0

(4)

I f there are no assertions associated w i t h Head then the corresponding set of conditions is empty. The set of asser-t i o n condiasser-tions for a program is asser-the union of asser-the asserasser-tion conditions for each of the predicates i n the program. Also, given a single assertion Ai we define its corresponding set of assertion conditions as {C'o,Ci} (this w i l l be useful i n defining the status of an assertion).

The calls(_ffead,...) conditions encode the checks that the calls to the predicate represented by Head are w i t h i n those admissible by the set of assertions, and we thus call t h e m the calls assertion conditions. The conditions

success(Headi, Prei, Posti) encode the checks for

compli-ance of the successes for particular sets of calls, and we thus call t h e m the success assertion conditions.

E x a m p l e 4. The assertion conditions corresponding to the

predicate assertions for qsort in Example 1 are as follows:

calls(qsort(A, B), (list(A),list(B)))

success(qsort(A, B), list(A), (sorted(B), list(B))) success(qsort(A, B), list(B), (permutation(B, A), list(A)))

I n order to define the semantics of assertion conditions, we introduce the auxiliary partial functions prestep and step as follows:

prestep(La, D) = (6, a) = D[-i] = {L :: G \ 0) A 3o~ L = a(La) step(La, L)) = (#, a, o ) = JJ[-i] = (G | (/ ) A da L = a(La)

A 3« D[i] = (L :: G \ 9)

Given a derivation whose current state is a call to La (normalized atom), the prestep function returns the substi-t u substi-t i o n a for La, and the constraint store 6 at the predicate

call (i.e., just before the literal is reduced). Given a

deriva-t i o n whose currenderiva-t sderiva-taderiva-te corresponds exacderiva-tly deriva-to deriva-the rederiva-turn from a call to La, the step function returns the substitution a for La, the constraint store 6 at the call to La, and the constraint store 6' at La’s success (i.e., just after all literals introduced from the body of La have been fully reduced). Using these functions, the semantics of our calls and success assertion conditions are given by the following definition: D e f i n i t i o n 9. Given a calls or success assertion condition

C, the valuation of C on a derivation D, denoted solve(C, D) is defined as follows:

solve(calls(La, Pre), D) = (prestep(La, D) = (6, a)) => (6 ^P a(Pre))

solve(success(La, Pre, Post), D) = (step(La,D) = (6, a, 6')) => ((0 ^ P a(Pre))

=> (<?' =>P a (Post)))

where La is a normalized atom.

3.5 S t a t u s of Assertions and P a r t i a l Correctness

As mentioned before, the intended use of our assertions is to perform error detection and verification w i t h respect to partial correctness, i.e., to ensure that the program does not produce unexpected results for valid (“expected”) queries.3 Thus, we extend our notion of program to include assertions and valid queries.

D e f i n i t i o n 1 0 . An annotated program is a tuple (P, Q,A)

where P is a (higher-order) constraint logic program (as defined in Section 2), Q is a set of valid queries, and A is a set of assertions. As before, Ac denotes the set of calls and

success assertion conditions derived from A.

3 In practice, this set of expected queries is determined from module interfaces that define the set of exported predicates.

I n the context of annotated programs we extend derivations to operate on the set of valid queries as follows: derivs(Q) = U0 FQ derivs(Q). We now provide several simple definitions which w i l l be instrumental:

D e f i n i t i o n 1 1 . Given the set of queries Q, the assertion

condition C can be either checked or false, as follows:

checked(C) = V D G derivs(Q) . solve(C,D)

false(C) = 3D G derivs(Q) | ¬solve(C,D)

D e f i n i t i o n 12. In an annotated program (P,Q,A) an

as-sertion A G A is checked (false) if all (any) of the corre-sponding assertion conditions are checked (false).

D e f i n i t i o n 13. An annotated program (P, Q, A) is partially correct w.r.t. the set of assertions A and the set of queries

Q iffyA G A, A is checked for Q.

Note t h a t i t follows immediately t h a t a program is partially correct if all its assertion conditions are checked. The goal of assertion checking is thus to determine whether each as-sertion A is false or checked for Q. Again, for this i t is sufficient to prove the corresponding assertions conditions false or checked. There are two kinds of approaches to do-ing this (which can also be combined). While i t is i n gen-eral not possible to t r y all derivations stemming from Q, an alternative is to explore a hopefully representative set of them [21]. Though this does not allow fully validating the program i n general, it makes i t possible to detect many incorrectness problems. This approach is explored i n Sec-t i o n 3.6 i n Sec-the conSec-texSec-t of our higher-order derivaSec-tions. The second approach is to use global analysis techniques and is based on computing safe approximations of the program be-havior statically [4, 13]. The extension of this approach to higher-order assertions is beyond the scope of this paper.

3.6 O p e r a t i o n a l Semantics for H i g h e r - o r d e r P r o g r a m s w i t h F i r s t - o r d e r Assertions

We now provide an operational semantics which checks whether assertion conditions hold or not while computing the (possibly higher-order) derivations from a query. D e f i n i t i o n 14. Given the atom La and the set of assertion conditions Ac, Acz(La) denotes the set of labeled assertion condition instances for La of the form c#Ca, such that 3 C G Ac, C = calls(L, P re) (or C = success(L, P re, P ost)), a is a renaming s.t. L = a(La), Ca = calls(La,o~(P re)) (or Ca = success(La,o~(P re), . (P ost))), and c is an identifier that is unique for each Ca

In order to keep track of the violated assertion conditions,

we introduce an extended program state of the form {G |

6 | £), where £ denotes the set of identifiers f or falsified

assertion condition instances. For the sake of readability, we write labels i n negated form when they appear i n the error set. We also extend the set of literals w i t h syntactic objects of the form check(c) where c is an identifier f o r an assertion condition instance, which we call check literals. Thus, a literal is now a constraint, an atom, a higher-order literal, or a check literal.4

(5)

D e f i n i t i o n 1 5 . A state S = {L :: G | 9 \ £), where L is

a literal can be r e d u c e d to a state S , denoted S ~+A S , as follows:

1. If L is a constraint or L = X(ti,... ,tn), then S = (G \ 9 | £) where G and 9 are obtained in a same manner as in {L::G\0)~+ ( G " | 9')

2. If L is an atom and 3 ( L : - B ) G defn(L), then S = (B :: PostC :: G \ 9 \ £ ) where:

• PostC is the sequence check(ci) :: ... :: check(cn)

including all the checks check(ci) such that Ci#success(L, Prei, Posti) G AQ(L) A 9 =3*p Pre-i • and either £' = £ U { c } if 3 c#calls(L, Pre) G AQ(L)

s.t. 9 T ^ P Pre or £' = £ otherwise.

3. If L is a check literal check(c), then S = (G \ 9 \ £ } where either £' = £ U { c } if c#success(L,_,Post) G AQ(L) A 9 T ^ P Post or £' = £ otherwise.

Note that the order in which the P o s t C check literals are selected is irrelevant.

T h e set o f d e r i v a t i o n s f o r a p r o g r a m f r o m i t s set o f q u e r i e s Q u s i n g t h e s e m a n t i c s w i t h a s s e r t i o n s is d e n o t e d d e r i v S y i ( Q ) .

D e f i n i t i o n 1 6 . The set of e r r o r - e r a s e d derivations from

~-^A is obtained by a syntactic rewriting (—)° that removes states that begin by a check literal, check literals from goals, and the error set. It is recursively defined as follows:

\L>i,...,Dn} =\L>i,...,Dn}

re c \°

if Sm+i = (check(S) :: _ | _ | _} ( S i , . . . , Sm) || ( ( Sm +i ) J

otherwise [S\, • • • , Sm, <~>m + l j =

[G | O | t) = \G \ O)

(L :: G) = G° if L = check(S)

L :: (G°) otherwise

o I I

where \\ stands for sequence concatenation.

T h e o r e m 1 ( C o r r e c t n e s s a n d C o m p l e t e n e s s U n d e r A s s e r

-t i o n C h e c k i n g ) . For any anno-ta-ted program (P, Q, A), given V = derivs(Q) and V' = derivsj^(Q), it holds that V and V' are equivalent after filtering out check literals and error sets (formally defined as V = (D')° in Def. 16).

Proof. W e w i l l p r o v e T> = (T>')° b y s h o w i n g t h a t T> C (D')° a n d V D (V')°.

• ( C ) F o r a l l D G T> e x i s t s D G T> so t h a t D = ( D ) ° .

• ( 3 ) F o r a l l D' G V, D = ( D ' ) ° G V.

W e w i l l p r o v e each case:

• ( C ) L e t D = ( S i , . . . , Sn) , Si = {Li \ 9i), f o r s o m e Q = (Li,9i) G Q a n d Si ^ Si+i. P r o o f b y i n d u c t i o n o n t h e l e n g t h n o f D:

• Base case (n = 1 ) . L e t Si = (Li \ 9\ | 0 ) . I t h o l d s t h a t ( 5 i ) ° = ( L i | 9X | 0 ) ) ° = {LI | 0 i > = ( L i | 0i) = Si (since L\ does n o t c o n t a i n a n y check l i t e r a l ) . T h u s ,

• I n d u c t i v e case ( s h o w n + 1 a s s u m i n g n h o l d s ) . F o r each D2 = (Si,... , Sn, Sn+i) t h e r e e x i s t s D2 =

(S'i,..., S'm,S'rn+i) s u c h t h a t (D'2)° = D2. G i v e n t h e

i n d u c t i o n h y p o t h e s i s i t is e n o u g h t o s h o w t h a t f o r each Sn ^ Sn+i t h e r e e x i s t s Srn

^A S'rn+i, s u c h t h a t

(S'rn+i)° = Sn+i- A c c o r d i n g t o ~-^A (see D e f . 1 5 ) ,

L'rn+i a n d 9'm+i are o b t a i n e d i n t h e s a m e w a y t h a n i n ^ (see D e f . 2 ) , e x c e p t f o r t h e i n t r o d u c t i o n o f check l i t e r a l s . Since a l l check l i t e r a l s are r e m o v e d i n e r r o r -erased s t a t e s , i t f o l l o w s t h a t (S^+1)° = Sn+1. •

Q ) L e t D' = (S[,...,S'm), S!i = ( i - | Q[ \ Si), f o r s o m e

Q = (Li,6i) G Q a n d S'i ^A S'i+1. P r o o f b y i n d u c t i o n

o n t h e l e n g t h m o f D :

• Base case (m = 1 ) . I t h o l d s t h a t ( S ' i )0 = Si ( s h o w e d i n base case f o r C ) . T h e n ( D ' ) ° = D eV.

• I n d u c t i v e case ( s h o w m + 1 a s s u m i n g m h o l d s ) . W e w a n t t o s h o w t h a t g i v e n D'2 = (S[,... , S'rn, S'rn+1),

(D'2)° = D2 G V. G i v e n t h e i n d u c t i o n h y p o t h e s i s

i t is e n o u g h t o s h o w t h a t f o r each S'rn ~-^A S^+I t h e r e e x i s t s Sn ^ Sn+i s u c h t h a t Sn+i = ( S ^+i ) ° (so t h a t ( S i , . . . , Sn, Sn +i ) G V) o r Sn = (SU+i)" (D2 = D G V). A c c o r d i n g t o cases o f D e f . 15:

I f L'm b e g i n s w i t h a check l i t e r a l t h e n (L'm+1)° = (L'm)°. T h u s ( S ^+i ) ° = ( S ^ ) ° = Sn.

O t h e r w i s e , i t h o l d s t h a t ( S ^ + 1 )0 = Sn+i u s i n g t h e

s a m e r e a s o n i n g t h a n i n t h e i n d u c t i v e case f o r C .

T h i s r e s u l t i m p l i e s t h a t t h e s e m a n t i c s w i t h a s s e r t i o n s c a n also b e u s e d t o o b t a i n a l l answers t o t h e o r i g i n a l q u e r y . F u r -t h e r m o r e , -t h e f o l l o w i n g -t h e o r e m g u a r a n -t e e s -t h a -t w e c a n use t h e p r o p o s e d o p e r a t i o n a l s e m a n t i c s f o r a n n o t a t e d p r o g r a m s i n o r d e r t o d e t e c t ( a l l ) v i o l a t i o n s o f a s s e r t i o n s :

D e f i n i t i o n 1 7 . Let £(D) denote the error set of the last

state of derivation D, D[-i] = ( - | - | £). The run-time valuation of an assertion condition C on a derivation D is given by:

rtsolve(C,D) = \/c,C',a, L (c$=Cr G AQ(L) A<T(C) = C')

=^> £(D) Y- c

I.e., c o n d i t i o n r t s o l v e ( C , D) is v a l i d i f n o n e o f t h e p o s s i b l e i n s t a n c e s o f t h e a s s e r t i o n c o n d i t i o n C are i n t h e e r r o r set f o r d e r i v a t i o n D.

T h e o r e m 2 ( R u n - t i m e E r r o r D e t e c t i o n ) . For any

anno-tated program (P,Q,A), C G Ac is false iff 3 D G derivsj.(Q) s.t. ¬rtsolve(C,D).

Proof. L e t u s assume a s s e r t i o n c o n d i t i o n A G Ac is false <$ f r o m D e f . 12 a n d D e f . 8 3 { CC, CS} a s s e r t i o n c o n d i t i o n s s.t. f a l s e ( Cc) V f a l s e ( Cs) , w h e r e Cc = c a l l s ( L , Pre) a n d Cs =

success(L, Pre, Post) c o r r e s p o n d t o A. L e t u s f i r s t p r o v e t h e ¬ r t s o l v e ( Cc, D) case a n d t h e n t h e ¬ r t s o l v e ( Cs, D) o n e :

• fa I s 6 ( G r 1

<=> f r o m D e f . 1 1 3 D G d e r i v s ( Q ) s.t. s o l v e ( Cc, D)

<=> f r o m D e f . 9 ( p r e s t e p ( L , D) = (9, ¬ ) A 9 ^ P a (Pre)) <^ f r o m D e f . 15 3 S ~+A S w h e r e :

I I r\ I C\ ~1 T .- l # / S = {L :: G \ o \ t) s.t. d c#calls(!v, Pre) G AQ{L) S = ( _ | f | i ) A i = t U \c)

& f r o m D e f . 17 ¬ r t s o l v e ( Cc, D) • fals6(GQ )

<$ f r o m D e f . 1 1 3 D G d e r i v s ( Q ) s.t. ¬ s o l v e ( Cs, D)

(6)

-4=^ from Def. 15 d i ^ - ^ b —*_A O where

S = (L :: G \ 9 | _}A

3 c#success(L, Pre, Post) G _ 4 * ( L ) A 0 =^p Pre 5 = (check(c) :: G \ 9 \ £ ) A 9 y^p Post

o = \ _ | _ | c / A c = G U-[¯|

<^ from Def. 17 ¬Ttsolve(Cs, X)) r j

T h . 2 states t h a t assertion condition C is false iff there is a derivation D i n which the run-time valuation of the assertion condition of C i n D is false (i.e., if at least one instance of the assertion condition A is i n the error set for such derivation

D). Given a set of false assertion conditions we can easily

derive the set of false assertions using Def. 8. I n order to prove that any assertion is checked this has to be done for all possible derivations for all possible queries, which is often not possible i n practice. This is why analysis based on abstractions is often used i n practice for this purpose.

4 . H i g h e r - o r d e r A s s e r t i o n s o n H i g h e r - o r d e r D e r i v a t i o n s

Once we have established basic results for the case of first-order assertions i n the context of higher-first-order derivations, we extend the notion of assertion itself to the higher-order case. The motivation is that i n the higher-order context terms can be bound to predicates and our aim is to also be able to state and check properties of such predicates.

4.1 A n o n y m o u s Assertions

We start by generalizing the notion of assertion to include

anonymous assertions: assertions where the predicate

sym-bol is a variable from VS, which can be instantiated to any suitable predicate symbol from PS to produce non-anonymous assertions. A n non-anonymous assertion is an ex-pression of the from “:- pred L : Pre => Post”, where L is of the form X(Vi,... , Vn) and Pre and Post are D N F formulas of prop literals.

E x a m p l e 5. The anonymous assertion:

“:- pred X(A,B) : list(A) => list(B).” states that any predicate p£? that X is constrained to be of arity 2, it should be called with its first argument instantiated to a list, and if it succeeds, then its second argument should be also a list on success.

We now introduce predprops, which gather a number of anonymous assertions i n order to fully describe variables containing higher-order terms (predicate symbols), similarly to how prop literals describe conditions for variables contain-ing first-order terms.

D e f i n i t i o n 18. Given Prei and Posti conjunctions of prop

literals, a predprop pp(X) is an expression of the form:

pp(X){ :- pred X(Vi,...,Vm) : Prei => Posti.

:- pred X(Vi,...,Vm) : Pren => Postn. }

D e f i n i t i o n 19. The corresponding set of anonymous asser-t i o n condiasser-tions for asser-the predprop pp(X) is defined as

Ac[pp(X)] = {Ci[X] | i = 0..n} where:

The variable X can be instantiated to a particular predicate symbol q G PS to produce a set of non-anonymous assertion conditions Ac[pp(p)] for q.

E x a m p l e 6. Consider defining a comparator(Cmp) pred-prop that describes predicates of arity 3 which can be used

to compare numerical values:

comparator(Cmp) {

:- pred Cmp(X,Y,R) : int(X),int(Y) => between(-l,l,R). :- pred Cmp(X,Y,R) : flt(X),flt(Y) => between(-l,l,R). }.

The comparator(Cmp) predprop includes two anonymous

assertions describing a set of possible preconditions and postconditions for predicates of this kind. In this example:

A-c[comparator(Cmp)] = {

calls(Cmp(X,Y, Res), (int(X) A int(Y)) V (flt(X) A flt(Y))), success(Cmp(X, Y, Res),int(X) A int(Y), between( — 1, 1, Res)) success(Cmp(X, Y, Res),flt(X) A flt(Y),between(-1, 1, Res)) }

E x a m p l e 7. Fig. 1 provides a larger example. It is more

stylized for brevity, but it covers a good subset of the rele-vant cases, used later to illustrate the semantics. Lines 1-2 provide the definitions of two predprops, nneg/1 and neg/1

respectively. The former describes a unary predicate which should have its argument constrained to a non-negative in-teger on success (expressed by the nnegint/1 property in the assertion on line 3), independently of how the predicate is called (note the true keyword in the precondition part). Sim-ilarly, the latter describes a unary predicate which succeeds with its argument bound to a negative integer (hegint/1 property). Predicates z/1, p/1, n/1 and c/1 are used as arguments in queries to test_c/2 and test_s/2 to trigger the checking of the predprops. Whiles/1 and n/1 completely satisfy nneg/1 and neg/1 respectively, z/1 and c/1 satisfy neither one of these predprops.

Note that i t would still be possible to define nneg/1 or neg/1 without the higher-order assertions. For example, we could define them by considering the meaning of each predicate symbol i n our program. However, this approach has some serious limitations. First, we would need global reasoning over the whole program.5

D e f i n i t i o n 2 0 . The meaning of a predprop pp(X), denoted

\pp(X)\ is the set of constraints {X = q \ q G PS, V # C G Ac[pp(q)] : checked(C)}.

A predicate given by its predicate symbol p G PS is com-patible w i t h a predprop pp(X) if all the assertions resulting

from pp(p) are checked for all possible queries i n an anno-tated program.

4.2 O p e r a t i o n a l Semantics for H i g h e r - o r d e r P r o g r a m s w i t h H i g h e r - o r d e r Assertions

We now discuss several alternative operational semantics for higher-order programs w i t h higher-order assertions. I n all cases the aim of the semantics is to check whether assertions w i t h predprops hold or not during the computation of the derivations from a query.

calls(X(V1, . .

Ci[X] =

success(X(V1,

, Vm), Pre) i = 0

• • ,Vm), Prei, Posti) i = 1..n

(7)

1 2 3 4 5 6

8 9

10 11 12 13

:- prop nneg(P) { : :- prop neg(P) {:

:- pred test_c(P ,N) :- pred test_c(P,N) test_c(P ,N) :- P(N)

:- pred test_s(N ,P) :- pred test_s (N ,P) test_s ( 1,P) :- P = test_s (-1 ,P) :- P =

z(1) . z(-2) . p(1) .

- pred PCX) : - pred PCX) :

: nnegCP) => : negCP) =>

: nnegint(N) : negint(N) z . % bug here

n .

p C2) . n C-1) .

true => nnegint(X).} true => negint(X).}

true. true.

=> n n e g ( P ) . => n e g ( P ) .

, should be P = p

n C-2) . c Ca) . c Cb) .

F i g u r e 1 . Sample program w i t h predprops.

4.2.1 C h e c k i n g w i t h S t a t i c p r e d p r o p s

According to Definition 20, a predprop literal pp(X) denotes the subset of predicates for which all the associated asser-tions are checked. W h e n t h a t set of asserasser-tions can be stati-cally computed, then 9 =^p Cond can be used for b o t h prop and predprop Cond literals, and the operational semantics is identical to the one for the higher-order programs and regular assertions.

We w i l l denote as S -*HAs •$" a reduction from a state S to a state S' under the semantics for higher-order derivations i n programs w i t h assertions that may contain higher-order properties, which are statically precomputed. Thus, state reductions are performed as follows:

(G | u | c) —*A (G \u \ o )

(G\0\£) ^HAs (G' I 6" I £')

The meaning of each predprop, | p p ( X ) | , can be inferred or checked (if given by the user) by static analysis.

I n this semantics, given the program shown i n Fig. 1 and the goal test_c(z,-2), assertions are detected to be false since {P = z } (jL |neg(P) | and {P = z } (jL |nneg(P) |. 4.2.2 C h e c k i n g w i t h D y n a m i c p r e d p r o p s

Given the difficulty i n determining the meaning of | p p ( X ) | statically, we also propose a semantics w i t h dynamic check-ing. We start w i t h an over-approximation of each predprop | p p ( X ) | = {X = p | p G PS} and incrementally remove predicate symbols, as violations of assertion conditions are detected. :

• we can detect when some assertion condition instance is violated (Def. 15);

• we need a way to obtain a set of assertion condition instances from predprops (anonymous asserion condition instances);

We do t h a t by defining instantiations of anonymous asser-t i o n condiasser-tions for parasser-ticular predicaasser-te symbols and asser-the de-pendencies among those instances.

The following two definitions extend the notion of asser-t i o n condiasser-tion insasser-tances from Def. 14 asser-to asser-the case of anony-mous assertion conditions and higher-order literals:

D e f i n i t i o n 2 1 . Given a predprop pp(X) and a predicate

symbol p G PS, _4*[pp(p)] denotes the set of labeled

hypo-I

thetical assertion conditions of the form h#Cp, C[X]

p(Vi,

G Ac[pp(X)] (Def. 19), L = X(Vi tVn), Cp is defined as:

such that

V), Lp =

and h is an identifier that is unique for each Cv.

E x a m p l e 8. Consider the comparator(P) predprop from Ex. 6, where P is constrained to a predicate symbol less/3.

Then, the set of corresponding hypothetical assertion condi-tions is constructed as:

A # 11 M

-Q [comparator{iess)\ = {

ftl#ca//s(Zess(X,Y,R),(mipO A i n i ( Y ) ) V (flt(X) Aflt(Y))),

h2#success(less(X,Y,R),int(X) Aint(Y),between(—l, 1,R)) h3#success(less(X, Y, R), flt(X) A flt(Y),between(-l, 1, R)) }

This way we obtain “first-order” assertion conditions for

less/3 similar to the ones that would be obtained from user-provided assertions.

I n this semantics we allow the assertion condition i n -stances to be derived from the hypothetical assertion condi-tions i n the same way, as i n Def. 14. However, the violation of such an instance has to be treated i n a special way, as i t does not signal the violation of its conditions, but instead of the corresponding predprop. For simplicity, we also introduce a special label ho to denote the assertion conditions that ap-peared originally i n the program. The error set £ i n Def. 15 contained negated assertion condition instance identifiers. Now we extend this set w i t h assertion dependency rules of the form / \ ( \ / c) —• c. The following definitions provide the description of how such dependencies are generated.

D e f i n i t i o n 2 2 . The simplification of a literal L w.r.t. 9 is

defined as:

= L if L is a predprop

true if 6 =£>p L false if 6 T ^ P L

We extend this definition for a conjunction of literals.

D e f i n i t i o n 2 3 . Given the label c of an assertion condition

instance and a formula of the form Props = \/™_1 ( A " - o ProPij),

where Propij is either a prop or predprop literal, the

exten-sion of Ac and £ for dynamic predprop checking, denoted

as ext(Ac, c, Props) = ( _4c, f ) , is obtained as follows:

1. if simp(Props,d) = false, then Ac = 0 and £ = {c};

2. otherwise: Ac = ¯- i ^h> and

£ = ( A - — i ( V h F H ^ ~~^ ¯J* where:

AV

{h#C G Afj[Propij] | 0 < j < m(i),

Propij = PPij(Xij),ppij(Xij) is a predprop and Xij is bound to some q G PS}.

calls(Lp,Pre) if C [ X]

P= (T P P f) f C \Y]

calls(L, Pre)

success(L, Pre, Post)

Hi = {h | / i # _ G -4c}>

We w i l l denote as b -*+HAd <-> a reduction from a state & to a state S under the current semantics.

D e f i n i t i o n 24. A state S = {L :: G | 6 \ £), where L is a

literal can be reduced to a state S , denoted S ~+HAJ S , as follows:

(8)

2. If L is an atom and 3 ( L : - B ) G defn(L), then for each Ci#G'i G A%(L):

{

h if Ci is an instance of some

hjfcC G Ac

ho otherwise

(AiAc , A j £ ) =

PostCi = and simp(Prei,6) = true

otherwise

j ext(Ac, d, Pre) if d = calls(L, Pre)

J (0, 0) otherwise

{

check(ci) if C\ = success(L, Prei, Posti

true

and S = (B :: PostC :: G \ 0 \ £), where £ =

£ U Ute ^ M

u

U

A

^ , A'

c

= Ac U \J. AUc,

and PostC is the sequence PostC'i :: ... :: PostCn (simplifying true literals).

3. If L is a check literal check^c) and c#success(L , , Post) G At(L'), then S' = {G\6\ £') where:

(a) (AS, AAc) = ext(Ac,c,Post), (b) £' = £ U AS and A'c = Ac U AAc.

Note t h a t i n this semantics we support more t h a n one

calls assertion condition per predicate (as several predprops

may be applied t o the same predicate symbol). Also note that i n general we cannot prove w i t h dynamic checking that a predprop is true. So, as a safe approximation we treat preconditions i n such success assertion conditions as false. E x a m p l e 9. Let us trace finished derivations D , D and

D3 from the queries Qi = (test_c(n,X), true), Q2 =

(test_c(c,X), true) andQs = ((test_s(l,P),P(-2)), true),

respectively, to the program in Fig. 1.

In D1^] (see Tab. 1) we encounter two assertions for test_c/2 with a predprop in each precondition and trivial postconditions. According to state reduction rules, AAc con-sists of calls assertion condition instance c\ and two hypo-thetical assertion conditions hi and I12, derived from pred-props nneg/l and neg/l, and A£ = { c i —• / i o , / i i A/12 —• C i } . In D1[2] and current goal P(-l) (which is implicitly re-duced as n C - D ) , success assertion condition instances C2 and cs are derived from the hypotheses hi and /12, and A£ = {c2 —• hi,cs —y /12}. Consequently, two check lit-erals, check(2) and check(S) are added to the goal sequence. In states D1^ and D1^ those literals are reduced, which results in adding 02 to £ because nnegint(-l) property from the postcondition of C2 is violated. This example shows that the mechanism of dependencies between assertion conditions allows avoiding “false negative” results in assertion check-ing.

The derivation D2 is similar to D1 (see Tab. 2). The difference is in the D2[4] state, when it becomes possible to

infer £ h c\ and thus to conclude that c/1 £ \nneg(X)\ A

c/1 £ |ne<?(X)| and that both assertions for test_c/2 are false for this query.

In D3{1] (see Tab. 3) we encounter two assertions with a predprop in each postcondition. According to the state re-duction rules, AAc for this state consists of calls and suc-cess assertion condition instances, Co and c\, A£ = {co —•

/io,ci —• ho} for them. Also, a check literal check(ci) is

added to the goal sequence. After its reduction a hypothetical assertion condition /12, derived from the nneg(X) predprop which appears in c\, is added to Ac in D3[3], and £ is ex-tended with a dependency rule {/12 —> ci}. In state D3[4]

an assertion condition instance C2 is obtained from /12 and A£ = {c2 —> /i2j-. Finally, in D p] the error set contains the

following chain of dependency rules: £ D {02, C2 —• ^ 2 , ^2 —• ci,ci —• ho} and rule c\ —t ho allows us to detect and re-port the violation of the assertion condition c\ for predicate

test_s/2.

D e f i n i t i o n 25. An assertion condition C is t r i v i a l if it is of

the form calls(-, true) or success(-, _, true). It is also assumed that for any predprop pp(X) Ac[pp(X)] does not contain trivial assertion conditions.

T h e o r e m 3 (Higher-order Run-time Checking). For any

annotated program (P,Q,A), if 3 D G derivsHAd(Q) s.t.

¬rtsolve(C, D) =>• C G Ac is false.

Proof. I n this proof we reflect on the case when an

as-sertion condition is falsified because some of its pred-props are violated. To do so i t is enough t o show that at least one predprop was violated. Let us first prove the theorem for the case when the unsatisfied assertion condition is Cc = calls(L,pp(X)) and then for the case Ca = success(L, Pre,pp(X)), where pp(X) is a predprop. W i t h o u t loss of generality we assume that Ac[pp(X)] has cardinality 1 (which is the case when pp(X) consists of one anonymous assertion and one of the corresponding anony-mous assertion conditions is t r i v i a l ) .

• ¬rtsolve(Cc, D)

-£> From Def. 17: 3c' ,C'c,a, L (c'$=C'c G AQ(L)) A (a(Cc) = C"c) A (S(D) h c')

=>• From Def. 24 and £(D) h c' i t must hold that

D = (... , Si,... , S2, S3 ... , S4,...) where:

Si = (L :: _ I 0i I _) s.t. 3 L :-B G defn(L),

c'#ca\\s(L',a(pp(X))) G AQ(L),

01 ^(X=q),_qePS S2 = (L12 :: - I - I £2) s.t. {h —> c , c —> ho, } G £2,

h#CqeAc:{pp(q)},L2 = ? ( . . . )

03 = (_ I _ I 03) s . t . { c —V h} G 03, c #Oc G AQ{L2)

S4 = (_ I _ I £4) s.t. £4 h c

=^> From £3 h c" and T h . 2 we know that ¬checked(C") and thus (X = q) £ \pp(X)\ according t o Def. 20. =^> From Def. 6 i t follows that 03 ^>p pp(q)

=3> Given the state Si before the call t o L' and the state S3: (prestep(L, D) = (03, a)) A (0 j^p a(pp(X))) => From Def. 9 ¬solve(Cc, D)

=> From Def. 11 false(Cc) D

• —irtsolve(Cs, D)

¬> From Def. 17: 3c',C's,a, L (c'#C's G AQ(L)) A (a(Cs)=C's)A(£(D)^c')i

=^> From Def. 24 and £(D) h c i t must hold that

D = ( . . . , Si, S2, • • • , S3, S4, • • • , S5, Sa, • • • , Sr,. ..) where:

Si = (L' :: _ I 0i I _) s.t. 3 L':-B' G defn(L),

c'7^success(L',a(Pre),a(pp(X))) G AQ(L),

01 =^P a(Pre) 52 = (B :: check(c ) :: _ | _ | £2) s.t. { c —> ho} G £2 53 = (check(c') :: _ I _ I _)

54 = (_ I 04 I £4) s.t. 04 \= ( X = q),q G PS, {h —> c } G £4 , h#CqeA*[PP{q)].

SB = (La :: _ I _ I _) s.t. LB = <?(. • •)

Sa = (_ I _ I £(,) s.t. { c —y h} G £6where

Sr = (_ I 0r I £7)

(9)

G

test c(n,X)

P(-l)

check(c2), check(cs) check(cs) D

d P = N = X = Z =

-n

- 1

N

—1

S

c ¯ —>_ho

hi A¯,2 —> ci

c2 - » hi

c3 ->• h2

¯2

-(labeled instances + hypothetic Ac)

ci#calls(tesic(n, X),nneg(n) ( neg(n)) hi#success(n(Z),true,nnegint{Z))

/i2#success(n(iv), true, negint(Z)) C2#success(n(—1), true, nnegint(-1))

C3#success(n(—1),true,negint(—1))

-T a b l e 1 . A derivation of the query (test_c(n,X), true) to the program i n Fig. 1.

=>• From £7 h c" and T h . 2 we know t h a t ¬checked(C") and thus (X = q) £ \pp(X)\ according to Def. 20. =>• From Def. 6 it follows t h a t 67 ^>p pp(q)

=>• Given the state Si before the call to L and the state Sr: (step ( L,D) = (61, a,67)) A (61 =^p a ( P r e ) )

A (07 ji>p <i(ppX))) for c'#C"s e A%(L) => From Def. 9 ¬solve(Cs, D)

=> From Def. 11 false(Cs) n

5. Conclusions and Future W o r k

This paper contributes towards filling the gap between higher-order (C)LP programs and assertion-based exten­ sions for error detection and program verification. To this end we have defined a new class of properties, “predicate properties” (predprops i n short), and proposed a syntax and semantics for them. These new properties can be used i n assertions for higher-order predicates to describe the prop­ erties of the higher-order arguments. We have also discussed several operational semantics for performing run-time check­ ing of programs including predprops and provided correct­ ness results.

Our predprop properties specify conditions for predicates that are independent of the usage context. This corresponds i n functional programming to the notion of tight contract satisfaction [9], and i t contrasts w i t h alternative approaches such as loose contract satisfaction [11]. I n the latter, con­ tracts are attached to higher-order arguments by implicit function wrappers. The scope of checking is local to the func­ t i o n evaluation. A l t h o u g h this is a reasonable and pragmatic solution, we believe t h a t our approach is more general and more amenable for combination w i t h static verification tech­ niques. For example, avoiding wrappers allows us to remove checks (e.g., by static analysis) w i t h o u t altering the program semantics.6 Moreover, our approach can easily support loose contract satisfaction, since i t is straightforward i n our frame­ work to optionally include wrappers as special predprops.

We have included the proposed predprop extensions i n an experimental branch of the Ciao assertion language imple­ mentation. This has the immediate advantage, i n addition to the enhanced checking, t h a t it allows us to document higher-order programs i n much more accurate way. We have also implemented several prototypes for operational seman­ tics w i t h dynamic predprop checking (see Appendix A for

6 E.g. f(g)=g is not an identity function if wrappers are added to g on call. This complicates reasoning about the program, and may lead to unexpected and hard to detect differences in program semantics. Similar examples can be constructed where the presence of predprops in assertions would invalidate many reasonable program transformations.

a minimalistic implementation), which we plan to integrate into the already existing tools for assertion checking ( “ r u n ­ time verification”) of first-order assertions.

References

[1] H. A¨ıt-Kaci. A n Introduction to LIFE – Programming with Logic, Inheritance, Functions and Equations. In D. Miller, editor, Proceedings of the 1993 International Symposium on

Logic Programming, pages 52–68. M I T Press, 1993.

[2] C. Beierle, R. Kloos, and G. Meyer. A Pragmatic Type Concept for Prolog Supporting Polymorphism, Subtyping, and Meta-Programming. In Proc. of the ICLP’99 Workshop

on Verification of Logic Programs, Las Cruces, Electronic

Notes in Theoretical Computer Science, volume 30, issue 1. Elsevier, 2000. U R L http://www.elsevier.nl/locate/

entcs/volume30.html.

[3] J. Boye, W. Drabent, and J. Maluszyn´ski. Declarative Diagnosis of Constraint Programs: an Assertion-based Ap­ proach. In Proc. of the 3rd. Int’l Workshop on Automated

Debugging-AADEBUG’97, pages 123-141, Linkoping, Swe­

den, May 1997. U. of Linkoping Press.

[4] F. Bueno, P. Deransart, W. Drabent, G. Ferrand, M. Hermenegildo, J. Maluszynski, and G. Puebla. On the Role of Semantic Approximations in Validation and Diagno­ sis of Constraint Logic Programs. In Proc. of the 3rd. Int’l

WS on Automated Debugging-AADEBUG, pages 155-170.

U. Linkoping Press, May 1997.

[5] D. Cabeza. An Extensible, Global Analysis Friendly Logic

Programming System. PhD thesis, Universidad Politecnica

de Madrid (UPM), Facultad Informatica UPM, 28660-Boadilla del Monte, Madrid-Spain, August 2004.

[6] D. Cabeza, M. Hermenegildo, and J. Lipton. Hiord: A Type-Free Higher-Order Logic Programming Language with Predicate Abstraction. In Ninth Asian Computing Science

Conference (ASIAN’04), number 3321 in LNCS, pages

93-108. Springer-Verlag, December 2004. ISBN ISBN 3-540-24087-X.

[7] R. Cartwright and M. Fagan. Soft Typing. In PLDI’91, pages 278-292. SIGPLAN, ACM, 1991.

[8] W. Chen, M. Kifer, and D. Warren. HiLog: A Foundation for Higher Order Logic Programming. Journal of Logic

Programming, 15(3):187-230, 1993.

[9] C. Dimoulas and M. Felleisen. On Contract Satisfaction in a Higher-order World. ACM Trans. Program. Lang. Syst., 33 (5):16, 2011.

[10] W. Drabent, S. Nadjm-Tehrani, and J. Maluszyn´ski. The Use of Assertions in Algorithmic Debugging. In Proceedings

of the Intl. Conf. on Fifth Generation Computer Systems,

pages 573-581, 1988.

(10)

G

test c(c,X)

P(a)

check(c2), check(cs) check(cs)

D

Ad

P = N = X =

z

=

-c a N a

AS

ci —>_ho

h,2 A hi —y ci

c2 - » hi

c3 ->• h3

C2 C3

-A (labeled instances + hypothetic -Ac)

ci#calls(tesic(c,X),nneg(c) V neg(c)) h2#success(c(Z),true,nnegint(Z))

/i3#success(c(iv), true, negint(Z))

C2#success(c(a),true,nnegint(a))

C3#success(c(a), true, negint(a))

-T a b l e 2. A derivation of the query (test_c(c,X), true) to the program i n Fig. 1.

G

test s(1,P), P(-2) P = z, check(ci), P(-2) check(ci), P(-2) P(-2)

check(c2)

D

Ad

N = 1

P = z

-iv = —2

-A£ Co —>• Ao

ci —> h0

hi —> Cl

C2 —>• h,2

C~2

-A (labeled instances + hypothetic -Ac)

co#ca\\s(tests(l, P),nnegint(l) V negint(l)) ci#success(tests(l, P), nnegint(l), nneg(P))

/i2#success(«(iv), true, nnegint(Z))

C2#success(z(—2), true, nnegint(—2))

-T a b l e 3. A finished derivation of the query ((test_s(l,P),P(-2)), true) to the program i n Fig. 1.

[12] M . Hermenegildo, G. Puebla, and F. Bueno. Using Global Analysis, Partial Specifications, and an Extensible Assertion Language for Program Validation and Debugging. I n K. R. A p t , V . Marek, M . Truszczynski, and D. S. Warren, editors,

The Logic Programming Paradigm: a 25–Year Perspective,

pages 161–192. Springer-Verlag, July 1999.

[13] M . Hermenegildo, G. Puebla, F. Bueno, and P. L. Garc´ıa. Integrated Program Debugging, Verification, and Optimiza­ tion Using Abstract Interpretation (and The Ciao System Preprocessor). Science of Computer Programming, 58(1–2), 2005.

[14] M . V . Hermenegildo, F. Bueno, M . Carro, P. L´opez, E. Mera, J . Morales, and G . Puebla. A n Overview of Ciao and its Design Philosophy. Theory and Practice

of Logic Programming, 12(1–2):219–252, January 2012. .

http://arxiv.org/abs/1102.5497.

[15] P. Hill and J . Lloyd. The Goedel Programming Language. M I T Press, Cambridge M A , 1994.

[16] C. La¨ı. Assertions with Constraints for CLP Debugging. I n P. Deransart, M . V . Hermenegildo, and J . Maluszynski, ed­ itors, Analysis and Visualization Tools for Constraint Pro­

gramming, volume 1870 of Lecture Notes in Computer Sci­ ence, pages 109–120. Springer, 2000. ISBN 3-540-41137-2.

[17] L. Lamport and L. C. Paulson. Should your specification language be typed? ACM Transactions on Programming

Languages and Systems, 21(3):502–526, May 1999.

[18] G . T . Leavens, K. R. M . Leino, and P. Mu¨ller. Specification and Verification Challenges for Sequential Object-oriented Programs. Formal Asp. Comput., 19(2):159–189, 2007. [19] F. Logozzo et al. Clousot. http://msdn.microsoft.com/

en-us/devlabs/dd491992.aspx.

[20] E. Mera and J . Wielemaker. Porting and Refactoring Prolog Programs: the PROSYN Case Study. TPLP, 13(4-5-Online-Supplement), 2013.

[21] E. Mera, P. L´opez-Garc´ıa, and M . Hermenegildo. Integrating Software Testing and Run-Time Checking in an Assertion Verification Framework. I n 25th International Conference

on Logic Programming (ICLP’09), number 5649 i n LNCS,

pages 281–295. Springer-Verlag, July 2009.

[22] G . Nadathur and D. Miller. Higher-Order Logic Program­ ming. I n D. Gabbay, C. Hogger, and A . Robinson, editors,

Handbook of Logic in Artificial Intelligence and Logic Pro­ gramming, volume 5. Oxford University Press, 1998.

[23] L. Naish. Higher-order Logic Programming. Technical Report 96/2, Department of Computer Science, Univer­ sity of Melbourne, Melbourne, Australia, feb 1996. U R L :

http://www.cs.mu.oz.au/˜lee/papers/ho/.

[24] G . Puebla, F. Bueno, and M . Hermenegildo. A n Assertion Language for Debugging of Constraint Logic Programs. I n

Proceedings of the ILPS’97 Workshop on Tools and Environ­ ments for (Constraint) Logic Programming, October 1997.

Available from ftp://clip.dia.fi.upm.es/pub/papers/ assert lang tr discipldeliv.ps.gz as technical report CLIP2/97.1.

[25] G . Puebla, F. Bueno, and M . Hermenegildo. A n Assertion Language for Constraint Logic Programs. I n Analysis and

Visualization Tools for Constraint Programming, number

1870 in LNCS, pages 23–61. Springer-Verlag, 2000.

[26] G . Puebla, F. Bueno, and M . Hermenegildo. Combined Static and Dynamic Assertion-Based Debugging of Con­ straint Logic Programs. I n Logic-based Program Synthesis

and Transformation (LOPSTR’99), number 1817 in LNCS,

pages 273–292. Springer-Verlag, March 2000.

[27] Z. Somogyi, F. Henderson, and T . Conway. The Execution Algorithm of Mercury: an Efficient Purely Declarative Logic Programming Language. JLP, 29(1–3):17–64, October 1996. [28] T . Swift and D. S. Warren. XSB: Extending Prolog with

Tabled Logic Programming. TPLP, 12(1-2):157–187, 2012. [29] S. Tobin-Hochstadt and M . Felleisen. The Design and I m ­

(11)

A C M , 2008.

[30] D. Warren. Higher-order Extensions to Prolog: Are They Needed? I n J . Hayes, D. Michie, and Y . - H . Pao, editors,

Machine Intelligence 10, pages 441–454. Ellis Horwood L t d . ,

Chicester, England, 1982.

A . Minimalistic Sample Implementation

The following code (portable to most Prolog systems with minor changes) shows a minimalistic sample implementa-tion (as an interpreter intr/1) of the operational seman-tics for dynamic predprop checking (Def. 24). Conciseness and simplicity has been favoured over efficiency. We as-sume that clauses, assertion conditions, and predprops have been parsed and stored in cl/2, ac/1, pp/2 facts, respec-tively. The interpreter will throw an exception the first time that a failed program assertion is detected (see ext/2 pred-icate). E.g., intr((test s(1,P),P(1))) is a valid query while intr((test s(1,P),P(-2))) throws a failed asser-tion excepasser-tion. Predicate reset/0 must be called between intr/1 queries to reset error status and temporary data. I n the handler errors can be gathered (as in the semantics) or execution aborted.

:- module(_,[reset/0,intr/1],[hiord,dcg dynamic_clauses]). :- use_module(library(aggregates)).

% % % Sample program data and properties

% negint/1 and nnegint/1 properties eval_prop(negint(X)) :- integer(X), X < 0. eval_prop(nnegint(X)) :- integer(X), X >= 0.

% predprops nneg/1 and neg/1

pp(nneg(P),ac(P(X), nneg_c1(P)#success(true,nnegint(X)))). pp(neg(P), ac(P(X), neg_c1(P)#success(true, negint(X)))).

% assertion conditions and clauses for test_s/2 ac(test_s(N,_P), c1#calls((nnegint(N);negint(N)))). ac(test_s(N,P), c2#success(nnegint(N), nneg(P))). ac(test_s(N,P), c3#success(negint(N), neg(P))). cl(test_s( 1,P), P = z ) .

cl(test_s(-1,P), P = n ) .

% clauses for z/1, n/1

cl(z(1), true). cl(z(-2), true). cl(n(-1), true). cl(n(-2), true).

% % % Intepreter

:- dynamic hyp_ac/2. % hypothetical assertion condition :- dynamic negac/1. % (negated) assertion dependency rule

% Reset errors and hypothetical assertion conditions reset :- retractall(hyp_ac(_, _ ) ) ,

( retract((negac(_) :- _ ) ) , fail ; true ) .

% Interpreter with higher-order assertion checking intr(X) :- ctog(X, X 1 ) , !, intr(X1).

intr(X) :- is_blt(X), !, X. intr((A,B)) :- !, intr(A), intr(B).

intr((A ; B)) :- !, ( intr(A) ; intr(B) ) . intr(A)

:-get_acs(A, Acs),

pre(Acs, Ids, []), cl(A, Body), intr(Body), post(Ids, Acs).

% Built-ins

is_blt(true). is_blt(fail). is_blt(_ = _ ) .

% From call(N,...) to N(...),where N is a predicate symbol ctog(X, _) :- var(X), !, throw(inst_error).

ctog(X, X1)

:-X =.. [call,N|Args], ( atom(N) -> true ; X1 =.. [N|Args].

throw(inst_error) ) ,

% Get assertion conditions for the given literal A get_acs(A, Acs) :- ( bagof(Ac, get_ac(A, A c ) , Acs)

-> true ; Acs = [ ] ) . get_ac(A, Ac) :- ( ac(A, Ac) ; hyp_ac(A, Ac) ) .

pre([]) --> [ ] .

pre([Ac|Acs]) --> pre_(Ac), pre(Acs). pre_(Id#calls(Pre)) --> { ext(Pre, Id) }.

pre_(Id#success(Pre, _)) --> ( { simp0(Pre, true) } -> [Id] ; [] ) .

post([], _Acs) post([Id|Ids], post_(Id, Acs)

Id == Id0, post_(_, _ ) .

Acs) :- post_(Id, Acs), post(Ids, Acs). :- member(Id0#success(_Pre,Post), Acs), !, ext(Post, I d ) .

% Check/extend assertion conditions ext(Props, Id)

:-simp(Props, Props2), ext_(Props2, I d ) , ( negac(A), atom(A)

-> throw(failed_assertion(A)) ; true ) . ext_(true, _Id) :- !.

ext_(false, Id) :- !, assertz((negac(Id) :- true)). ext_(Props, Id) :- acsubs(Props, Props2),

assertz((negac(Id) :- Props2)).

% Add assertion dependency rules acsubs((A,B), (A2;B2)) :- !,

acsubs(A, A2), acsubs(B, B 2 ) . acsubs((A ; B ) , (A2 , B2)) :- !,

acsubs(A, A2), acsubs(B, B 2 ) . acsubs(ac(L, Id#Ac), negac(Id))

:-ctog(L, L2), assertz(hyp_ac(L2, Id#Ac)).

% Condition simplification simp(true, R) :- !, R = true. simp((X;Y), R) :- !,

simp(X, Rx), simp(Y simp((X,Y), R) :- !,

simp(X, Rx), simp(Y simp(X, R) :- pp(X, Ac) simp(X, R) :- eval_prop(X) simp(_, R) :- R = false.

R y ) , or(Rx, Ry, R ) .

R y ) , and(Rx, Ry, R ) . !, R = Ac.

!, R = true.

and(Rx, Ry, R ) . R = true.

% Condition simplification for success preconditions simp0(true, R) :- !, R = true.

simp0((X,Y), R ) :- !,

simp0(X, R x ) , simp0(Y, R y ) , simp0(X, R) :- eval_prop(X), !, simp0(_, R) :- R = false.

or(true, _, R) :- !, R = true. or(false, X, R ) :- !, R = X. or(_, true, R) :- !, R = true. or(X, false, R) :- !, R = X. or(X, Y, (X;Y)).

and(false, _, R) : and(true, X, R ) :-and(_, false, R) : and(X, true, R) :-and(X, Y, (X,Y)).

Referencias

Documento similar