tur/forms/panics
unrecoverable errors, and catching one at a boundary.
A panic is for the failure a program cannot sensibly continue from: a broken invariant, an impossible state. Expected failures belong in a Result (error-handling-guide). panic unwinds the stack, firing each defer on the way, and aborts the program with panic at FILE:LINE: MESSAGE on stderr -- unless a catch-unwind or catch-panic-of boundary is above it, which turns the panic into an ordinary Result and lets execution go on.
The panic-payload-* forms read a caught panic: its message or value, its source location, and the type of its payload. Most programs want the named wrapper in stdlib/panic.tur (result-panic, panic-message, panic-file, panic-line, panic-type) instead; load it with (load "stdlib/panic.tur"). Both engines answer them alike: the type, file (the program's file name) and line of the panic site.
panic
(panic msg)
abort the program with an unrecoverable error.
Prints panic at FILE:LINE: MESSAGE to stderr, runs the deferred cleanups registered in the enclosing scopes, then aborts (exit status 134, SIGABRT, when compiled). Exactly one argument; a string is the message, and any other value prints (non-string panic). The form has no value -- control never continues past it, so it can stand where any type is expected. If a defer that runs during unwinding itself panics, the program aborts at once (a double panic is fatal and cannot be caught).
| msg | the message, normally a string |
(err? (catch-unwind (fn [] : int (panic "boom")))) ; => true
panic-with
(panic-with payload)
panic with a typed payload instead of a message.
Like panic, but the payload can be any value, and catch-panic-of and panic-payload-downcast can tell payloads apart by type. An uncaught panic-with aborts, naming your source file and line, as panic does; so does panic-payload-line on a caught one. It has no value, like panic.
| payload | the value to panic with |
(err? (catch-unwind (fn [] : int (panic-with 42)))) ; => true
catch-unwind
(catch-unwind thunk)
run a thunk, turning any panic into an err result.
The thunk is a zero-argument fn. If it returns, the result is (ok value); if it panics, the panic is caught and the result is (err panic), where the err slot holds a panic handle for the panic-payload-* forms, and execution continues after the boundary. The result is an ordinary Result for ok?, err? and ok-val. Defers inside the thunk fire during unwinding, before the err result exists; defers above the boundary are untouched. A panic is not a substitute for a Result: use this at FFI boundaries (a panic must not unwind into C), test harnesses and supervisor loops. The argument must be a function of no arguments: (catch-unwind 5) and a function that takes parameters are type errors.
| thunk | a zero-argument function to run |
(ok value) if the thunk returned, (err panic) if it panicked.
(+ (ok-val (catch-unwind (fn [] : int 41))) 1) ; => 42
catch-panic-of
(catch-panic-of type thunk)
like catch-unwind, but only for one payload type.
Catches a panic whose payload has the type given by the first argument (:cstr for panic "...", :int for (panic-with 42)), as catch-unwind would. A panic with any other payload type continues to the next boundary outward; with none above, it is an uncaught panic and the program aborts, as an uncaught panic does.
| type | the payload type to catch, as a keyword or bare type name | |
| thunk | a zero-argument function to run |
(ok value) if the thunk returned, (err panic) if it panicked with a
payload of that type.
(err? (catch-panic-of :cstr (fn [] : int (panic "s")))) ; => true (err? (catch-panic-of :int (fn [] : int (panic-with 42)))) ; => true
panic-payload-type
(panic-payload-type [p :ptr<void>] :int)
the type tag of a caught panic's payload.
Takes the panic handle from the err slot of a catch-unwind result (result-panic in tur/panic extracts it). Returns the integer type kind of the payload: 5 for the string of (panic "...").
| p | a panic handle |
The payload's type kind, an int.
(panic-payload-type (result-panic (catch-unwind (fn [] : int (panic "boom"))))) ; => 5 ; doctest: needs (load "stdlib/panic.tur")
panic-payload-value
(panic-payload-value [p :ptr<void>] :ptr<void>)
the payload of a caught panic, as a raw pointer.
For (panic "...") this points at the message string. For panic-with of an integer the pointer value IS the integer (42 for (panic-with 42)), not an address -- do not dereference it. Read the message with panic-message in tur/panic, or branch on panic-payload-type first.
| p | a panic handle |
A ptr<void> to the payload.
panic-payload-file
(panic-payload-file [p :ptr<void>] :cstr)
the source file a caught panic came from.
The base name of the file holding the panic or panic-with form.
| p | a panic handle |
The file name, a cstr.
(panic-payload-file (result-panic (catch-unwind (fn [] : int (panic "boom"))))) ; => "main.tur" ; doctest: needs (load "stdlib/panic.tur"); the name is the program's own
panic-payload-line
(panic-payload-line [p :ptr<void>] :int)
the source line a caught panic came from.
The line of the panic or panic-with form.
| p | a panic handle |
The line number, an int.
(panic-payload-line (result-panic (catch-unwind (fn [] : int (panic "boom"))))) ; => 1 ; doctest: needs (load "stdlib/panic.tur"); the line is the program's own
panic-payload-downcast
(panic-payload-downcast [p :ptr<void> type :T] :ptr<void>)
the payload, if it has the given type.
Returns a pointer to the payload when its type matches the second argument and a null pointer otherwise (ptr-null?, inside unsafe, tells which): (panic-payload-downcast p :cstr) is the message of a (panic "..."), and null for the payload of a panic-with of an integer.
| p | a panic handle | |
| type | the payload type to match, as a keyword or bare type name |
A ptr<void>: the payload, or null.