The assertions package adds a number of new declaration definitions and new operator definitions which allow including program assertions in user programs. Such assertions can be used to describe predicates, properties, modules, applications, etc. These descriptions can contain formal specifications (such as sets of preconditions, post-conditions, or descriptions of computations) as well as machine-readable textual comments.
This module is part of the assertions library. It defines the basic code-related assertions, i.e., those intended to be used mainly by compilation-related tools, such as the static analyzer or the run-time test generator.
Here we document mainly the use of assertions for providing specifications for predicates and other program elements, as well as the locations within assertions where machine-readable documentation strings can be placed. The commands that can be used in the documentation strings and other directives that can be used to provide additional machine-readable comments are described in the autodocumenter (lpdoc [Knu84,Her99]) manual.
There are two kinds of assertions: predicate assertions and program point assertions. Predicate assertions are placed as directives in the source code, i.e., preceded by ``:-''. Program point assertions are placed as literals in clause bodies. Additional documentation on the syntax and fields of predicate assertions can be found in the Types and properties related to assertions module.
This documentation is intended to provide information at a ``reference manual'' level. For more tutorial introductions to the assertion language and more examples please see [BCHP96,HPB99,PBH00,HPBLG05,HBC12] and the ciaopp tutorial. % The assertion language as implemented in this library essentially follows these documents, although, due to its evolution, it may differ in some details. The purpose of this manual is to document precisely what the implementation of the library supports at any given point in time.
There can be more than one of these assertions per predicate, in which case each one represents a possible ``mode'' of use (usage) of the predicate. The exact scope of the usage is defined by the properties given for calls in the body of each assertion (which should thus distinguish the different usages intended). Predicates are typically specified using a set of pred assertions, so that together they cover all ways in which the predicate is intended be used (all usages). Each pred assertion is translated internally to a calls assertion covering all the calling modes and a success assertion covering the successes for those calls.
For example, the following assertions would describe all intended modes (and the only modes) of use of a predicate length/2 (see lists):
:- pred length(L,N) : list * var => list * integer # "Computes the length of @var{L}.". :- pred length(L,N) : var * integer => list * integer # "Outputs @var{L} of length @var{N}.". :- pred length(L,N) : list * integer => list * integer # "Checks that @var{L} is of length @var{N}.".
Usage::- pred(AssertionBody).
For example, the following assertion:
:- pred length(L,N) : list * var => list * integer.is equivalent to:
:- check pred length(L,N) : list * var => list * integer.
Usage::- pred(AssertionStatus,AssertionBody).
For example, the following assertion describes all possible calls to predicate is/2 (see arithmetic):
:- calls is(term,arithexpression).
Usage::- calls(AssertionBody).
Usage::- calls(AssertionStatus,AssertionBody).
For example, the following assertion specifies the answers of the length/2 predicate if it is called as in the first mode of usage above (note that the previous pred assertion already conveys such information, however it also restricts the set of admissible calls, while the success assertion does not):
:- success length(L,N) : list * var => list * integer.
Usage::- success(AssertionBody).
Usage::- success(AssertionStatus,AssertionBody).
For example, the following assertion specifies that the computation of append/3 (see lists) will not fail if it is called as described (but does not force the predicate to be called only that way):
:- comp append(Xs,Ys,Zs) : var * var * var + not_fail.
Usage::- comp(AssertionBody).
Usage::- comp(AssertionStatus,AssertionBody).
Properties are standard predicates, but which are guaranteed to terminate for any possible instantiation state of their argument(s), do not perform side-effects which may interfere with the program behaviour, and do not further instantiate their arguments or add new constraints.
Provided the above holds, properties can thus be safely used as run-time checks. The program transformation used in ciaopp for run-time checking guarantees the third requirement. It also performs some basic checks on properties which in most cases are enough for the second requirement. However, it is the user's responsibility to guarantee termination of the properties defined. (See also Declaring regular types for some considerations applicable to writing properties.)
The set of properties is thus a strict subset of the set of predicates. Note that properties, in addition to being used to describe characteristics of arguments in assertions, they can also be executed (called) as any other predicates.
Usage::- prop(AssertionBody).
Usage::- prop(AssertionStatus,AssertionBody).
:- test length(L,N) : ( L = [1,2,5,2] ) => ( N = 4 ).
Usage::- test(AssertionBody).
Usage::- test(AssertionStatus,AssertionBody).
Usage::- texec(AssertionBody).
Usage::- texec(AssertionStatus,AssertionBody).
These assertions are trusted by the compiler, i.e., they are similar to writing a trust calls assertion (except for referring only to the external calls). As a result, if they are erroneous they can introduce bugs in programs. Thus, entry assertions should be written with care.
An important use of these assertions is in providing information to the compiler which it may not be able to infer from the program. The main use is in providing information on the ways in which exported predicates of a module will be called from outside the module. This will greatly improve the precision of the analyzer, which otherwise has to assume that the arguments that exported predicates receive are any arbitrary term.
The distinction between external and internal calls is not always relevant and in those cases the use of trust calls assertions is preferred. Because of this, entry assertions may be deprecated in the future, since the distinction between external and internal calls can also be achieved by means of a bridge predicate.
Usage::- entry(AssertionBody).
:- exit length(L,N) : list * var => list * integer.
These assertions are trusted by the compiler, i.e., they are similar to writing a trust success assertion (except for referring only to the external calls). As a result, if they are erroneous they can introduce bugs in programs. Thus, exit assertions should be written with care.
The distinction between external and internal calls is not always relevant and in those cases the use of trust success assertions is preferred. Because of this, entry assertions may be deprecated in the future, since the distinction between external and internal calls can also be achieved by means of a bridge predicate.
Usage::- exit(AssertionBody).
Usage::- exit(AssertionStatus,AssertionBody).
The syntax of mode definitions is similar to that of pred declarations. For example, the following set of assertions:
:- modedef +A : nonvar(A) # "@var{A} is bound upon predicate entry.". :- pred p(+A,B) : integer(A) => ground(B).
is equivalent to:
:- pred p(A,B) : (nonvar(A),integer(A)) => ground(B) # "@var{A} is bound upon predicate entry.".
Usage::- modedef(AssertionBody).
Usage::- decl(AssertionBody).
Usage::- decl(AssertionStatus,AssertionBody).
Usage::- doc(Pred,Comment).
Documentation . This assertion provides a text Comment for a given predicate Pred, as well as other directives for the documenter.
Usage::- comment(Pred,Comment).
An alias for doc/2 (deprecated, for compatibility with older versions).
Usage:check(PropertyConjunction)
This assertion provides information on a clause program point (position in the body of a clause). Calls to a check/1 assertion can appear in the body of a clause in any place where a literal can normally appear. The property defined by PropertyConjunction should hold in all the run-time stores corresponding to that program point. See also Run-time checking of assertions.
Usage:trust(PropertyConjunction)
This assertion also provides information on a clause program point. It is identical syntactically to a check/1 assertion. However, the properties stated are not taken as something to be checked but are instead trusted by the compiler. While the compiler may in some cases detect an inconsistency between a trust/1 assertion and the program, in all other cases the information given in the assertion will be taken to be true. As a result, if these assertions are erroneous they can introduce bugs in programs. Thus, trust/1 assertions should be written with care.
An important use of these assertions is in providing information to the compiler which it may not be able to infer from the program (either because the information is not present or because the analyzer being used is not precise enough). In particular, providing information on external predicates which may not be accessible at the time of compiling the module can greatly improve the precision of the analyzer. This can be easily done with trust assertion.
Usage:true(PropertyConjunction)
This assertion is identical syntactically to a check/1 assertion. However, the properties stated have been proved to hold by the analyzer. Thus, these assertions often represent the analyzer output.
Usage:false(PropertyConjunction)
This assertion is identical syntactically to a check/1 assertion. However, the properties stated have been proved not to hold by the analyzer. Thus, these assertions often represent the analyzer output.