[Ada Information Clearinghouse]
Ada '83 Rationale, Sec 14.2: Presentation of Exception Handling in Ada

"Rationale for the Design of the
Ada® Programming Language"

[Ada '83 Rationale, HTML Version]

Copyright ©1986 owned by the United States Government. All rights reserved.
Direct inquiries to the Ada Information Clearinghouse at adainfo@sw-eng.falls-church.va.us.

CHAPTER 14: Exception Handling

14.2 Presentation of Exception Handling in Ada

There exist situations that prevent the completion of an action; for example, where a constraint is violated. An exception is a name that is attached to such a situation; for example, the name CONSTRAINT_ERROR is attached to the violation of a constraint. Raising an exception means telling the invoker of an action that the corresponding error situation has occurred; and handling an exception means executing some actions in response to this occurrence.

The definition of the exception handling facility will provide answers to the following questions:

We first examine these different questions in the case of sequential programs; then the case of parallel tasks is discussed in section 14.4.

In this section...

14.2.1 Declaration of Exceptions
14.2.2 Exception Handlers
14.2.3 The Raise Statement
14.2.4 Association of Handlers with Exceptions
14.2.5 Raising the Same Exception Again
14.2.6 Suppressing Checks
14.2.7 Order of Exceptions


14.2.1 Declaration of Exceptions

An exception declaration associates a name with a particular error situation. The form of an exception declaration is shown by the following example:

    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.


14.2.2 Exception Handlers

Exception handlers are the sections of the program to which control is passed when exceptions occur. Each exception handler has the form of a sequence of statements prefixed by the reserved word when followed by the names of the exceptions that are serviced by the handler considered (or the reserved word others, as described below).

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.


14.2.3 The Raise Statement

There are two possible reasons for an exception to be raised in a given program unit. It may either be explicitly raised by a raise statement or, as we will explain later, it may be propagated by subprograms (including operators), package bodies and blocks executed by the program unit considered. (Violation of a constraint is treated as propagation.)

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.


see 14.5.1).

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:

(a) The frame includes a handler for the exception:

In this case the execution of the sequence of statements of this handler completes the execution of the frame.

(b) The frame does not have a handler for the exception:

In this case the subsequent actions depend on the nature of the frame. For a subprogram body, the same exception is raised - implicitly - at the point of call of the subprogram; for a block statement, the same exception is raised within the frame containing the block statement itself, after this statement. In either case, we say that the exception is propagated.
In the above example, if the exception SINGULAR is raised during the execution of Q that is called from R: Note that the outer handler for SINGULAR, that of P, would be executed if the exception were raised by the execution of Q that is called directly from within P.

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.


14.2.5 Raising the Same Exception Again

Within a handler, the exception that caused transfer to the handler may be raised again by a normal raise statement (mentioning its name) or by an abbreviated raise statement of the form

    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.


14.2.6 Suppressing Checks

It is possible to inform the compiler that certain run-time checks need not be provided within a given frame. This is achieved by a pragma. For example, the pragma

    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.


14.2.7 Order of Exceptions

A compiler may choose to evaluate the constituent terms of an expression in any order that is consistent with the precedence properties of the operators, and with the parentheses. As a consequence, the order in which exceptions might be raised in the course of the evaluation of an expression is not guaranteed by the language. The semantics of the language only defines the value of expressions whose evaluation does not raise any exception.


NEXTPREVIOUSUPTOCINDEX
Address any questions or comments to adainfo@sw-eng.falls-church.va.us.