Exceptions: Declaring, Raising, Handling & Propagation

Declare and raise exceptions with messages, handle them with exception handlers (when E : Name =>, when others), inspect occurrences with Ada.Exceptions (Exception_Name, Exception_Message, Exception_Information, Save_Occurrence), re-raise, raise expressions, propagation rules including exceptions in declarations, and the behavior of unhandled exceptions.

Ada has had exceptions since 1983. The predefined ones — Constraint_Error, Program_Error, Storage_Error, Tasking_Error — are raised by language checks; you declare your own for domain errors. An exception propagates up the call chain until a handler catches it; unhandled, it terminates the program with a clear message giving the exception name and source location.

Declaring and Raising

'Invalid_Input : exception;' declares an exception (usually in a package spec so clients can handle it). 'raise Invalid_Input;' raises it; 'raise Invalid_Input with "age out of range";' attaches a message. Ada 2012 raise expressions ('(if B = 0 then raise Constraint_Error with "..." else A / B)') allow raising inside expressions — useful in expression functions and contracts. Exceptions are not types and carry only a name and message; for richer data, store it separately or encode it in the message.

• E : exception; — declaration. • raise E; / raise E with "message"; • raise expressions inside expressions. • Predefined: Constraint_Error, Program_Error, Storage_Error, Tasking_Error.

Handling

Any begin ... end frame (subprogram body, block, task body, accept) may end with 'exception' and handlers: 'when Invalid_Input => ...', 'when Constraint_Error | Program_Error => ...', 'when E : others => ...'. The optional choice parameter E gives access to the occurrence via Ada.Exceptions: Exception_Name (E), Exception_Message (E), Exception_Information (E) (includes a traceback when enabled). After a handler completes, execution continues after the frame — the failing frame is abandoned, never resumed. 'raise;' inside a handler re-raises the current occurrence; Save_Occurrence stores one for later; Reraise_Occurrence raises a saved one.

• exception when X => ...; when E : others => ... • Ada.Exceptions: Exception_Name / Message / Information. • raise; re-raises; Save_Occurrence / Reraise_Occurrence. • Handlers don't resume: control continues after the frame.

Propagation Rules

An exception raised in a frame without a matching handler propagates to the caller (or enclosing block). An exception raised while elaborating a frame's declarations is not handled by that frame's own handlers — it propagates to the enclosing scope. An unhandled exception in the main program terminates it with a message such as 'raised CONSTRAINT_ERROR : file.adb:5 range check failed' and a non-zero exit status. In tasks, unhandled exceptions silently terminate the task (log them in the task body's handler!).

• Unhandled → propagates to caller. • Declaration failures skip the frame's own handlers. • Unhandled in main → message + exit status 1. • Unhandled in a task → task dies silently; always add a handler.

Code Examples

Integer'Value ("abc") raises Constraint_Error with a message; 200 triggers the user-defined exception. Level_1 logs and re-raises; main catches it with 'others'. Safe_Divide uses a raise expression. The declaration of Small fails (0 is not Positive) and is caught by the outer handler. Output: Parse_Age ("42") = 42 CONSTRAINT_ERROR: bad input for 'Value: "abc" Invalid_Input: age out of range: 200 Level_1 logs the error and re-raises it Main caught PROGRAM_ERROR ("deep failure") Saved occurrence: PROGRAM_ERROR Safe_Divide: division by zero requested Declaration failure caught by the OUTER handler

No handler at all. GNAT warns at compile time; at run time the program prints (stderr) and exits: raised CONSTRAINT_ERROR: l41_unhandled.adb:5 range check failed [process exited with status 1] exception when others => null; -- swallow everything exception when E: others => Log (Exception_Information (E)); raise;

Silently ignoring exceptions hides bugs; log and re-raise, or handle specific ones.

-- using exceptions for normal control flow in a loop -- test the condition (e.g. End_Of_File) before the operation

Exceptions are for exceptional situations; they are slower and obscure the logic.

declare X: Positive:= Compute; -- may fail begin... exception when Constraint_Error =>... -- won't catch it -- put the handler in the enclosing frame, or initialize X in the statements

Exceptions from declarations propagate outward.

How do you attach a message to a raised exception? A. raise E ("msg"); B. raise E with "msg"; C. throw E("msg"); D. raise E : "msg";

What does 'raise;' do inside a handler? A. Raises Program_Error B. Re-raises the current exception occurrence C. Nothing D. Resumes execution

Does a block's handler catch an exception raised in its own declarative part? A. Yes B. No, it propagates to the enclosing scope C. Only for Constraint_Error D. Only with others

What happens to an unhandled exception in a task? A. The whole program stops B. The task terminates silently C. It is re-raised in main D. It is ignored and the task continues Trace the exception example.

Real output: Parse_Age ("42") = 42 CONSTRAINT_ERROR: bad input for 'Value: "abc" Invalid_Input: age out of range: 200 Level_1 logs the error and re-raises it Main caught PROGRAM_ERROR ("deep failure") Saved occurrence: PROGRAM_ERROR Safe_Divide: division by zero requested Declaration failure caught by the OUTER handler

with Ada.Text_IO;    use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;

procedure L41_Exceptions is
   Invalid_Input : exception;                        --  user-defined exception

   function Parse_Age (S : String) return Natural is
      Age : Integer;
   begin
      Age := Integer'Value (S);                      --  may raise Constraint_Error
      if Age not in 0 .. 150 then
         raise Invalid_Input with "age out of range:" & Integer'Image (Age);
      end if;
      return Age;
   end Parse_Age;

   procedure Try (S : String) is
   begin
      Put_Line ("Parse_Age (""" & S & """) =" & Natural'Image (Parse_Age (S)));
   exception
      when E : Invalid_Input =>
         Put_Line ("  Invalid_Input: " & Exception_Message (E));
      when E : Constraint_Error =>
         Put_Line ("  " & Exception_Name (E) & ": " & Exception_Message (E));
   end Try;

   procedure Level_2 is
   begin
      raise Program_Error with "deep failure";
   end Level_2;

   procedure Level_1 is
   begin
      Level_2;
   exception
      when Program_Error =>
         Put_Line ("  Level_1 logs the error and re-raises it");
         raise;                                      --  re-raise the sam
…
procedure L41_Unhandled is
   type Percent is range 0 .. 100;
   P : Percent := 100;
begin
   P := P + 1;          --  no handler anywhere
end L41_Unhandled;