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;