Version 1.2 of ai05s/ai05-0145-2.txt
!standard 11.5 (00) 09-11-02 AI05-0145-2/02
!standard 11.4.1 (10)
!class amendment 09-06-12
!status work item 09-06-12
!status received 09-06-12
!priority Medium
!difficulty Medium
!subject Pre- and Postconditions
!summary
To augment the basic "assert" pragma capability in Ada 2005, we propose
to allow the specification of pre/postconditions for subprograms
and entries.
We presume the use of the "aspect_specification" syntax defined in
AI05-0183-1.
!problem
A number of programming paradigms include the heavy use of pre/post
conditions on subprograms. Having a basic standardized "Assert" pragma
supports this approach to some degree, but generally requires that the
Assert pragmas actually be inserted inside the bodies of the associated
code. It would be much more appropriate if these pre/post conditions
appeared on the specification of the subprograms rather than buried in the
body, as they really represents part of the contract. In particular, one
wants any implementation of a given operation to conform to the
pre/post conditions specified, in the same way one wants any implementation
of a package to conform to the constraints of the parameter and result
subtypes of the visible subprograms of the package.
Effectively, pre/post conditions may be considered as generalized,
user-definable constraints. They have the same power to define more
accurately the "contract" between the user and implementor of a package,
and to thereby catch errors in usage or implementation earlier in the
development cycle. They also can provide valuable documentation of the
intended semantics of an abstraction.
!proposal
Using the aspect_specification syntax, we propose to allow the
specification of pre/postconditions for subprograms and entries.
The aspects Pre and Post are used for this purpose. In addition, for
primitive subprograms of tagged types, the aspects Pre'Class and
Post'Class are also available. These aspects are inherited by the
corresponding primitive subprogram of types descended from the original
tagged type. When multiple preconditions apply to a subprogram, they are
"or"ed together (that is, if any of the preconditions is satisfied, the
overall precondition is satisfied). When multiple postconditions apply
to a subprogram, they are "and"ed together (that is, all of the
postconditions must be satisfied for the overall postcondition is
satisfied).
!wording
Modify 3.9.3(1.1/2):
abstract_subprogram_declaration :=
subprogram_specification is abstract
[aspect_specification];
Modify 6.1(2/2):
subprogram_declaration ::=
[overriding_indicator]
subprogram_specification
[aspect_specification]
Modify 6.7(2/2):
null_procedure_declaration ::=
procedure_specification is null
[aspect_specification];
Modify 9.5.2(2/2):
entry_declaration ::=
[overriding_indicator]
entry defining_identifier [(discrete_subtype_definition)] parameter_profile
[aspect_specification];
Add a new section:
13.3.2 Preconditions and Postconditions
Preconditions and postconditions may be specified for subprograms and
entries, using an aspect_specification for aspect Pre, Pre'Class,
Post, or Post'Class. The expression associated with a Pre or Pre'Class
aspect is called a precondition expression, and the expression
associated with a Post or Post'Class aspect is called a *postcondition
expression.*
Name Resolution
The expected type for a precondition or postcondition expression is
the predefined type Boolean. Within a postcondition expression of a
function, the attribute Result is defined for the function, yielding
the value returned by the function. Within a postcondition expression
of a subprogram or entry with at least one IN OUT formal parameter of a
nonlimited type, the attribute Old is defined for each such formal
parameter, yielding the value of the formal parameter at the beginning
of the execution of the subprogram or entry.
Within the expression for a Pre'Class or Post'Class aspect for a
primitive subprogram of a tagged type T, a name that denotes a formal
parameter of type T is converted to T'Class. Similarly, a name that
denotes a formal access parameter of type access-to-T is converted to
access-to-T'Class. Finally, if the subprogram is a function returning
T or access T, the Result attribute is converted to T'Class or
access-to-T'Class, respectively. [Redundant: This ensures the
expression is well-defined for a primitive subprogram of a type
descended from T.]
Legality Rules
The Pre or Post aspect shall not be specified for an abstract
subprogram. [Redundant: Only the Pre'Class and Post'Class aspects may
be specified for such a subprogram.]
Static Semantics
If a Pre'Class or Post'Class aspect is specified for a primitive
subprogram of a tagged type T, then the associated precondition
expression also applies to the corresponding primitive subprogram of
each descendant of T.
Dynamic Semantics
If one or more precondition expressions apply to a subprogram or
entry, and the Assertion_Policy in effect at the point of the
subprogram or entry declaration is Check, then upon a call of the
subprogram or entry, after evaluating any actual parameters, a
precondition check is performed. This consists of the evaluation of
the precondition expressions that apply to the subprogram or entry. If
and only if all the precondition expressions evaluate to False, then
Ada.Assertions.Assertion_Error is raised. The order of performing the
checks is not specified, and if any of the precondition expressions
evaluate to True, it is not specified whether the other precondition
expressions are checked. It is not specified whether any check for
elaboration of the subprogram body is performed before or after the
precondition check. It is not specified whether in a call on a
protected operation, the check is performed before or after starting
the protected action. For an entry call, the check is performed prior
to checking whether the entry is open.
If one or more postcondition expressions apply to a subprogram or
entry, and the Assertion_Policy in effect at the point of the
subprogram or entry declaration is Check, then upon successful return
from a call of the subprogram or entry, prior to copying back any
by-copy IN OUT or OUT parameters, a postcondition check is performed.
This consists of the evaluation of the postcondition expressions that
apply to the subprogram or entry. If any of the the postcondition
expressions evaluate to False, then Ada.Assertions.Assertion_Error is
raised. The order of performing the checks is not specified, and if
one of them evaluates to False, it is not specified whether the others
are checked. It is not specified whether any constraint checks
associated with copying back IN OUT or OUT parameters are performed
before or after the postcondition check.
If a precondition or postcondition check fails, the exception is
raised at the point of the call. [Redundant: The exception cannot
be handled inside the called subprogram.]
For a dispatching call or a call via an access-to-subprogram value,
the precondition or postcondition check performed is determined by the
subprogram actually invoked. [Redundant: Note that for a dispatching
call, if there is a Pre'Class aspect that applies to the subprogram
named in the call, then if the precondition expression for that aspect
evaluates to True, the precondition check for the call will succeed.
[Redundant: If the Assertion_Policy in effect at the point of a
subprogram or entry declaration is Ignore, then no precondition or
postcondition check is performed on a call on that subprogram or entry.]
!discussion
This is based on AI05-145-1. The Pre/Post aspects are specified using
the aspect_specification syntax defined in AI05-0183-1. There is no
message associated with the failure of a precondition or postcondition
check: it was deemed that these annotations are intended for
verification, and that for debugging purposes the Assert pragma is
sufficient.
The new version preserves the flexibility of the previous one concerning
the point at which the aspect is checked: either callee or caller can
perform the check, except for indirect calls, where a wrapper will be
required if the caller normally performs the checks. Note however that
the callee cannot handle the Assertion_Error, as there seems no reason
to allow that degree of implementation dependence.
These aspects can be specified for protected operations and for
protected and task entries. It appears necessary that preconditions on
entries be evaluated by the caller, because their failure should take
place before the caller is suspended waiting on the entry queue. In the
case of an entry that implements a primitive subprogram, the
preconditions would have to be checked by the "wrapper" prior to calling
the entry. We allow the precondition check to be performed inside or
outside the protected action associated with a protected subprogram
call.
!example
generic
type Item is private;
package Stacks is
type Stack is private;
function Is_Empty(S : in Stack) return Boolean;
function Is_Full(S : in Stack) return Boolean;
procedure Push(S : in out Stack; I : in Item)
with
Pre => not Is_Full(S),
Post => not Is_Empty(S);
procedure Pop(S : in out Stack; I : out Item)
with
Pre => not Is_Empty(S),
Post => not Is_Full(S);
function Top(S : in Stack) return Item
with
Pre => not Is_Empty(S);
function Is_Valid_Stack(S : in Stack) return Boolean;
Stack_Error : exception;
private
--
end Stacks;
!ACATS test
!appendix
****************************************************************
Questions? Ask the ACAA Technical Agent