Chapter 13 — Key Takeaways (Error Handling, Debugging, and Defensive Programming)
A one-page reference. The chapter's core: ask whether each operation worked, halt deliberately when it did not, arm the compiler to catch what you missed, and check your own beliefs.
Error handling: ask, then decide
| Construct | What it does |
|---|---|
allocate(a(n), stat=s, errmsg=msg) |
Guard allocation: s = 0 on success, nonzero (processor-dependent) on failure; msg gets the text. Without stat, a failure aborts. |
deallocate(a, stat=s) |
Same guard on release. |
open(..., iostat=ios, iomsg=msg) |
The I/O version (Ch. 7). Same shape — this is what stat generalizes. |
if (s /= 0) … |
The only portable test. Never compare stat/iostat to a magic number. |
Halting: stop vs error stop
stop |
error stop [code] |
|
|---|---|---|
| Termination | normal | error |
| Default exit code | 0 (success) |
nonzero (gfortran 1) |
With code N |
exit N |
exit N |
| Parallel (coarrays) | orderly | immediate, all images |
| Meaning to the shell | "all well" | "something failed — notice me" |
- Exit codes are read with
echo $?, and taken mod 256 on Unix — keep codes in 1–255. - Use distinct codes per failure class (e.g.
2bad config,3allocation,4out-of-range) so scripts branch on$?without parsing text. - Diagnostics go to
error_unit(standard error), not standard output.
Compile flags introduced (see Appendix C)
| Flag | Effect | When |
|---|---|---|
-fcheck=all |
Runtime checks: bounds, allocation, pointers | Development — remove for production |
-fbacktrace |
Stack trace on a crash (pair with -g) |
Cheap; keep on |
-ffpe-trap=invalid,zero,overflow |
Halt at the op that makes NaN/Inf |
Development (and often production) |
-finit-real=snan |
Fill uninitialized reals with signaling NaN |
Development — trips -ffpe-trap=invalid |
-fsanitize=address / -fsanitize=undefined |
Fast in-binary memory / UB checks | Development |
-ftrapv |
Trap signed integer overflow instead of wrapping | Development |
Never trap underflow/inexact — they fire constantly in correct code.
Debuggers: which tool for which fault
| Symptom | Tool | Moves |
|---|---|---|
| "Why is this value wrong?" | gdb | break file:line · run · print v · next/step · bt · continue |
| Leak / read of freed memory / dangling | valgrind (--leak-check=full) or -fsanitize=address |
run under it; slow (~10–30×), use small cases |
| Out-of-bounds, unallocated, bad pointer | -fcheck=all |
just recompile; names the line |
NaN/Inf born mid-run |
-ffpe-trap + -fbacktrace |
crashes at the raising line |
Defensive programming
- Assertion — a hand-written
assert(cond, msg)thaterror stops whencondis false. For the "impossible" (a bug in your logic), not for expected errors (those usestat/iostat).
subroutine assert(condition, message)
logical, intent(in) :: condition
character(len=*), intent(in) :: message
if (.not. condition) then
write(error_unit, '(a)') 'ASSERTION FAILED: ' // message
error stop 1
end if
end subroutine assert
- Precondition: check arguments/config at the boundary, before computing.
- Postcondition: check the result is sane (e.g. no
NaN). - Make checks removable:
logical, parameter :: checking = .true.+if (checking) call assert(...); flip to.false.and the optimizer deletes them. (Same idea as C'sNDEBUG, Python's-O.) NaNtest:if (x /= x)is.true.only forNaN(or useieee_is_nan, Ch. 20).
The usual suspects (name → fix)
| Suspect | Fix |
|---|---|
| Uninitialized variable (holds garbage, not 0) | Initialize before use; -Wall, -finit-real=snan |
| Out-of-bounds index (1-based! Ch. 5) | -fcheck=bounds |
Integer overflow (int32 wraps past ~2.1e9) |
Use integer(int64) for counts/products |
| Precision loss | No == on reals (use a tolerance); kind suffixes 1.0_dp; watch integer division 1/2 = 0 (Ch. 3) |
Implicit save |
integer :: n = 0 in a procedure is set once and persists — assign in an executable statement for a per-call reset; write save explicitly if you want persistence |
Numbers and rules worth memorizing
huge(0_int32)= 2,147,483,647;+ 1wraps to −2,147,483,648 (two's-complement).stat/iostat: 0 = success, nonzero = failure; end-of-file is negative (iostat_end).- Exit code: 0 = success, nonzero = failure, mod 256.
- An initializer in a declaration ⇒ implicit
save— the single most surprising Fortran trap.
The ethic
Fail early, fail loud, fail with a message that names what was expected and what was seen. A solver that stops with
'dt must be positive, got -0.5'is worth infinitely more than one that runs for a day and writesNaN.
Project piece added this chapter
validate_config(nx, ny, dt, alpha) — reject a non-positive grid, dt, or alpha at the boundary; a
stat-guarded field allocation; distinct error stop codes for config (2) vs allocation (3)
failure; build with -fcheck=all -g while developing (strip -fcheck for production, Ch. 30).
Key terms
error stop · stat/errmsg (allocation) · floating-point exception trap · assertion ·
defensive programming · (used from Ch. 7: iostat/iomsg; from Ch. 11: dangling pointer).