Error Handling and Program Termination
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Fortran 2023 (ISO/IEC 1539-1:2023, J3/23-007r1) and was checked on 2026-10-18. Every
example was compiled with gfortran -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all (GNU Fortran 16.2)
and run, and the examples were also built and run with LLVM Flang 23; the output shown is real output. Where the two
compilers print different text or return different numbers, both are shown, because most of what an error produces
(status values, message texts, the form of the stop output) is processor dependent. To keep the listings readable the
output was captured with the stderr and stdout streams merged and unbuffered
(GFORTRAN_UNBUFFERED_ALL=1 for gfortran), and gfortran’s address-only backtrace frames were switched off with
-fno-backtrace. The rules are in these places in the standard: termination of execution is 5.3.7, the stop and
error stop statements are 11.4, the stat= and errmsg= specifiers of allocate and deallocate are 9.7.4 and 9.7.5,
input/output conditions are 12.11, the IEEE exceptions are 17.3 and halting is 17.6. Annex C.1 lists what Fortran 2018
changed.
Fortran has no try/catch, no throw and no stack unwinding. An error is handled in one of three ways: the statement
that detected it reports it through a status variable that the programmer supplied, the program decides that it cannot go
on and terminates, or nothing detects it at all and the program carries on with wrong data. This page covers the first
two, in the order a program meets them, and then says what the third looks like and how to turn it into one of the first
two. It uses the heatflow running example: the configuration reader that returns stat and msg, the solver factory
that stops on an unknown solver, and the application that turns a bad configuration into a nonzero exit status. The
same ideas for I/O are developed in
Input/Output: Units and Files, for allocation in
Allocatable Arrays and Dynamic Memory, for
floating-point exceptions in
Floating-Point and IEEE Arithmetic, and for
debugging tools in
Testing, Debugging and Profiling.
Fortran has no exceptions
The mechanisms Fortran offers are all local: the statement or procedure call that can fail takes an argument in which the outcome is stored, and the caller looks at it, or does not, in which case a default happens. The defaults differ, and that is the first thing to know.
| Mechanism | Where | If you ask for nothing | Clause |
|---|---|---|---|
|
|
error termination of the image |
9.7.1.2, 9.7.4, 9.7.5, 11.7.11 |
|
|
error termination for an error, an end-of-file or an end-of-record condition |
12.11 |
|
|
depends on the procedure: a truncated value is silent, a failed |
16.9 |
IEEE exception flags |
every floating-point operation |
nothing happens, the flag stays set and the result is |
17.3, 17.6 |
|
anywhere (not |
not applicable: these are the termination |
11.4 |
Runtime checks |
compiler options such as |
undetected: wrong results or a crash |
processor dependent |
Two ideas run through the table. A condition that is detected and has no handler ends the program (error termination) for the I/O and allocation statements, which is the safe default. A condition that the processor does not detect, or detects only on request, is simply not an error as far as the program can tell. The next figure is the decision a procedure writer faces.
stat= and errmsg= on allocation
An allocate or deallocate statement that fails and has no stat= specifier initiates error termination (9.7.1.2 and
9.7.3). With stat= it does not terminate: the integer stat-variable becomes zero on success and a positive,
processor-dependent value on failure, and with errmsg= a message is stored in the character variable as if by
intrinsic assignment (9.7.4, 9.7.5). errmsg= is left unchanged when nothing goes wrong, so it must be cleared by the
caller if it is going to be examined.
The program tries five things: an allocation of 250 eight-byte elements (8 PiB) that no machine can satisfy, a normal allocation, an allocation of an array that is already allocated, a normal deallocation and a deallocation of an array that is not allocated.
program alloc_stat
use, intrinsic :: iso_fortran_env, only: real64, int64
implicit none
integer, parameter :: dp = real64
real(dp), allocatable :: u(:, :)
integer :: stat
character(len=200) :: msg
integer(int64) :: n
n = 2_int64**50 ! 2**50 elements of 8 bytes: 8 PiB, cannot succeed
msg = ''
allocate (u(n, 1), stat=stat, errmsg=msg)
print '(a, i0, a, l1)', 'huge allocate: stat = ', stat, ', allocated = ', allocated(u)
print '(a, a)', 'errmsg: ', trim(msg)
allocate (u(21, 21), stat=stat, errmsg=msg)
print '(a, i0, a, l1)', 'normal allocate: stat = ', stat, ', allocated = ', allocated(u)
allocate (u(5, 5), stat=stat, errmsg=msg) ! already allocated: an error condition
print '(a, i0)', 'allocate of an allocated array: stat = ', stat
print '(a, a)', 'errmsg: ', trim(msg)
deallocate (u, stat=stat, errmsg=msg)
print '(a, i0)', 'deallocate: stat = ', stat
deallocate (u, stat=stat, errmsg=msg) ! already unallocated: an error condition
print '(a, i0)', 'deallocate of an unallocated array: stat = ', stat
print '(a, a)', 'errmsg: ', trim(msg)
end program alloc_stat
Output with gfortran 16.2:
huge allocate: stat = 5020, allocated = F
errmsg: Insufficient virtual memory
normal allocate: stat = 0, allocated = T
allocate of an allocated array: stat = 5014
errmsg: Attempt to allocate an allocated object
deallocate: stat = 0
deallocate of an unallocated array: stat = 1
errmsg: Attempt to deallocate an unallocated object
Output with LLVM Flang 23:
huge allocate: stat = 19, allocated = F
errmsg: Memory allocation failed
normal allocate: stat = 0, allocated = T
allocate of an allocated array: stat = 12
errmsg: Base address is not null
deallocate: stat = 0
deallocate of an unallocated array: stat = 11
errmsg: Base address is null
The rules this shows:
-
Only zero versus nonzero is portable. The standard fixes the sign (positive) and, for coarray and image failures, two values; every other number and every message text belongs to the compiler, as the two columns above show (5020 against 19 for the same failure). Test
stat /= 0, never a particular value, and printerrmsgrather than parsing it. -
A failed
allocateleaves the object as it was. After the first statementallocated(u)is still false. With severalallocate-objectentries in one statement the state of each is processor dependent after a failure, and the standard recommends askingallocatedorassociatedfor each (9.7.4). -
Give
statan integer with a decimal exponent range of at least four. A default integer is fine; the standard says a smaller range might be unable to hold the processor’s code (9.7.4). -
The variable cannot be part of the statement. The
stat-variableanderrmsg-variableshall not be anallocate-object, nor depend on one, anderrmsg-variableshall not depend onstat-variable(9.7.4, 9.7.5). -
deallocateof an unallocated allocatable is an error condition, not a no-op, as the last statement shows. Guard it withif (allocated(u))or accept the status. -
Coarrays add two values. For an
allocateordeallocateof a coarray,statisstat_stopped_imageif the current team contains a stopped image andstat_failed_imageif it contains a failed image (both iniso_fortran_env, 16.10.2.31 and 16.10.2.28), see Termination in coarray programs. -
-fcheck=memis about compiler-generated allocations. In gfortran it adds run-time checks for memory allocation, and explicitallocatestatements are always checked whatever the option (GNU Fortran manual, Code Gen Options).
Without stat=, the same failure is fatal. The next program allocates the same impossible array, with no stat=:
program alloc_nostat
use, intrinsic :: iso_fortran_env, only: real64, int64
implicit none
real(real64), allocatable :: u(:)
allocate (u(2_int64**50)) ! no stat=: failure means error termination
u(1) = 1.0_real64
print '(a, f4.1)', 'not reached: ', u(1)
end program alloc_nostat
gfortran 16.2 terminates with exit status 1 and one line of output:
In file '12_alloc_nostat.f90', around line 5: Error allocating 9007199254740992 bytes: Cannot allocate memory
exit status: 1
Source: J3/23-007r1 (Fortran 2023)
Flang 23, on the same program, did not error-terminate: the failed allocation went unnoticed and the first use of u
crashed with Segmentation fault (exit status 139). The standard requires error termination here (9.7.1.2), so this is
a compiler shortcoming on the version tested, and a reason to put stat= on every allocation whose size depends on
input, which is portable and testable.
Input/output conditions: iostat=, iomsg=, err=, end= and eor=
Input/output statements report three kinds of condition (12.11):
-
an error condition, whose set is processor dependent (a file that cannot be opened, a conversion that fails, a write to a full disk);
-
an end-of-file condition, for sequential and stream input that runs past the last record, and for a read beyond the end of an internal file;
-
an end-of-record condition, for non-advancing input that tries to read past the end of the current record.
With iostat= the integer becomes zero for success, a positive processor-dependent value for an error, the
negative constant iostat_end for end of file and the negative constant iostat_eor for end of record (12.11.5;
the constants are in iso_fortran_env, 16.10.2.16 and 16.10.2.17). iomsg= receives an explanatory message for any of
the three conditions and is otherwise left alone (12.11.6). The intrinsic functions is_iostat_end(i) and
is_iostat_eor(i) (16.9.116 and 16.9.117) test a value without importing the constants.
If a condition occurs and the statement contains neither the matching specifier nor iostat=, the statement
initiates error termination (12.11.2 to 12.11.4). The program below triggers each condition in turn: an open of a
file in a directory that does not exist, a read of a number from text that is not a number, a loop that reads until
end of file, a non-advancing read of a record that is shorter than the field, and finally a read with no specifier at
all.
program io_status
use, intrinsic :: iso_fortran_env, only: iostat_end, iostat_eor
implicit none
integer :: unit, stat, k
real :: x
character(len=200) :: msg
character(len=*), parameter :: file = 'no_such_dir/heatflow.nml'
character(len=6) :: text = '3.5abc'
! 1. a file that cannot be opened
open (newunit=unit, file=file, status='old', action='read', iostat=stat, iomsg=msg)
print '(a, i0, a, a)', 'open: iostat = ', stat, ' iomsg = ', trim(msg)
! 2. a record that cannot be converted
read (text, *, iostat=stat, iomsg=msg) x
print '(a, i0, a, a)', 'bad real: iostat = ', stat, ' iomsg = ', trim(msg)
! 3. end of file: negative, and equal to iostat_end
open (newunit=unit, status='scratch', action='readwrite')
write (unit, '(a)') '1.5'
write (unit, '(a)') '2.5'
rewind (unit)
k = 0
do
read (unit, *, iostat=stat, iomsg=msg) x
if (stat == iostat_end) exit
if (stat /= 0) then
print '(a, i0, a, a)', 'read error: ', stat, ' ', trim(msg)
exit
end if
k = k + 1
end do
print '(a, i0, a, i0, a, l1)', 'read ', k, ' values; last iostat = ', stat, ', is iostat_end: ', stat == iostat_end
close (unit)
! 4. end of record in non-advancing input: negative, equal to iostat_eor
open (newunit=unit, status='scratch', action='readwrite')
write (unit, '(a)') 'abc'
rewind (unit)
read (unit, '(a5)', advance='no', iostat=stat, size=k) msg
close (unit)
print '(a, i0, a, l1)', 'short record: iostat = ', stat, ', is iostat_eor: ', stat == iostat_eor
! 5. no iostat= or err=: the error terminates the program
print '(a)', 'now reading without iostat='
read (text, *) x
print '(a)', 'not reached'
end program io_status
Output with gfortran 16.2:
open: iostat = 2 iomsg = Cannot open file 'no_such_dir/heatflow.nml': No such file or directory
bad real: iostat = 5010 iomsg = Bad real number in item 1 of list input
read 2 values; last iostat = -1, is iostat_end: T
short record: iostat = -2, is iostat_eor: T
now reading without iostat=
At line 46 of file 02_iostat.f90
Fortran runtime error: Bad real number in item 1 of list input
Error termination. Backtrace:
exit status: 2
Output with LLVM Flang 23:
open: iostat = 2 iomsg = No such file or directory
bad real: iostat = 1046 iomsg = invalid character (0x62) after list-directed input value, at column 5 in record 1
read 2 values; last iostat = -1, is iostat_end: T
short record: iostat = -2, is iostat_eor: T
now reading without iostat=
fatal Fortran runtime error(/work/02_iostat.f90:46): invalid character (0x62) after list-directed input value, at column 5 in record 1
Aborted
exit status: 134
What to take from the output:
-
The positive codes are as compiler specific as the allocation codes (2 and 5010 against 2 and 1046), and so are the messages. The values of
iostat_end(-1) andiostat_eor(-2) happen to be the same on both compilers but are documented as processor dependent: always compare with the constants oris_iostat_end. -
gfortran’s runtime error reports the file and the line of the failing statement and ends with the exit status 2. Flang aborts with a different message and exit status 134 (SIGABRT). Neither status is specified by the standard.
-
The pattern in step 3 is the idiomatic read loop: test for end of file first, then for any other error, and only then use the value. Testing
stat /= 0alone would treat the normal end of the data as an error. -
After an error condition the file position is indeterminate and the
readlist items become undefined (12.11.2): do not usexafter step 2.
The heatflow configuration reader follows the same pattern from the inside, passing the status of the two I/O
statements straight back to the caller:
open (newunit=unit, file=file, status='old', action='read', iostat=stat, iomsg=msg)
if (stat /= 0) return
read (unit, nml=heatflow, iostat=stat, iomsg=msg)
close (unit)
if (stat /= 0) return
read_config has stat and msg dummy arguments and never stops by itself; the application decides what a
configuration error means (Exit statuses and the calling script).
err=, end= and eor= with labels
The specifiers err=, end= and eor= name a statement label to branch to. They are not obsolescent in the standard and
they behave like iostat= for the branching part; they also suppress error termination. They carry no number and no
message unless iostat= and iomsg= are given too, and a branch to a label is awkward in structured code, so
iostat= is the style to prefer. A book that calls err= obsolescent is passing on its authors' recommendation, not the
standard’s list in Annex B.
program err_labels
implicit none
integer :: unit, n
real :: x
open (newunit=unit, status='scratch', action='readwrite')
write (unit, '(a)') '1.5'
write (unit, '(a)') 'oops'
rewind (unit)
n = 0
do
read (unit, *, err=90, end=80) x ! labels instead of iostat=
n = n + 1
end do
80 print '(a, i0, a)', 'end of file after ', n, ' values'
stop
90 print '(a, i0, a)', 'read error after ', n, ' values'
end program err_labels
read error after 1 values
gfortran and Flang print the same line.
Further rules of 12.11 worth knowing: an inquire whose unit is an internal file gives the positive constant
iostat_inquire_internal_unit; an iostat= in a flush statement can return a processor-dependent negative value (not
iostat_end or iostat_eor) when the unit does not support flushing; and the iostat=, iomsg= and size= variables
must not be associated with anything in the input/output list (12.12). Asynchronous, namelist and derived-type I/O add
their own conditions, covered in
Internal Files, Namelist and Non-Advancing I/O
and
Derived-Type and Asynchronous I/O.
Status arguments of intrinsic procedures
A handful of intrinsic procedures that talk to the operating system or the command line report the outcome through
optional status arguments instead of failing: get_command_argument (16.9.93), get_environment_variable (16.9.94)
and execute_command_line (16.9.83). The collective subroutines (co_sum and friends) and move_alloc take stat
and errmsg in the same way for coarray work. The calls below show the main values.
program intrinsic_status
implicit none
character(len=4) :: short
character(len=64) :: msg
integer :: stat, exitstat, cmdstat, len_needed
! get_command_argument: status 0, -1 (value too short) or a positive value (cannot retrieve)
call get_command_argument(1, short, length=len_needed, status=stat)
print '(a, i0, a, i0, a, a)', 'argument 1: status = ', stat, ', length = ', len_needed, ', value = ', short
! get_environment_variable: status 1 when the variable does not exist
call get_environment_variable('HEATFLOW_NO_SUCH_VARIABLE', short, status=stat)
print '(a, i0)', 'environment variable: status = ', stat
! execute_command_line: exitstat is the command's own status, cmdstat the status of running it
call execute_command_line('exit 3', exitstat=exitstat, cmdstat=cmdstat, cmdmsg=msg)
print '(a, i0, a, i0)', 'exit 3: exitstat = ', exitstat, ', cmdstat = ', cmdstat
msg = ''
call execute_command_line('/no/such/command', exitstat=exitstat, cmdstat=cmdstat, cmdmsg=msg)
print '(a, i0, a, i0, a, a)', 'missing command: exitstat = ', exitstat, ', cmdstat = ', cmdstat, ', cmdmsg = ', trim(msg)
end program intrinsic_status
Run with the single argument longer-argument, gfortran prints:
argument 1: status = -1, length = 15, value = long
environment variable: status = 1
exit 3: exitstat = 3, cmdstat = 0
sh: 1: /no/such/command: not found
missing command: exitstat = 127, cmdstat = 3, cmdmsg = Invalid command line
Flang prints the same first three lines and the same shell message, and ends with
missing command: exitstat = 127, cmdstat = 5, cmdmsg = Command not found with exit code: 127.. Source:
J3/23-007r1: 16.9.83 EXECUTE_COMMAND_LINE, 16.9.93 GET_COMMAND_ARGUMENT, 16.9.94 GET_ENVIRONMENT_VARIABLE
A status of -1 from get_command_argument means the value was truncated; length gives the size needed, which is how
a program sizes an allocatable character(len=:) buffer. exitstat and cmdstat answer two different questions: what
did the command return, and could it be run at all. A command-not-found shows cmdstat positive (3 or 5 here, processor
dependent) together with the shell’s own 127. When cmdstat is omitted and the call fails, the program error-terminates
(16.9.83).
Designing error reporting
The mechanisms above are for the statements the language gives you. Code you write has to decide how it reports errors to its callers. Fortran offers no standard exception type, so the design is a convention, and the choice matters more than in languages where the runtime enforces one.
| Design | Strengths | Weaknesses |
|---|---|---|
Integer status argument ( |
matches |
callers can ignore it silently; carries no message; codes must be documented |
Status plus message ( |
what |
two arguments everywhere; the message length is fixed by the caller |
Optional status: report if present, |
zero cost for callers that want to handle errors, safe default for those that do not; the convention used by |
|
Error derived type ( |
one argument; room for a location, a code and a text; |
not usable from |
Logical or integer function result |
reads naturally in an |
a function result cannot also be the value, so the value goes in an |
Named exit codes ( |
scripts and tests can recognize the failure; one place defines them |
must be kept in step with the documentation of the program |
Three conventions make any of them work well:
-
Libraries report, programs stop. A procedure in a module that other programs call should never call
stop. It returns a status, or stops only when the caller supplied no way to receive one. Themainprogram, the one place that knows what failure means for the user, decides on the message, the cleanup and the exit status. -
A programming error is not a status. A violated precondition (a negative array size, an unknown solver name) is a bug in the caller;
error stopwith a message names it and gives a nonzero status. Do not invent a status code for a condition the caller cannot sensibly handle. -
Pure code returns its status. A
pureprocedure cannot print orstop. It can return a status or, for a true bug, useerror stop(error stop in pure procedures).
The next example is a complete, small version of the optional-status convention for heatflow: a status type, a helper
that either fills the status or stops, and a validator that uses it. Calling validate_grid with a status handles the
error locally; calling it without one terminates with the grid-specific exit code.
module heatflow_status
use, intrinsic :: iso_fortran_env, only: error_unit
implicit none
private
public :: status_t, status_ok, status_fail, report
public :: exit_config, exit_grid, exit_io
integer, parameter :: exit_config = 2, exit_grid = 3, exit_io = 4 ! process exit codes
type :: status_t
integer :: code = 0 ! 0 means success
character(len=:), allocatable :: message
contains
procedure :: failed => status_failed
end type status_t
contains
pure function status_ok() result(s)
type(status_t) :: s
s%code = 0
s%message = ''
end function status_ok
pure function status_fail(code, message) result(s)
integer, intent(in) :: code
character(len=*), intent(in) :: message
type(status_t) :: s
s%code = code
s%message = message
end function status_fail
elemental logical function status_failed(self)
class(status_t), intent(in) :: self
status_failed = self%code /= 0
end function status_failed
!> Optional-status convention: report to the caller if it asked, otherwise stop.
subroutine report(s, status)
type(status_t), intent(in) :: s
type(status_t), intent(out), optional :: status
if (present(status)) then
status = s
else if (s%failed()) then
write (error_unit, '(a)') 'fatal: '//s%message
error stop s%code
end if
end subroutine report
end module heatflow_status
module heatflow_checks
use heatflow_status
implicit none
private
public :: validate_grid
contains
subroutine validate_grid(nx, ny, status)
integer, intent(in) :: nx, ny
type(status_t), intent(out), optional :: status
character(len=80) :: text
if (nx < 3 .or. ny < 3) then
write (text, '(a, i0, a, i0, a)') 'grid ', nx, ' x ', ny, ' is smaller than 3 x 3'
call report(status_fail(exit_grid, trim(text)), status)
else
call report(status_ok(), status)
end if
end subroutine validate_grid
end module heatflow_checks
program status_design
use heatflow_status
use heatflow_checks
implicit none
type(status_t) :: st
call validate_grid(21, 21, st)
print '(a, l1)', '21 x 21 failed: ', st%failed()
call validate_grid(2, 40, st) ! the caller handles the failure
print '(a, l1, a, i0, a, a)', ' 2 x 40 failed: ', st%failed(), ' code = ', st%code, ' message = ', st%message
print '(a)', 'now without a status argument'
call validate_grid(2, 40) ! nobody is listening: error stop
print '(a)', 'not reached'
end program status_design
Output with gfortran 16.2 (the program’s exit status is 3):
21 x 21 failed: F
2 x 40 failed: T code = 3 message = grid 2 x 40 is smaller than 3 x 3
now without a status argument
fatal: grid 2 x 40 is smaller than 3 x 3
ERROR STOP 3
exit status: 3
Flang 23 produces the same program output and exit status, with its own stop text, Fortran ERROR STOP: code 3.
Notes on the design: status is intent(out), so it is reset on every call, and present(status) is the whole
decision. Because error stop accepts a nonconstant integer (F2018) the exit code comes straight from the status.
report writes to error_unit first so that the message is the last thing the user reads before the process ends. The
type has an allocatable component, so status_fail and status_ok are pure but they allocate: if you need a status
inside a pure procedure that is called from a do concurrent loop, use plain integer codes instead.
stdlib: stdlib_error, check and state_type
The Fortran standard library stdlib (community project, not part of ISO Fortran; see
Fortran stdlib) ships the same convention as the stdlib_error
module. The version checked here is stdlib 0.8.1, where every entity in the module is marked experimental. It provides:
-
check(condition, msg, code, warn): ifconditionis false, printmsg(defaultCheck failed.) to stderr and stop with exit codecode(default 1), or only print whenwarnis true; -
error_stop(msg, code): a subroutine that performs anerror stopwith the message and the code (a procedure, so it can be passed around and wrapped); -
state_type: a derived type with an integer flag, a fixed-size message and a location string, with the type-bound proceduresok(),error(),print(),print_msg()andhandle(); a generic constructor of the same name builds a state from a location, a flag and a list of integer, real, complex or character values to put in the message; -
the flags
STDLIB_SUCCESS(0),STDLIB_VALUE_ERROR,STDLIB_LINALG_ERROR,STDLIB_INTERNAL_ERROR,STDLIB_IO_ERRORandSTDLIB_FS_ERROR(all negative), and comparison operators between a state and an integer flag.
The stdlib documentation describes this type on its "state_type" page (the spec file in the 0.8.1 repository is
stdlib_error_state_type.md, and the module is stdlib_error). The handle procedure is the
optional-status convention in one call: if an output state was passed it receives the state, otherwise an error state
stops the program.
module heatflow_load
use stdlib_error, only: state_type, STDLIB_VALUE_ERROR, STDLIB_IO_ERROR
implicit none
private
public :: parse_nx
contains
!> Read the grid size from text. With a state argument the routine reports; without one it stops.
integer function parse_nx(text, err0) result(nx)
character(len=*), intent(in) :: text
type(state_type), intent(out), optional :: err0
type(state_type) :: err
integer :: stat
read (text, *, iostat=stat) nx
if (stat /= 0) then
nx = -1
err = state_type('parse_nx', STDLIB_IO_ERROR, 'cannot read an integer from "'//text//'"')
else if (nx < 3) then
err = state_type('parse_nx', STDLIB_VALUE_ERROR, 'nx must be at least 3, got', nx)
end if
call err%handle(err0) ! copies to err0 if present, otherwise error stop
end function parse_nx
end module heatflow_load
program stdlib_error_demo
use stdlib_error, only: check, state_type
use heatflow_load, only: parse_nx
implicit none
type(state_type) :: err
integer :: nx
nx = parse_nx('21', err)
print '(a, i0, a, l1)', 'nx = ', nx, ', ok = ', err%ok()
nx = parse_nx('two', err)
print '(a, l1)', 'ok = ', err%ok()
print '(a)', err%print()
nx = parse_nx('2', err)
print '(a)', err%print()
call check(nx == 2, msg='nx should be 2 here') ! passes: silent
call check(nx > 100, msg='nx > 100 failed', warn=.true.) ! prints and carries on
print '(a)', 'now without a state argument'
nx = parse_nx('1') ! nobody listens: the program stops
print '(a)', 'not reached'
end program stdlib_error_demo
This was built with gfortran 16.2 (-std=f2023 and the usual warning options, no warnings) against stdlib 0.8.1, which
was built from the v0.8.1 tag with the project’s own preprocessing script and fpm. Output, with stderr and stdout
merged:
nx = 21, ok = T
ok = F
[parse_nx] returned I/O Error: cannot read an integer from "two"
[parse_nx] returned Value Error: nx must be at least 3, got 2
nx > 100 failed
now without a state argument
ERROR STOP [parse_nx] returned Value Error: nx must be at least 3, got 1
exit status: 1
state_type(location, flag, ...) joins the trailing values into the message, so nx is formatted without a format. The
warn=.true. call prints the message (through list-directed output, hence the leading blank) and goes on. The
error stop text is the printed state, and its exit status is 1, because the stop code is a character string. This example
was not built with Flang: the module files of a library are specific to the compiler that built it, and stdlib was built only
with gfortran here. One discrepancy in the documentation: the specification text lists the default of warn as .true., but the
source and a test run (call check(a == 5, msg='message')) stop the program with exit status 1, so the default is false.
stop and error stop
stop and error stop are the only statements that end a program on purpose. Their syntax is (R1162 to R1164):
stop [stop-code] [, quiet=scalar-logical-expr]
error stop [stop-code] [, quiet=scalar-logical-expr]
where stop-code is a scalar default character expression or a scalar integer expression of default kind (C1176).
Note the comma: with no stop code the quiet= specifier is written stop , quiet=.true., and stop quiet=.true. is a
syntax error on both compilers.
| Aspect | Rule | Clause |
|---|---|---|
Kind of termination |
|
5.3.7, 11.4 |
Stop code |
made available in a processor-dependent way. Recommended: an integer code is the process exit status; with a character code or no code, |
11.4 |
Output |
unless |
11.4 |
|
if present and true, no exception summary and no stop code are written (F2018) |
11.4, C.1 |
Nonconstant code |
any scalar default character or default integer expression, not only a constant (F2018) |
C.1 |
Range |
the integer code might be reduced by the processor, for example to its low 8 bits |
11.4 Note 2 |
Finalization |
entities existing at termination are not finalized |
7.5.6.4 |
Files |
on normal termination all connected units are closed (scratch files deleted); the standard does not say this for |
12.5.7.1 |
|
executing the |
5.3.7 |
error stop was introduced in Fortran 2008 (the GNU Fortran Fortran 2008 status page lists it as supported since 4.6),
and quiet=, the nonconstant stop code and error stop in pure procedures are Fortran 2018 (Annex C.1). The old pause
statement is gone: Annex B.1 lists it among the features deleted from Fortran 90, that is, in Fortran 95.
The cases below exercise every form once. The program takes the name of a case on the command line:
program stop_variants
implicit none
character(len=12) :: arg
integer :: code
call get_command_argument(1, arg)
code = 7
select case (trim(arg))
case ('end'); continue ! falls off end program: normal termination
case ('stop'); stop
case ('stop-int'); stop 3
case ('stop-char'); stop 'finished early'
case ('stop-var'); stop code + 1 ! nonconstant stop code (F2018)
case ('stop-quiet'); stop 3, quiet=.true.
case ('err'); error stop
case ('err-int'); error stop 4
case ('err-char'); error stop 'solver diverged'
case ('err-var'); error stop code ! nonconstant stop code (F2018)
case ('err-quiet'); error stop 4, quiet=.true.
case ('big'); stop 300 ! only the low 8 bits reach the shell
case default; print '(a)', 'usage: stop_variants <case>'
end select
end program stop_variants
The table shows what each case wrote to standard error and the exit status seen by the shell. The first output column is gfortran 16.2, the second Flang 23. Flang writes a blank line after the lines that give a code.
| Case | gfortran output | Exit | Flang output | Exit |
|---|---|---|---|---|
|
nothing |
0 |
nothing |
0 |
|
nothing |
0 |
|
0 |
|
|
3 |
|
3 |
|
|
0 |
|
0 |
|
|
8 |
|
8 |
|
nothing |
3 |
nothing |
3 |
|
|
1 |
|
1 |
|
|
4 |
|
4 |
|
|
1 |
|
1 |
|
|
7 |
|
7 |
|
nothing |
4 |
nothing |
4 |
|
|
44 |
|
44 |
Reading the table:
-
Character codes do not set the status.
stop 'finished early'exits with 0 anderror stop 'solver diverged'with 1, exactly as the standard recommends. If a script has to tell failures apart, use integer codes. -
The code is reduced to 8 bits by the shell. 300 became 44 (300 mod 256) on both compilers; stay within 1 to 125 so that the value cannot be mistaken for a shell or signal status (126, 127 and 128+n have special meanings).
-
quiet=removes the output, not the status. It is for programs that print their own message, and it is the way to keep a clean standard error when the exception summary would be noise. -
The text on stderr is processor dependent. gfortran prints
STOP code, Flang printsFortran STOP: code n. Do not parse it; use the exit status. -
A bare
stopdoes not mean success to the reader. Prefer falling offend programfor success and an expliciterror stopfor failure, so that the intent is visible.
Exception flags at termination
When quiet= is absent or false, a stop or error stop reports the IEEE exceptions that are signaling at that moment
(11.4). The program below divides by zero, prints the Infinity, and then stops with or without quiet=:
program ieee_at_stop
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
real(dp) :: zero, ratio
character(len=8) :: arg
call get_command_argument(1, arg)
zero = 0.0_dp
ratio = 1.0_dp / zero ! divide-by-zero flag is raised (no trap by default)
print '(a, es9.2)', 'ratio = ', ratio
if (arg == 'quiet') then
stop , quiet=.true.
else
stop
end if
end program ieee_at_stop
gfortran 16.2, run with the argument plain:
ratio = Infinity
Note: The following floating-point exceptions are signalling: IEEE_DIVIDE_BY_ZERO
exit status: 0
Flang 23, same argument:
ratio = Infinity
Fortran STOP
IEEE arithmetic exceptions signaled: DIVBYZERO
exit status: 0
With the argument quiet both compilers print only the first line. The exit status is 0 in every case: the summary is
information, not an error. Two compiler-specific details. gfortran lists every flag except inexact by default and
-ffpe-summary=list changes the list (none, all or a set of invalid, zero, overflow, underflow, inexact,
denormal); the manual says the summary is printed when stop or error stop is invoked. Flang 23 also includes the
inexact flag in its summary, as the pure-procedure example below shows. Source:
GNU Fortran manual: Options for debugging your program (-ffpe-trap, -ffpe-summary)
error stop in pure procedures
A pure procedure cannot contain a stop statement: stop is an image control statement (11.7.1) and C15109 forbids
those in pure subprograms, and a pure procedure may have no external input/output either (C15107, C15108). Fortran 2018 added the
exception: error stop is not an image control statement and is permitted in a pure subprogram (Annex C.1;
the GNU Fortran Fortran 2018 status page lists it too). That makes it the standard way to report a precondition
violation from the pure kernels and elemental functions that do concurrent requires.
module heatflow_guard
use, intrinsic :: iso_fortran_env, only: real64
implicit none
private
public :: stable_dt
integer, parameter :: dp = real64
contains
!> Largest stable FTCS time step; a nonpositive spacing is a programming error.
pure function stable_dt(alpha, dx, dy) result(dt)
real(dp), intent(in) :: alpha, dx, dy
real(dp) :: dt
if (alpha <= 0.0_dp .or. dx <= 0.0_dp .or. dy <= 0.0_dp) then
error stop 'stable_dt: alpha, dx and dy must be positive'
end if
dt = 0.25_dp * min(dx, dy)**2 / alpha
end function stable_dt
end module heatflow_guard
program pure_error_stop
use, intrinsic :: iso_fortran_env, only: real64
use heatflow_guard, only: stable_dt
implicit none
integer, parameter :: dp = real64
real(dp) :: dt(3)
integer :: i
dt = [(stable_dt(1.0e-2_dp, 0.05_dp * i, 0.5_dp), i=1, 3)]
print '(a, 3es11.3)', 'stable dt: ', dt
dt = [(stable_dt(1.0e-2_dp, 0.05_dp * (i - 2), 0.5_dp), i=1, 3)]
print '(a)', 'not reached'
end program pure_error_stop
stable dt: 6.250E-02 2.500E-01 5.625E-01
ERROR STOP stable_dt: alpha, dx and dy must be positive
exit status: 1
Flang 23 prints the same first line and Fortran ERROR STOP: stable_dt: alpha, dx and dy must be positive, and also
IEEE arithmetic exceptions signaled: INEXACT (it reports the inexact flag, which the arithmetic of the first line raised),
with exit status 1. In the third element i - 2 gives zero spacing, so the second statement stops.
A stop in the same position is rejected at compile time. This module is deliberately wrong:
module m
implicit none
contains
pure subroutine fail(msg)
character(len=*), intent(in) :: msg
if (len(msg) > 0) stop 1
end subroutine fail
end module m
gfortran 16.2:
06b_pure_stop_rejected.f90:6:28:
6 | if (len(msg) > 0) stop 1
| 1
Error: STOP statement not allowed in PURE procedure at (1)
Flang 23:
06b_pure_stop_rejected.f90:6:5: error: An image control statement may not appear in a pure subprogram
if (len(msg) > 0) stop 1
^^^^^^^^^^^^^^^^^^^^^^^^
Notice that the two diagnostics name different reasons for the same rule: gfortran names the statement and Flang the
class of statements it belongs to. The practical advice: use error stop for bugs in pure code and keep recoverable
conditions as returned status values, which pure code can do too.
Data left behind: files, buffers and finalization
On normal termination the standard closes every connected unit with status='keep' (status='delete' for scratch files)
and says nothing equivalent for error termination. Finalizers are not run in either case (7.5.6.4), so a final
subroutine cannot be relied on to write a restart file. The next program writes a line to a file and then ends with stop 1 (argument stop), with error stop 2 (argument error), or with error stop 2 after an explicit flush (argument flush); the shell then prints the file:
program unit_flush
implicit none
character(len=12) :: arg
integer :: unit
call get_command_argument(1, arg)
open (newunit=unit, file='log.txt', status='replace', action='write')
write (unit, '(a)') 'step 1 written'
if (trim(arg) == 'flush') flush (unit)
select case (trim(arg))
case ('stop'); stop 1
case default; error stop 2
end select
end program unit_flush
All three variants, with both compilers, left log.txt containing step 1 written and returned the exit status 1 or 2,
so both runtimes close open units on error stop. That is a courtesy of these runtimes, not a promise of the
standard. If a restart file matters, flush it (or close it) before the stop; and never rely on a final
procedure or on another image’s data being intact.
Exit statuses and the calling script
The exit status is what a build script, a batch scheduler or a test runner sees. The standard only recommends how a stop code maps to it (11.4), and process statuses are limited to 0 to 255 by the operating system. A workable scheme for an application:
| Exit status | Meaning in heatflow |
|---|---|
0 |
normal termination: |
1 |
an |
2 |
configuration cannot be read ( |
3 |
invalid grid ( |
4 |
output cannot be written ( |
The heatflow application turns a failed read_config into the second row. This is its real behavior, with a file
that does not exist (gfortran 16.2 built through fpm; the backtrace frames that follow the message are omitted):
call read_config(trim(file), cfg, stat, msg)
if (stat /= 0) then
write (*, '(a)') 'cannot read configuration: '//trim(msg)
error stop 2
end if
$ heatflow missing.nml
cannot read configuration: Cannot open file 'missing.nml': No such file or directory
ERROR STOP 2
Error termination. Backtrace:
$ echo $?
2
A calling script then tests the status:
if ! heatflow case-17.nml; then
status=$?
case $status in
2) echo "configuration problem in case-17.nml" ;;
3) echo "grid too small" ;;
*) echo "heatflow failed with status $status" ;;
esac
fi
Under fpm run the same failure is reported as Execution for object " heatflow " returned exit code 2, so the
status survives the package manager. Two more points. First, a status is for machines: also write a human-readable message
to error_unit (not to output_unit, which may be redirected into the results file). Second, in a program with several
images or MPI ranks the status of the whole job is decided by the launcher, not only by the code of one image.
The vendor extensions exit and abort
Compilers provide call exit(status) and call abort() as extensions. They are not part of any Fortran standard. They
exist in gfortran and in Flang, and legacy code calls them. With -std=f2023 gfortran treats them as ordinary external
procedures, and the link fails:
13_exit_abort.f90:(.text+0xf0): undefined reference to `exit_'
13_exit_abort.f90:(.text+0x14c): undefined reference to `abort_'
Without -std=f2023 (here -std=gnu) the program below runs, and gfortran gives status 5 for exit(5) and 134
(SIGABRT) for abort; Flang gives the same two statuses and prints a stack trace for abort.
program exit_abort
implicit none
character(len=8) :: arg
call get_command_argument(1, arg)
print '(a)', 'before'
if (trim(arg) == 'exit') call exit(5) ! vendor extension, not in the standard
if (trim(arg) == 'abort') call abort() ! vendor extension, not in the standard
print '(a)', 'after'
end program exit_abort
Source: GNU Fortran manual: EXIT
IEEE flags as an error channel
Floating-point arithmetic reports problems through the five IEEE exception flags rather than through status
arguments. By default a flag only records that something happened (the result is Infinity or NaN) and execution
continues. The module ieee_exceptions (also visible through ieee_arithmetic) lets a program read and clear the flags,
and ask for halting (17.3, 17.6). The program below provokes the three serious exceptions, reads the flags and clears
them. The full catalogue of rounding modes, ieee_value and ieee_class is on
Floating-Point and IEEE Arithmetic.
program ieee_flags
use, intrinsic :: ieee_arithmetic
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
type(ieee_flag_type), parameter :: watched(3) = [ieee_invalid, ieee_divide_by_zero, ieee_overflow]
character(len=14), parameter :: names(3) = [character(len=14) :: 'invalid', 'divide_by_zero', 'overflow']
logical :: raised(3)
real(dp) :: zero, ten, b, c, d
integer :: i
call ieee_set_flag(watched, .false.) ! start from a clean slate
zero = 0.0_dp
ten = 10.0_dp
b = 1.0_dp / zero ! divide by zero (b = +Infinity)
c = zero / zero ! invalid (c = NaN)
d = huge(1.0_dp) * ten ! overflow (d = +Infinity)
call ieee_get_flag(watched, raised)
do i = 1, size(watched)
print '(a, a, l1)', names(i), ' signaling: ', raised(i)
end do
print '(a, 3l2)', 'finite results (b, c, d): ', ieee_is_finite(b), ieee_is_finite(c), ieee_is_finite(d)
print '(a, l2)', 'c is NaN: ', ieee_is_nan(c)
print '(a, l2)', 'halting supported for divide_by_zero: ', ieee_support_halting(ieee_divide_by_zero)
call ieee_set_flag(ieee_all, .false.) ! clear every flag, so that stop reports none
stop 'flags cleared: no exception summary at termination'
end program ieee_flags
invalid signaling: T
divide_by_zero signaling: T
overflow signaling: T
finite results (b, c, d): F F F
c is NaN: T
halting supported for divide_by_zero: T
STOP flags cleared: no exception summary at termination
Flang 23 prints the same lines, with Fortran STOP: flags cleared: no exception summary at termination as the last one.
Source: J3/23-007r1: 17.3 The exceptions, 17.6 Halting, 17.11.5 IEEE_GET_FLAG, 17.11.39 IEEE_SET_FLAG
Where the program should react to a flag, the pattern is: clear the flags of interest, run the computation, read the
flags once, and decide. Reading a flag after every operation defeats vectorization; reading it per sweep of the solver
costs nothing. ieee_support_halting(flag) says whether halting can be switched on for the exception; with
ieee_set_halting_mode(flag, .true.) an exception then stops execution, but the standard says halting is "not precise" and
can occur at any time after the exception.
For the whole program, compilers offer the equivalent option. gfortran’s -ffpe-trap=invalid,zero,overflow enables the
traps for the three serious exceptions and turns the first occurrence into a SIGFPE. On the divide-by-zero program of the
previous section the result is (the message line is the shell’s, printed because the process died of a signal):
Floating point exception
exit status: 136
(136 is 128 + 8, the signal number of SIGFPE; with -fbacktrace, the default, gfortran prints a backtrace first.) The
GNU manual advises enabling at least those three traps unless the program provides for the exceptions and notes that by
default no trap is enabled; once enabled a trap can only be cleared with -ffpe-trap=none. Flang 23’s --help
listing has no equivalent option on the version tested: use ieee_set_halting_mode in the program.
Termination in coarray programs
A program with several images (Coarrays, Images and Synchronization) adds three rules to the picture (5.3.7, 11.4, 11.5):
-
Normal termination needs everyone. An image that executes
stopor reachesend programis stopped, but the program only terminates normally when all images have initiated normal termination. In three steps, initiation, synchronization and completion: the images synchronize at the second step so no image starts the completion step (which closes units) before all have finished initiation. The data of a stopped image stay available to the others. -
Error termination spreads. When one image initiates error termination (
error stop, or an error condition that the standard says causes it), the processor should terminate all the others as quickly as possible. -
An image can fail without terminating. The
fail imagestatement makes the executing image cease to participate without initiating termination, to test recovery code; on hardware that can detect a dead image the same state happens for real.failed_images(),stopped_images()andimage_status()(16.9.87, 16.9.199, 16.9.108) list them.
The stat= specifier of image control statements reports these states. It is the zero for success,
stat_stopped_image if the current team contains a stopped image, stat_failed_image if it contains a failed image and no
other error occurred, and a different positive value for other errors (11.7.11). Without stat= an error condition on an
image control statement initiates error termination. A program that wants to survive a stopped neighbour therefore writes
sync all(stat=s) and tests s.
The program below has image 2 stop or error-stop after a first sync all:
program coarray_termination
implicit none
character(len=12) :: arg
integer :: me, stat
character(len=64) :: msg
call get_command_argument(1, arg)
me = this_image()
print '(a, i0, a, i0)', 'image ', me, ' of ', num_images()
sync all
select case (trim(arg))
case ('error') ! image 2 error-terminates: every image must terminate
if (me == 2) error stop 'image 2 failed'
sync all
case ('stop') ! image 2 stops normally: the others may continue
if (me == 2) stop 'image 2 is done'
sync all (stat=stat, errmsg=msg)
print '(a, i0, a, i0)', 'image ', me, ': sync all stat = ', stat
end select
print '(a, i0, a)', 'image ', me, ' reached the end'
end program coarray_termination
This was built with OpenCoarrays 2.10.3 (caf and cafrun -n 4, which uses MPI) and with GCC 16.2’s native shared-memory
library (-fcoarray=lib with -lcaf_shmem). Flang 23 cannot build it: its multi-image support is experimental and the
link failed on missing runtime symbols of the PRIF interface, so Flang results are not given. The error case under
OpenCoarrays:
image 1 of 4
image 2 of 4
image 4 of 4
image 3 of 4
ERROR STOP image 2 failed
Abort(1) on node 1 (rank 1 in comm 0): application called MPI_Abort(MPI_COMM_WORLD, 1) - process 1
Error: Command:
`/usr/bin/mpiexec -n 4 /tmp/co2.out error`
failed to run.
exit status: 1
No image reached its last print, and the job’s exit status is 1: the whole job was terminated, as the standard
requires for error termination. The stop case under OpenCoarrays also ended the job with Abort(0) on node 1 ... MPI_Abort
and status 0, without the other images reaching the end: the standard allows the other images to carry on and the
runtime tested did not do that. With the shared-memory library the behavior was different again: in both the stop and
the error case the program printed the stop message and then did not end. The other three images were still waiting
for the next sync all when a 10 second timeout killed the run (status 124), in every one of three repeated stop runs
and two error runs, and the library printed ERROR: Image 2 ... failed with signal 0, exitstatus 1. in the error
case. The sync all (stat=stat) therefore never reported stat_stopped_image. Treat termination and failure detection in coarray programs as runtime-specific: test your
chosen runtime, set a timeout in the launcher, and prefer designs where no image waits for one that may have stopped.
Runtime checks and what happens without them
A program that violates a rule of the language is not an error in the sense of this page unless something detects it. Most violations are not detected by default: the standard places the burden on the programmer, and a conforming processor is not required to diagnose them. The common ones and their behavior with and without checking, for the program below, run once per case:
program checks_off
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
real(dp) :: u(10)
real(dp), allocatable :: v(:)
character(len=8) :: arg
integer :: i
call get_command_argument(1, arg)
u = 1.0_dp
i = 11
select case (trim(arg))
case ('bounds')
print '(a, f4.1)', 'u(11) = ', u(i) ! outside 1:10
case ('alloc')
print '(a, f4.1)', 'v(1) = ', v(1) ! v is not allocated
case ('dealloc')
deallocate (v) ! nothing to deallocate
end select
end program checks_off
| Case | gfortran with -fcheck=all |
gfortran without checks | Flang 23 |
|---|---|---|---|
|
runtime error, exit 2: |
prints |
prints |
|
runtime error, exit 2: |
|
|
|
runtime error, exit 2: |
the same runtime error, exit 2 |
silent, exit 0 |
gfortran’s runtime errors carry the file and line (At line 15 of file 07_checks_off.f90) and the text Fortran runtime error.
The first row shows the worst outcome: a bounds violation without checking gives a plausible number and no signal. The
second shows the second worst: a crash far from the cause. The third shows a difference between compilers on a rule the
standard states plainly (a deallocate of an unallocated allocatable is an error condition and error-terminates when
there is no stat=): gfortran reports it with or without -fcheck, Flang 23 does not.
What to do about it, in order of cost:
-
Develop with the checks on.
gfortran -fcheck=all -g -fbacktraceenables thebounds,array-temps,bits,do(modification of a loop variable),mem,pointerandrecursionchecks (options documented in the Code Gen Options page). The checks slow the program down, so release builds omit them. Flang 23 has no bounds-checking option in its--helplisting on the version tested, so run the test suite with gfortran, or with another compiler that has run-time checks, as well. -
Add
stat=where the program can recover, as above, so that a failure is a value rather than a death. -
Use the other tools that look for what the compiler does not:
valgrindfor invalid reads and leaks,-fsanitize=address,undefinedfor out-of-range accesses at run time and-ffpe-trapfor floating-point exceptions. They are covered with profilers and unit-test frameworks on Testing, Debugging and Profiling. -
Write assertions for invariants.
if (.not. condition) error stop 'message'costs one branch; a small helper, like the stdlibcheck, keeps the call site short. A precondition checked by an assertion in a hot loop can be disabled by a preprocessor flag in release builds (see Program Structure and Source Form for.F90andcpp).
Practical guidelines
-
Put
stat=(anderrmsg=) on everyallocatewhose size comes from input or from a computation that can be large. Test onlystat /= 0. -
Put
iostat=andiomsg=on everyopen,read,writeandclosethat touches a user’s file. Test foriostat_endfirst, then for any other error. Never test for a particular positive code. -
Give routines that can fail a status argument; make it
optionalwith anerror stopfallback if the routine is also used in simple programs. Do not callstopfrom library code. -
Use
error stopwith a message for bugs,stoporend programfor success and finished-early, and integer codes between 1 and 125 for failures a script must recognize. Character codes never change the exit status. -
Write failure messages to
error_unit. Flush or close files whose contents matter before anerror stop. -
In
pureprocedures useerror stop, neverstop; do not allocate a status in code that may be called from ado concurrentloop. -
Clear the IEEE flags before a computation whose exceptions you intend to inspect, and clear them again before a
stopif you do not want the summary; usequiet=.true.when the summary would be noise. -
Run the unit tests with
-fcheck=all(and-ffpe-trap=invalid,zero,overflow) on at least one compiler. -
Do not use
call exitorcall abortin code that has to build with-std=f2023.
Common pitfalls
-
Ignoring
stat. Astat=that is never tested is worse than none: the program neither stops nor reports, and uses the object that was not allocated. -
Comparing
iostatwith a number.if (stat == -1)works on today’s compilers;if (stat == iostat_end)is the portable form.stat < 0also catchesiostat_eor. -
Treating end of file as an error, or the reverse. The read loop needs the
iostat_endtest before the generic check. -
A stale
errmsg. It keeps its previous contents when no error occurs; clear it before use if you print it. -
Using a variable after an error. After a failed read the items are undefined and the file position is indeterminate (12.11.2); after a failed
allocatethe object keeps its earlier status. -
deallocateof an unallocated array. An error condition: guard withallocated. -
stopwith a character code and the expectation of a nonzero status. Only integer codes set the status. -
stop 256. The shell sees 0 (both compilers returned status 0 on the Linux container used). Keep the codes small. -
Expecting
finalprocedures or buffered output to survive a stop. Neither is guaranteed. -
A
stopinside a library routine. It kills a host program that could have recovered, and in a multi-image program it takes the other images with it or leaves them waiting. -
Writing the message to
output_unitand the data to the same unit. A failure message mixed into a redirected result file is easy to miss. -
Suppressing the summary with
quiet=and then forgetting that flags are still set. The flags stay signaling for later code that reads them. -
Relying on a coarray runtime’s reaction to a stopped image. It differs between OpenCoarrays, the native GCC 16 library and other compilers (Termination in coarray programs).
-
Using
-fcheck=allfor timing. The checks change the performance by large factors; time the release build.
Where older references differ
-
err=,end=andeor=are not obsolescent in Fortran 2023. A recommendation to avoid them is the book author’s style, not the standard’s.iostat=is preferred because it carries a value and does not need a label. -
stopwith a numeric code. Older texts describestopas taking only a constant string of digits or a character constant; since Fortran 2018 any scalar integer or default character expression is allowed (C.1), anderror stop(Fortran 2008) takes the same. -
quiet=is new in Fortran 2018 and absent from older books and from compilers that implement only Fortran 2008. -
error stopin pure procedures is Fortran 2018. Older code works around the ban by returning a status from everywhere. -
pauseis a deleted feature (Fortran 95, Annex B.1); a program that pauses for the operator should read from input instead. -
Image failure and
stat_failed_imageare Fortran 2018 (they came from the coarray technical specification); a program written for Fortran 2008 coarrays has no way to survive a failed image. -
The vendor extensions
exitandabortappear in many tutorials as if they were standard; they are not (see above). -
Exception handling by IEEE flags reads differently from IEEE 754-2008 references: the standard now follows IEEE 754-2019 (ISO/IEC 60559:2020), see Floating-Point and IEEE Arithmetic.
See also Best Practices and Style and Common Gotchas and Pitfalls, and, for the I/O and allocation statements themselves, Input/Output: Units and Files and Allocatable Arrays and Dynamic Memory.
References
-
GNU Fortran manual: Options for debugging your program (-ffpe-trap, -ffpe-summary, -fno-backtrace) and Options for code generation conventions (-fcheck)
-
GNU Fortran manual: Fortran 2008 status and Fortran 2018 status
-
GNU Fortran manual: Runtime environment variables (GFORTRAN_UNBUFFERED_ALL)
-
Fortran stdlib: stdlib_error specification and state_type specification