Subsections

- 1 The raise statement
- 2 The try...except statement
- 3 The try...finally statement
- 4 Exception handling nesting
- 5 Exception classes

#
11 Exceptions

Exceptions provide a convenient way to program error and error-recovery mechanisms, and are closely related to classes. Exception support is based on 3 constructs:

Raise
  statements. To raise an exeption. This is usually done to signal an error condition.
Try ... Except
  blocks. These block serve to catch exceptions raised within the scope of the block, and to provide exception-recovery code.
Try ... Finally
  blocks. These block serve to force code to be executed irrespective of an exception occurrence or not. They generally serve to clean up memory or close files in case an exception occurs. The compiler generates many implicit `Try ... Finally` blocks around procedure, to force memory consistence.

# 1 The raise statement

The `raise` statement is as follows:

> Raise statement

This statement will raise an exception. If it is specified, the exception instance must be an initialized instance of a class, which is the raise type. The address exception is optional. If itis not specified, the compiler will provide the address by itself. If the exception instance is omitted, then the current exception is re-raised. This construct can only be used in an exception handling block (see further).

Remark: Control never returns after an exception block. The control is transferred to the first `try...finally` or `try...except` statement that is encountered when unwinding the stack. If no such statement is found, the Free Pascal Run-Time Library will generate a run-time error 217 (see also section exceptclasses).

As an example: The following division checks whether the denominator is zero, and if so, raises an exception of type `EDivException`

    Type EDivException = Class(Exception);
    Function DoDiv (X,Y : Longint) : Integer;
    begin
      If Y=0 then
        Raise EDivException.Create ('Division by Zero would occur');
      Result := X Div Y;
    end;

The class `Exception` is defined in the Sysutils unit of the rtl. (section exceptclasses)

# 2 The try...except statement

A `try...except` exception handling block is of the following form :

> Try..except statement

If no exception is raised during the execution of the `statement list`, then all statements in the list will be executed sequentially, and the except block will be skipped, transferring program flow to the statement after the final `end`.

If an exception occurs during the execution of the `statement list`, the program flow will be transferred to the except block. Statements in the statement list between the place where the exception was raised and the exception block are ignored.

In the exception handling block, the type of the exception is checked, and if there is an exception handler where the class type matches the exception object type, or is a parent type of the exception object type, then the statement following the corresponding `Do` will be executed. The first matching type is used. After the `Do` block was executed, the program continues after the `End` statement.

The identifier in an exception handling statement is optional, and declares an exception object. It can be used to manipulate the exception object in the exception handling code. The scope of this declaration is the statement block foillowing the `Do` keyword.

If none of the `On` handlers matches the exception object type, then the statement list after `else` is executed. If no such list is found, then the exception is automatically re-raised. This process allows to nest `try...except` blocks.

If, on the other hand, the exception was caught, then the exception object is destroyed at the end of the exception handling block, before program flow continues. The exception is destroyed through a call to the object's `Destroy` destructor.

As an example, given the previous declaration of the `DoDiv` function, consider the following

    Try
      Z := DoDiv (X,Y);
    Except
      On EDivException do Z := 0;
    end;

If `Y` happens to be zero, then the DoDiv function code will raise an exception. When this happens, program flow is transferred to the except statement, where the Exception handler will set the value of `Z` to zero. If no exception is raised, then program flow continues past the last `end` statement. To allow error recovery, the `Try ... Finally` block is supported. A `Try...Finally` block ensures that the statements following the `Finally` keyword are guaranteed to be executed, even if an exception occurs.

# 3 The try...finally statement

A `Try..Finally` statement has the following form:

> Try...finally statement

If no exception occurs inside the `statement List`, then the program runs as if the `Try`, `Finally` and `End` keywords were not present.

If, however, an exception occurs, the program flow is immediatly transferred from the point where the excepion was raised to the first statement of the `Finally statements`.

All statements after the finally keyword will be executed, and then the exception will be automatically re-raised. Any statements between the place where the exception was raised and the first statement of the `Finally Statements` are skipped.

As an example consider the following routine:

    Procedure Doit (Name : string);
    Var F : Text;
    begin
      Try
        Assign (F,Name);
        Rewrite (name);
        ... File handling ...
      Finally
        Close(F);
      end;

If during the execution of the file handling an execption occurs, then program flow will continue at the `close(F)` statement, skipping any file operations that might follow between the place where the exception was raised, and the `Close` statement. If no exception occurred, all file operations will be executed, and the file will be closed at the end.

# 4 Exception handling nesting

It is possible to nest `Try...Except` blocks with `Try...Finally` blocks. Program flow will be done according to a `lifo` (last in, first out) principle: The code of the last encountered `Try...Except` or `Try...Finally` block will be executed first. If the exception is not caught, or it was a finally statement, program flow will be transferred to the last-but-one block, ad infinitum.

If an exception occurs, and there is no exception handler present, then a runerror 217 will be generated. When using the sysutils unit, a default handler is installed which will show the exception object message, and the address where the exception occurred, after which the program will exit with a `Halt` instruction.

#
5 Exception classes

The sysutils unit contains a great deal of exception handling. It defines the following exception types:

           Exception = class(TObject)
            private
              fmessage : string;
              fhelpcontext : longint;
            public
              constructor create(const msg : string);
              constructor createres(indent : longint);
              property helpcontext : longint read fhelpcontext write fhelpcontext;
              property message : string read fmessage write fmessage;
           end;
           ExceptClass = Class of Exception;
           { mathematical exceptions }
           EIntError = class(Exception);
           EDivByZero = class(EIntError);
           ERangeError = class(EIntError);
           EIntOverflow = class(EIntError);
           EMathError = class(Exception);

The sysutils unit also installs an exception handler. If an exception is unhandled by any exception handling block, this handler is called by the Run-Time library. Basically, it prints the exception address, and it prints the message of the Exception object, and exits with a exit code of 217. If the exception object is not a descendent object of the `Exception` object, then the class name is printed instead of the exception message.

It is recommended to use the `Exception` object or a descendant class for all `raise` statements, since then the message field of the exception object can be used.