ion The term \lambda τ1 x1, . . . , τn xn ; t denotes the n-ary logic function which maps x1, . . . , xn to t. It has the same precedence as \forall and \exists In this latter case, note that the two ’>’ must be separated by a space, to avoid confusion with the shift operator. ANSI/ISO C Specification Language CAT RNTL project 2.6 Logic specifications 39 term ::= \lambda binders ; term abstraction | extended-quantifier ( term , term , term ) extended-quantifier ::= \max | \min | \sum | \product | \numof Figure 2.12: Grammar for higher-order constructs Extended quantifiers Terms \quant(t1, t2, t3) where quant is max min sum product or numof are extended quantifications. t1 and t2 must have type integer, and t3 must be a unary function with an integer argument, and a numeric value (integer or real) except for \numof for which it should have a boolean value. Their meanings are given as follows: \max(i, j, f) = max{f(i), f(i+ 1), . . . , f(j)} \min(i, j, f) = min{f(i), f(i+ 1), . . . , f(j)} \sum(i, j, f) = f(i) + f(i+ 1) + · · ·+ f(j) \product(i, j, f) = f(i)× f(i+ 1)× · · · × f(j) \numof(i, j, f) = #{k | i ≤ k ≤ j && f(k)} = \sum(i, j,\lambda integer k; f(k)?1 : 0) If i > j then \sum and \numof above are 0, \product is 1, and \max and \min are unspecified (see Section 2.2.2). Example 2.28 Function that sums the element of an array of doubles. /*@ requires n ≥ 0 ∧ \valid(t+(0..n−1)) ; @ ensures \result ≡ \sum(0,n−1,\lambda int k; t[k]); @*/ double array_sum(double t[],int n) { int i; double s = 0.0; /*@ loop invariant 0 ≤ i ≤ n; @ loop invariant s ≡ \sum(0,i−1,\lambda int k; t[k]); @ loop variant n−i; */ for(i=0; i = Nil | Cons(A,list ); ANSI/ISO C Specification Language CAT RNTL project 40 Specification language logic-type-decl ::= type logic-type = logic-type-def ; logic-type-def ::= record-type | sum-type | type-expr type abbreviation record-type ::= { type-expr id (; type-expr id)∗ ;? } sum-type ::= | constructor (| constructor)∗ constructor ::= id constant constructor | id ( type-expr (, type-expr)∗ ) non-constant constructor type-expr ::= ( type-expr (, type-expr)+ ) product type term ::= term . id record field access | \match term { match-cases } pattern-matching | ( term (, term)+ ) tuples | { (. id = term ;)+ } records | \let ( id (, id)+ ) = term ; term match-cases ::= match-case match-case ::= case pat : term pat ::= id constant constructor | id ( pat (, pat)∗ ) non-constant constructor | pat | pat or pattern | _ any pattern | cst numeric constant | { (. id = pat)∗ } record pattern | ( pat (, pat)∗ ) tuple pattern | pat as id pattern binding Figure 2.13: Grammar for concrete logic types and pattern-matching introduces a concrete definition of finite lists. The logic definition /*@ logic integer list_length (list l) = @ \match l { @ case Nil : 0 @ case Cons(h,t) : 1+list_length(t) @ }; @*/ defines the length of a list by recursion and pattern-matching. 2.6.5 Hybrid functions and predicates Logic functions and predicates may take both (pure) C types and logic types arguments. Such an hybrid predicate (or function) can either be defined with the same syntax as before, or simply declared, but in the latter case the declaration should usually be augmented with a reads clause, with the syntax given in Figure 2.14, which extends the one of Figure 2.11. This feature is useful when a function or a predicate is not easily definable, but can be more easily axiomatized. ANSI/ISO C Specification Language CAT RNTL project 2.6 Logic specifications 41 logic-function-decl ::= logic type-expr poly-id parameters reads-clause ; logic-predicate-decl ::= predicate poly-id parameters? reads-clause ; reads-clause ::= reads locations logic-function-def ::= logic type-expr poly-id parameters reads-clause = term ; logic-predicate-def ::= predicate poly-id parameters? reads-clause = pred ; poly-id ::= id normal identifier | id type-var-binders identifier for polymorphic object | id label-binders normal identifier with labels | id label-binders type-var-binders polymorphic identifier with labels label-binders ::= { id (, id)∗ } Figure 2.14: Grammar for logic declarations with reads clauses Be it defined either directly by an expression or through a set of axioms, an hybrid function (or predicate) usually depends on one or more program points, because it depends upon memory states, via expressions such as: • pointer dereferencing: *p, p->f; • array access: t[i]; • address-of operator: x • built-in predicate depending on memory: \valid To make such a definition safe, it is mandatory to add after the declared identifier a set of labels, between curly braces. Expressions as above must then be enclosed into the \at construct to refer to a given label. However, to ease reading of such logic expressions, it is allowed to omit a label whenever there is only one label in the context. Example 2.30 The following annotations declare a function which returns the number of occurrences of a given double in an array of doubles between the given indexes, together with the related axioms. It should be noted that without the reads clauses, this axiomatization would be inconsistent, since the function would not depend on the values stored in t, hence the two last axioms would say both that a = b+ 1 and a = b for some a and b. /* nb_occ(t,i,j,e) gives the number of occurrences of e in t[i..j] * (in a given memory state labelled L) */ /*@ logic integer nb_occ{L}(double t[], integer i, integer j, @ double e) ANSI/ISO C Specification Language CAT RNTL project 42 Specification language @ reads t[..]; @*/ /* Notice that without label {L}, t[..] would be rejected. * With {L}, it is indeed a shortcut for \at (t[..],L). */ /*@ axiom nb_occ_empty{L} : @ ∀ double t[], integer i, integer j, double e; @ i > j =⇒ nb_occ(t,i,j,e) ≡ 0; @*/ // without {L}, term nb_occ(t,i,j,e) would be rejected /*@ axiom nb_occ_true{L} : @ ∀ double t[], integer i, integer j, double e; @ i ≤ j ∧ t[i] ≡ e =⇒ @ nb_occ(t,i,j,e) ≡ nb_occ(t,i,j−1,e) + 1; @*/ // without {L}, term ti would be rejected, here it is \at (ti,L) /*@ axiom nb_occ_false{L} : @ ∀ double t[], integer i, integer j, double e; @ i ≤ j ∧ t[i] 6≡ e =⇒ @ nb_occ(t,i,j,e) ≡ nb_occ(t,i,j−1,e); @*/ Example 2.31 This second example defines a predicate which indicates whether two arrays of the same size are a permutation of each other. It illustrates the use of more than a single label. Thus, the \at operator is mandatory here. Indeed the two arrays may come from two distinct memory states. Typically, one of the post condition of a sorting function would be permut{Pre,Here}(t,t). /* permut{L1,L2}(t1,t2,n) is true whenever t1[0..n−1] in state L1 * is a permutation of t2[0..n−1] in state L2 */ /*@ predicate permut{L1,L2}(double t1[], double t2[], integer n) @ reads \at(t1[..],L1), \at(t2[..],L2); @*/ /*@ axiom permut_refl{L} : @ ∀ double t[], integer n; permut{L,L}(t,t,n); @*/ /*@ axiom permut_sym{L1,L2} : @ ∀ double t1[], double t2[], integer n; @ permut{L1,L2}(t1,t2,n) =⇒ permut{L2,L1}(t2,t1,n) ; @*/ ANSI/ISO C Specification Language CAT RNTL project 2.6 Logic specifications 43 /*@ axiom permut_trans{L1,L2,L3} : @ ∀ double t1[], double t2[], double t3[], integer n; @ permut{L1,L2}(t1,t2,n) ∧ permut{L2,L3}(t2,t3,n) @ =⇒ permut{L1,L3}(t1,t3,n) ; @*/ /*@ axiom permut_exchange{L1,L2} : @ ∀ double t1[], double t2[], integer i, integer j, integer n; @ \at(t1[i],L1) ≡ \at(t2[j],L2) ∧ @ \at(t1[j],L1) ≡ \at(t2[i],L2) ∧ @ (∀ integer k; 0 ≤ k = Nil | Cons(A , list ); @ @ logic integer length (list l) = @ \match l { @ case Nil : 0 @ case Cons(h,t) : 1+length(t) } ; @ @ logic A fold_right ((A −> B −> B) f, list l, B acc) = @ \match l { @ case Nil : acc @ case Cons(h,t) : f(h,fold(f,t,acc)) } ; @ @ logic list filter ((A −> boolean) f, list l) = @ fold_right((\lambda A x, list acc; @ f(x) ? Cons(x,acc) : acc), Nil) ; @ @ } @*/ Module components are then accessible using a qualified notation like List::length . Predefined algebraic specifications can be provided as libraries (see section 3), and imported using a construct like