The definition of the exception handling facility will provide answers to the following questions:
SINGULAR : exception;
Conceptually, we may view an exception declaration as declaring a constant of some type called "exception", whose values may only be mentioned in exception handlers and in raise statements. Thus the above declaration has the meaning that SINGULAR is one of the possible exceptions. Like any other declaration, an exception declaration has a scope, which is the region of text in which the corresponding name can be written in order to refer to the exception. However, as this analogy suggests, the error situation associated with an exception will exist beyond this region.
Declarations of the predefined exceptions, namely CONSTRAINT_ERROR, NUMERIC_ERROR, PROGRAM_ERROR, STORAGE_ERROR, and TASKING_ERROR, are provided in the package STANDARD that defines the predefined environment.
Exception handlers may only appear at the end of a block statement or at the end of the body of a subprogram, package, or task unit; after the reserved word exception. In each of these cases, the construct includes the following part, called a frame:
begin sequence_of_statements exception exception_handler {exception_handler} end |
The exception handlers given after the reserved word exception in a frame apply to the sequence of statements given after the reserved word begin in the same frame. As an example the following block contains a single handler that services the exception SINGULAR:
begin -- sequence of statements exception when SINGULAR => PUT("Matrix is singular"); end; |
A handler that starts with when others services all exceptions that have no explicit handler in the same frame. Note finally, that where we want to localize the effect of handlers to some specific statements, we may always do so by enclosing these statements and handlers within a block statement.
The normal form of raise statement includes the reserved word raise and the name of the exception that is raised:
raise SINGULAR; raise IO_EXCEPTIONS.DEVICE_ERROR; |
The name of the exception must of course be visible at the point of the raise statement. It may have the form of a selected component, as in the above case of the exception DEVICE_ERROR declared in the package IO_EXCEPTIONS.
Note that if a frame contains a raise statement for a given exception, it does not necessarily contain a handler for that exception. For example, in the procedure P given below, both the procedures P and R provide a handler for SINGULAR and have no explicit raise statement for that exception. On the other hand, the procedure Q contains an explicit raise statement for SINGULAR but provides no handler for that exception.
procedure P is ... SINGULAR : exception; ... procedure Q is begin ... if DETERMINANT = 0 then raise SINGULAR; end if; ... end Q; procedure R is begin ... Q; ... exception when SINGULAR => ... -- inner handler for SINGULAR end R; begin -- P ... R; ... Q; exception when SINGULAR => ... -- outer handler for SINGULAR end P; |
When an exception is raised within the sequence of statements of a frame, the execution of this sequence of statements is always abandoned. What happens next depends on the presence or absence of appropriate exception handlers:
With this definition of exception handling, the effect of a subprogram, which is normally completed by the sequence of statements of its body, may alternatively, when an exception occurs, be completed by a corresponding handler, if present.
The sequence of statements of a package body acts as a procedure that is implicitly called by the package for its initialization. This also applies for exceptions. A handler in a package body acts like a handler in a procedure. In the absence of a handler, an exception is propagated to the program unit that contains the package declaration. The case of task bodies is discussed in section 14.4.
After the explanation of the concept of exception propagation, it should now be clear that there is no conceptual difference between the predefined exceptions and exceptions that are declared by the user. Predefined exceptions are exceptions that can be propagated by the basic operations of the language such as indexing, accessing a value, and the arithmetic operations. As an example NUMERIC_ERROR is an exception that may be propagated by the (hardware supplied) operation of division.
raise;
In either case, the effect of raising the same (or another) exception within a handler is to abandon the execution of the frame and to propagate the corresponding exception (except for tasks as explained in section 14.4).
The abbreviated form for raising the same exception again is especially useful in the case of a handler for others. Thus, such a handler can be used to perform some general cleanup actions, such as undoing possible side-effects, before raising the same exception again. This is made possible by the fact that the exception is left anonymous both in the handler prefix and in the reraise statement.
pragma SUPPRESS(RANGE_CHECK, ON => INDEX);
allows the compiler to omit range checks for assignments to variables of subtype INDEX. Note that this pragma is not imperative, and its inclusion does not guarantee that the exception CONSTRAINT_ERROR will not be raised: it may be raised explicitly, or be propagated from a subprogram in which checks are not suppressed. Finally, the exception may be raised simply because the compiler did not inhibit the check: this is likely to be the case if hardware checks are available.