pragma Postcondition ( [Check =>] Boolean_Expression [,[Message =>] String_Expression]);
The Postcondition pragma allows specification of automatic postcondition checks for subprograms. These checks are similar to assertions, but are automatically inserted just prior to the return statements of the subprogram with which they are associated (including implicit returns at the end of procedure bodies and associated exception handlers).
In addition, the boolean expression which is the condition which must be true may contain references to function'Result in the case of a function to refer to the returned value.
Postcondition pragmas may appear either immediately following the (separate) declaration of a subprogram, or at the start of the declarations of a subprogram body. Only other pragmas may intervene (that is appear between the subprogram declaration and its postconditions, or appear before the postcondition in the declaration sequence in a subprogram body). In the case of a postcondition appearing after a subprogram declaration, the formal arguments of the subprogram are visible, and can be referenced in the postcondition expressions.
The postconditions are collected and automatically tested just before any return (implicit or explicit) in the subprogram body. A postcondition is only recognized if postconditions are active at the time the pragma is encountered. The compiler switch `gnata' turns on all postconditions by default, and pragma Check_Policy with an identifier of Postcondition can also be used to control whether postconditions are active.
The general approach is that postconditions are placed in the spec if they represent functional aspects which make sense to the client. For example we might have:
function Direction return Integer; pragma Postcondition (Direction'Result = +1 or else Direction'Result = -1);
which serves to document that the result must be +1 or -1, and will test that this is the case at run time if postcondition checking is active.
Postconditions within the subprogram body can be used to check that some internal aspect of the implementation, not visible to the client, is operating as expected. For instance if a square root routine keeps an internal counter of the number of times it is called, then we might have the following postcondition:
Sqrt_Calls : Natural := 0; function Sqrt (Arg : Float) return Float is pragma Postcondition (Sqrt_Calls = Sqrt_Calls'Old + 1); ... end Sqrt
As this example, shows, the use of the Old attribute is often useful in postconditions to refer to the state on entry to the subprogram.
Note that postconditions are only checked on normal returns from the subprogram. If an abnormal return results from raising an exception, then the postconditions are not checked.
If a postcondition fails, then the exception System.Assertions.Assert_Failure is raised. If a message argument was supplied, then the given string will be used as the exception message. If no message argument was supplied, then the default message has the form "Postcondition failed at file_name:line". The exception is raised in the context of the subprogram body, so it is possible to catch postcondition failures within the subprogram body itself.
Within a package spec, normal visibility rules in Ada would prevent forward references within a postcondition pragma to functions defined later in the same package. This would introduce undesirable ordering constraints. To avoid this problem, all postcondition pragmas are analyzed at the end of the package spec, allowing forward references.
The following example shows that this even allows mutually recursive postconditions as in:
package Parity_Functions is function Odd (X : Natural) return Boolean; pragma Postcondition (Odd'Result = (x = 1 or else (x /= 0 and then Even (X - 1)))); function Even (X : Natural) return Boolean; pragma Postcondition (Even'Result = (x = 0 or else (x /= 1 and then Odd (X - 1)))); end Parity_Functions;
There are no restrictions on the complexity or form of conditions used within Postcondition pragmas. The following example shows that it is even possible to verify performance behavior.
package Sort is Performance : constant Float; -- Performance constant set by implementation -- to match target architecture behavior. procedure Treesort (Arg : String); -- Sorts characters of argument using N*logN sort pragma Postcondition (Float (Clock - Clock'Old) <= Float (Arg'Length) * log (Float (Arg'Length)) * Performance); end Sort;
Note: postcondition pragmas associated with subprograms that are marked as Inline_Always, or those marked as Inline with front-end inlining (-gnatN option set) are accepted and legality-checked by the compiler, but are ignored at run-time even if postcondition checking is enabled.
Note that pragma Postcondition differs from the language-defined Post aspect (and corresponding Post pragma) in allowing multiple occurrences, allowing occurences in the body even if there is a separate spec, and allowing a second string parameter, and the use of the pragma identifier Check. Historically, pragma Postcondition was implemented prior to the development of Ada 2012, and has been retained in its original form for compatibility purposes.