Laurel User Guide

6.3. Handling: try, catch, finally🔗

catch dispatches on a predicate rather than on a type, written catch e when <condition on e>. Type-based dispatch is one such predicate: catch e when e is NotFound. Clauses are ordered and first-match-wins, and a clause with no when guard is a catch-all. finally runs on the way out of the try.

composite Exception {}
composite NotFound extends Exception {}
composite Invalid extends Exception {}

procedure handle(fail: int) returns (r: int)
  opaque
{
  r := 0;
  try {
    if fail == 1 then {
      var e: NotFound := new NotFound;
      throw e
    };
    if fail == 2 then {
      var i: Invalid := new Invalid;
      throw i
    }
  } catch e when e is NotFound {
    r := 1
  } catch e {
    r := 2
  } finally {
    assert r >= 0
  }
};

finally runs after a normal completion, after a caught exception, on the way out with an uncaught one, and when a handler itself throws or returns. A return or an exit that leaves the try runs it too, and nested finally arms chain outward.

One rule decides the rest: if the finally arm itself completes abruptly — it returns, throws, or exits — that completion wins, and whatever was pending is discarded. This is Java's rule (JLS 14.20.2), so try { throw e } finally { return } returns normally and the exception is gone.