Program Structure and Source Form
|
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, final draft J3/23-007r1). The examples were compiled and run with
gfortran 16.2 under -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all (warning-free unless the text says
otherwise) and, where noted, with LLVM Flang 23, on 2026-10-18. It covers the layer below the statements: which pieces a
program is built from, the order statements must appear in, and the rules for turning lines of text into tokens. The
statements themselves are covered on the pages that follow, starting with
Intrinsic Types and Kinds.
The examples use the heatflow 2D heat-diffusion scenario of this section (module names such as heatflow_kinds). If you
have not yet compiled a Fortran program, start with Getting Started.
Program units and scoping units
A Fortran program is a set of program units, each of which can be compiled separately (S, clauses 5 and 14). There are five kinds:
| Program unit | Role | Delimited by |
|---|---|---|
Main program |
Where execution starts. Exactly one per executable program. The |
|
External subprogram |
A |
|
Module |
A library of definitions: constants, types, variables, procedures, generics. The unit of encapsulation. |
|
Submodule |
Holds the bodies of separate module procedures declared in a parent module, so changing a body does not recompile users. |
|
Block data |
Initializes named |
|
Modules and submodules are the way to organise modern code, and the main program is usually short. Their rules are on
Modules and Submodules. Subprograms that live inside
another unit after a contains statement are module procedures (inside a module or submodule) or internal procedures
(inside a main program or another subprogram). An internal procedure can itself not contain further internal procedures,
which is why containment is only ever two levels deep.
A scoping unit is the region in which a name has one meaning (S, clause 19). It is one of a program unit or subprogram
(excluding the internal procedures it contains), a derived-type definition, an interface body, or a block construct. A name
declared in an enclosing scoping unit is visible in an inner one through host association; a module’s contents are visible
elsewhere only through use (use association). This is the reason an internal procedure can read the variables of its host
without arguments, and the reason implicit none must be repeated in each module and external subprogram but is inherited by
internal procedures.
The next example has two modules and a main program with an internal function. The internal function half reads nothing
from its host, but dx and dt would be visible to it.
module heatflow_kinds
use, intrinsic :: iso_fortran_env, only: real64, int32
implicit none
private
public :: dp, ip
integer, parameter :: dp = real64
integer, parameter :: ip = int32
end module heatflow_kinds
module heatflow_constants
use heatflow_kinds, only: dp
implicit none
private
public :: default_alpha, default_tolerance
real(dp), parameter :: default_alpha = 1.0e-2_dp
real(dp), parameter :: default_tolerance = 1.0e-6_dp
contains
pure function diffusion_number(alpha, dt, dx) result(r)
real(dp), intent(in) :: alpha, dt, dx
real(dp) :: r
r = alpha*dt/dx**2
end function diffusion_number
end module heatflow_constants
program units_demo
use heatflow_kinds, only: dp
use heatflow_constants, only: default_alpha
implicit none
real(dp) :: dx, dt
dx = 0.1_dp
dt = half(dx)
print '(a, es10.3)', 'dt = ', dt
print '(a, es10.3)', 'alpha = ', default_alpha
contains
! An internal subprogram: it sees dx and dt by host association.
pure function half(x) result(h)
real(dp), intent(in) :: x
real(dp) :: h
h = 0.5_dp*x
end function half
end program units_demo
dt = 5.000E-02
alpha = 1.000E-02
Points that follow from the example:
-
Program units can appear in one file or in many, in any order in the file, but a compiler that creates
.modfiles needs a module before the units thatuseit. This is the build-order problem that Building with Make, CMake and Meson and Fortran Package Manager (fpm) solve. -
The file name has no meaning to the language. By convention one module lives in one file named after it.
-
The main program is recognised by its position as the only unit that is neither a module nor a subprogram. Its name is not visible to other units and is not required; a program with no
programstatement ends with a plainend. -
The name on an
endstatement is optional but, when present, must match; writing it is the best defence against mismatched nesting in long units.
Statement order
Within a scoping unit the standard fixes the order of statement kinds (S, clause 5, "Statement order"). Reading it as a sequence of parts:
| Part | Contains | Notes |
|---|---|---|
1 |
|
Absent for a main program that has no |
2 |
|
All |
3 |
|
After all |
4 |
Declarations: type declarations, derived-type definitions, interface blocks, |
|
5 |
Executable statements and constructs |
Includes |
6 |
|
Each procedure is a complete scoping unit with the same rules. |
7 |
|
|
The rule is the same in every unit, and the compiler enforces it. The next program is deliberately ill-formed: it places a
use statement after a declaration. It is shown only to illustrate the diagnostic, and its source is not meant to compile.
! Intentionally ill-formed: a USE statement after a declaration.
program order_error
implicit none
integer :: n
use, intrinsic :: iso_fortran_env, only: real64
real(real64) :: x
n = 1
x = n
print *, x
end program order_error
07_order_error.f90:5:50:
4 | integer :: n
| 2
5 | use, intrinsic :: iso_fortran_env, only: real64
| 1
Error: USE statement at (1) cannot follow data declaration statement at (2)
07_order_error.f90:6:14:
6 | real(real64) :: x
| 1
Error: Symbol 'real64' at (1) has no IMPLICIT type
07_order_error.f90:8:4:
8 | x = n
| 1
Error: Symbol 'x' at (1) has no IMPLICIT type
Only the first error is the real one. The other two are consequences: because the use was rejected, real64 and x were
never declared, and implicit none then complains. LLVM Flang 23 reports the same fault as error: misplaced USE statement.
When a compiler produces a cascade of diagnostics, fix the first and rebuild.
The character set, tokens and names
Fortran source is a sequence of characters from the processor character set, which includes the Fortran character set (S 6.1):
-
the 26 letters, treated as the same letter in upper and lower case everywhere outside character context;
-
the 10 digits and the underscore, which together with the letters make up the alphanumeric characters;
-
the special characters of Table 6.1: blank,
=,+,-,*,/,\,(,),[,],{,},,,.,:,;,!,",%,&,~,<,>,?,', the grave accent,^,|,$,#and@. Fortran 90 added!,",%,&,;,<,>,?and$to the Fortran 77 set, and Fortran 2003 added the rest (the backslash, brackets and braces,~, the grave accent,^,|,#and@). Not every special character has a meaning in the syntax:[and]delimit array constructors and cosubscripts, while?and@had no syntactic role until Fortran 2023, which uses@to introduce a multiple subscript (9.5.3.2), see Generic and Rank Independent Programming, and?as the conditional-expression separator (10.1.2.3).
Inside character literals and comments any character the processor supports may appear, including non-ASCII ones, but how non-default characters are stored depends on the character kind (Characters and Strings).
Source text is split into tokens (S 6.2): names, keywords, literal constants, operators, statement labels and separators. Rules worth remembering:
-
A name has between 1 and 63 characters, begins with a letter, and continues with letters, digits and underscores (
nx,max_steps,u_new). Names are case-insensitive:Grid,GRIDandgridare one name, and diagnostics may show it in either case. A name may not contain$or-, which some compilers accept as an extension. -
Keywords are not reserved. The same word can be a statement keyword in one place and a variable name in another, because the grammar decides from context. This is legal:
program not_reserved implicit none integer :: if, then, data, end_count real :: real ! Legal but never a good idea: Fortran keywords are not reserved words. if = 1 then = 2 data = 3 real = 4.5 end_count = if + then + data if (end_count > 5) then print *, 'sum is', end_count, 'real is', real end if end program not_reservedsum is 6 real is 4.50000000The program is standard-conforming and compiles cleanly, but shadowing
realmakesreal(x, dp)impossible in that scope, anddata = 3is hard to read. Treat the property as an explanation of why old code compiles, not as a licence. Source: J3/23-007r1 (Fortran 2023), clause 6.2 "Low-level syntax" -
Blanks separate tokens and are significant in free form: no blank may appear inside a name, keyword or literal, and a blank (or another separator) must separate adjacent names and keywords, with a few exceptions such as
end if/endifandgo to/goto,else if/elseif,end do/enddo, whose spaces are optional. -
Statement labels are 1 to 5 digits (not all zero) at the start of a statement. In free form the label is followed by a blank. Modern code needs them only for
formatstatements and the obsolescent labelleddo. -
Operators are symbols or dot-delimited words, for example
+,**,//,==,/=,.and.,.not.. The old dotted relational operators (.eq.,.lt., …) are still standard; they are discouraged only by the book authors' style recommendations, not removed.
Free source form
Free form is the default for all code written today. The rules (S 6.3.2):
-
A comment starts with
!outside a character literal and runs to the end of the line. A line that contains only a comment, or only blanks, is a comment line and may appear anywhere, including between the lines of a continued statement. -
A statement normally occupies one line. To continue it, end the line with
&; the statement resumes on the next line that is not a comment line. A comment may follow the&. -
A token can be split across lines with an
&at the end of the first line and an&as the first non-blank character of the continuation line. This is also how a character literal is continued without picking up the blanks before the continuation: the literal continues after the second&, and anything between the leading blanks and that&is ignored. If the continuation line has no leading&, a split literal includes the leading blanks of that line. -
;separates several statements on one line. A trailing;or a;right after a label is a null statement. -
A statement label can precede any statement; an empty line has no meaning.
program source_form
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
real(dp) :: total, alpha_value, beta_value ! trailing comment
integer :: i; integer :: n ! two statements on one line
character(len=*), parameter :: msg = 'a long character literal that is &
&continued, with an ampersand at the start of the next line'
character(len=*), parameter :: tail = "split&
& literal"
n = 3
alpha_value = 1.5_dp
beta_value = 2.5_dp
total = alpha_value & ! the comment may follow the ampersand
+ beta_value &
* real(n, dp)
print *, total
print *, msg
print *, tail
! A token (here a name) may itself be split with two ampersands.
i = 1
print *, tota&
&l, i
end program source_form
9.0000000000000000
a long character literal that is continued, with an ampersand at the start of the next line
split literal
9.0000000000000000 1
The first value shows operator precedence working across the continuation: 2.5 * 3 is evaluated before the addition. The
last print shows a split name: tota& joined to &l is the variable total. LLVM Flang 23 accepts the same file and prints
9. for the real value, because list-directed output of reals is processor-dependent
(Format Specifications and Edit Descriptors).
Line and statement limits: Fortran 2023 versus before
Older standards limited a free-form line to 132 characters and a statement to 255 continuation lines; fixed form allowed 72 significant columns. Fortran 2023 raises the limits (S 6.3, also described in the "new features" paper):
| Limit | Fortran 2018 and earlier | Fortran 2023 |
|---|---|---|
Characters per free-form line |
132 |
10,000 |
Continuation lines per statement |
255 (39 in Fortran 95) |
no fixed limit |
Characters per statement |
determined by the two limits above |
1,000,000 |
Characters per fixed-form line |
72 significant |
unchanged |
Compilers do not all follow the standard’s numbers. gfortran limits lines according to the standard it is asked to follow:
under -std=f2018 a longer line is rejected, while under -std=f2023 (and with no -std option) it is accepted. The option
-ffree-line-length-n (or -ffree-line-length-none) overrides the limit explicitly. The next program has a line longer
than 132 characters.
program long_line
implicit none
integer :: total
! The next line is longer than 132 characters.
total = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
print *, total
end program long_line
Compiled with -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all it runs:
40
Compiled with gfortran -std=f2018 the same file fails, which is the behaviour code must survive when it targets an older
compiler:
09_long_line.f90:6:132:
Error: Line truncated at (1) [-Werror=line-truncation]
Adding -ffree-line-length-none makes the Fortran 2018 build print 40 as well. LLVM Flang 23 compiles the file without
options. The practical advice is unchanged by the larger limit: keep lines under about 100 characters and use &, so code
stays readable and portable to older compilers.
Fixed source form (recognition only)
Fixed form is the layout of FORTRAN 77 and earlier, built for punched cards. It still appears in old libraries, usually in
files with the extension .f, .for or .ftn. It is described here only so that you can read it; new code should use free
form. The rules (S 6.3.3):
-
Columns 1-5: an optional statement label. A
C,c,*(or!) in column 1 makes the line a comment. -
Column 6: any character other than blank or
0marks this line as a continuation of the previous one. -
Columns 7-72: the statement. Text beyond column 72 is ignored by the standard (many compilers warn about this).
-
Blanks are not significant outside character literals:
DO 10 I = 1, 4andDO10I=1,4mean the same. The notorious consequence is thatDO10I = 1.4is an assignment to a variable calledDO10I.
C Fixed form: comment in column 1, labels in columns 1-5,
C continuation mark in column 6, statements in columns 7-72.
PROGRAM FIXED
IMPLICIT NONE
INTEGER I, TOTAL
TOTAL = 0
DO 10 I = 1, 4
TOTAL = TOTAL +
& I
10 CONTINUE
PRINT *, TOTAL
END
10
This file is deliberately legacy. Compiled with gfortran 16.2 under -std=f2023 -Wall it succeeds, with one warning that
names the labelled do as an obsolescent feature; it is the only file on this page that does not build warning-free
under the strict options. gfortran picks the form from the extension: .f is fixed, .f90 is free, and
-ffree-form/-ffixed-form override the choice. A file can only be one form: you cannot mix them. The replacements
for each fixed-form idiom are on
Legacy Features and Modernization.
Include lines
An include line (S 6.4) is the only text-inclusion mechanism in the standard:
include 'grid_params.inc'
It must appear on a line by itself (apart from a trailing comment); the processor replaces the line with the contents of
the named file, which can contain include lines of its own. The standard does not say where the file is searched for.
gfortran searches the directory of the including file, then the directories given with -I.
program include_demo
implicit none
include 'grid_params.inc'
print *, 'nx, ny =', nx, ny
end program include_demo
Where grid_params.inc contains the single line:
integer, parameter :: nx = 64, ny = 32
nx, ny = 64 32
Include files were how pre-Fortran-90 code shared declarations and common blocks. Today a module does the same job
with checking: the declarations are compiled once, a use brings them in, interfaces are explicit, and a change forces
dependants to rebuild only if the interface changed. The book authors list include among the features they discourage;
this is their recommendation, not the standard’s, as include is neither obsolescent nor deleted. It remains useful for
generated tables, and for the pre-module pattern of one body file included several times with different kinds (see
Generic and Rank Independent Programming).
The preprocessor
Fortran 2023 contains no preprocessor. #ifdef, #define, #include and __FILE__ are a convention that compilers
provide as an option, usually by running the C preprocessor in a traditional mode that does not mangle Fortran text.
Compilers decide whether to preprocess by the file extension:
| Compiler | Behaviour |
|---|---|
gfortran |
Preprocesses files with an upper-case extension ( |
LLVM Flang |
Uses its own built-in Fortran-aware preprocessor for upper-case extensions, and |
Intel ifx |
|
program preprocessor_demo
implicit none
#ifdef USE_DOUBLE
integer, parameter :: wp = kind(1.0d0)
#else
integer, parameter :: wp = kind(1.0)
#endif
real(wp) :: x
x = 1.0_wp/3.0_wp
print '(a, i0, a, f0.6)', 'kind = ', wp, ', x = ', x
print '(a, a, a, i0)', 'file ', __FILE__, ' line ', __LINE__
end program preprocessor_demo
Saved as 05_preprocessor.F90, compiled as usual, the program prints:
kind = 4, x = .333333
file 05_preprocessor.F90 line 12
and compiled with -DUSE_DOUBLE the first line becomes kind = 8, x = .333333. LLVM Flang 23 gives the same two lines.
Because preprocessing is outside the standard, three cautions apply: the macro language of another tool (#define function
macros, token pasting) may not behave identically across compilers; a preprocessed file loses its original line numbers
unless the preprocessor emits line markers; and standard checking tools see only the preprocessed text. Prefer
real language features (modules, generics, kind parameters from iso_fortran_env) and reserve conditional compilation for
platform and library differences. The C counterpart of this discussion is on
Preprocessor and Macros in the C reference.
The Flang preprocessing documentation is at flang.llvm.org: Preprocessing.
stop and end program
A program ends in one of two ways. Reaching the end statement of the main program is normal termination. A stop
statement, which may appear anywhere in a program, also initiates normal termination. It takes an optional stop code,
an integer or a constant character expression, which the processor reports. The exit status is processor-dependent in
general, and a nonzero integer code is conventionally used as the process exit status.
program stop_demo
implicit none
integer :: remaining
remaining = 0
print *, 'before stop'
if (remaining == 0) stop 3
print *, 'never printed'
end program stop_demo
before stop
STOP 3
gfortran 16.2 writes STOP 3 to standard error and exits with status 3 (the shell variable $?). The stop code can also
be a character string, stop 'finished'. The related error stop statement requests error termination and is
preferable when a program must fail with a nonzero status; the quiet= specifier, failed images, exit and the
interaction of stop with coarray images are covered on
Error Handling and Program Termination.
A library routine should not call stop, since that removes the caller’s chance to react; return an error status instead.
Where the books are outdated
The comparison below is against Modern Fortran Explained: Incorporating Fortran 2023 (MRCB), which is the most recent of the books consulted for this section. Its chapter on source form describes the F2018 limits and does not forward-reference the F2023 changes made later in its own text.
| Older statement | Current position (checked 2026-10-18) |
|---|---|
Free form is limited to 132 characters per line and 255 continuation lines. |
Fortran 2023: 10,000 characters per line, no continuation limit, 1,000,000 characters per statement. Compilers follow the
limit of the |
The special characters are those of Fortran 2018. |
Fortran 2023 adds |
Source form and statement order are the whole story. |
In practice a build also needs the module build order, |
|
That is the authors' recommendation. The standard still defines |