Getting Started

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, text of the final draft J3/23-007r1). Every program on it was compiled and run with GNU Fortran 16.2 (-std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all) and, where the page says so, with LLVM Flang 23, on 2026-10-18. It is the first stop of this reference: it shows what the language is for, how to get a working toolchain, how a Fortran program becomes an executable, how a small project is organised with the Fortran Package Manager (fpm), and how to read what the compiler and the runtime tell you when something is wrong. The language itself starts on Program Structure and Source Form; the history of the standard and the compiler landscape are on History, Standards and Compilers.

Throughout the section the examples follow one small original scenario, heatflow: a program that computes how heat spreads on a rectangular plate. It starts here as a 30-line kernel inside a three-file fpm project and grows, page by page, into modules, derived types, submodules, namelist configuration, parallel variants and bindings to C, Python and MATLAB.

What Fortran is used for today

Fortran (the name comes from FORmula TRANslation) was created in 1957 and is still the language in which a large part of the world’s numerical software is written and, more importantly, still being written. It is a compiled, statically typed, array-oriented language standardized by ISO/IEC, designed so that a compiler can turn mathematical formulas over large arrays into fast machine code.

Area Typical use

Weather, climate and ocean models

Numerical weather prediction and climate models integrate partial differential equations on global grids for millions of time steps on supercomputers. Many operational and research codes are Fortran and are extended every year.

Computational fluid dynamics and engineering

Finite-difference, finite-volume and finite-element solvers for flow, structures and combustion, often with decades of validated physics inside them.

Chemistry, physics and materials

Electronic-structure, molecular-dynamics, plasma, astrophysics and nuclear codes, with new codes written in modern Fortran next to established ones.

Numerical libraries

The reference implementations of BLAS and LAPACK, the dense linear algebra libraries that NumPy, MATLAB, R and Julia call into, are Fortran. Vendor-tuned BLAS and LAPACK keep the same Fortran-style interfaces.

HPC benchmarks and parallel programming

Benchmark suites such as the NAS Parallel Benchmarks have Fortran versions, and the language has native parallel features (coarrays and do concurrent) besides its use with MPI, OpenMP and OpenACC.

Four properties explain why the language keeps this role.

Arrays are first-class values

Whole-array expressions, array sections, the where construct, reductions such as sum and maxval, and array-returning intrinsics are part of the language, not a library. The compiler sees the intent of u = 0.25_dp*(a + b) instead of a loop it has to analyse. The array chapters start at Arrays Fundamentals.

No aliasing by default

The standard forbids a procedure to modify, through one dummy argument, data that it can also reach through another dummy argument or a global (J3/23-007r1, 15.5.2.14), and pointers must be declared with the pointer or target attribute to alias anything. A compiler can therefore keep values in registers and vectorize loops without the restrict annotations C needs. The price is that the programmer, not the compiler, is responsible for never violating the rule.

Mature optimizing compilers

Compilers for the language have been tuned for numerical code for nearly seventy years, and the open-source ones (GNU Fortran and LLVM Flang) share their back ends with the C and C++ compilers.

A stable standard

A conforming Fortran 77 or Fortran 90 program is still accepted by current compilers, because the committee deletes features very rarely and marks the ones it wants to retire as obsolescent first. A numerical code can outlive many hardware generations. Parallel features were added to the language itself: coarrays in Fortran 2008 and do concurrent with its reduce locality specifier in Fortran 2023.

Where the books this section draws on are outdated: they present the language, not the ecosystem. Modern Fortran Explained (Fortran 2023 edition) contains no installation, compiler-invocation or package-manager guidance, so everything from the next section on comes from the compiler manuals and the fortran-lang.org tutorials.

Installing a compiler

The standard describes the language, not a command line, so every compiler has its own driver, options and module-file format. Four are free to use and appear in this reference.

Compiler Command Notes

GNU Fortran (gfortran), part of GCC 16.2

gfortran

Mature, free, available on Linux, macOS and Windows. -std=f2023 selects the Fortran 2023 language level and rejects extensions. GCC 16 added native shared-memory coarrays and teams. There is no per-feature Fortran 2023 status page in the manual, so the support table on History, Standards and Compilers is built from the release notes and test results.

LLVM Flang, from LLVM 23

flang (the Debian package of this reference ships it as flang-23)

The LLVM project’s Fortran compiler. The executable was called flang-new for several releases and is now flang. It implements Fortran 2018 except multi-image (coarray) features, which are experimental, and the per-feature Fortran 2023 table is on the Flang standards-support page.

Intel ifx (oneAPI)

ifx

The LLVM-based Intel compiler. The older ifort has been discontinued, although the fortran-lang.org compiler list still shows it as current.

LFortran

lfortran

An interactive compiler built on its own intermediate representation. Its home page describes it as alpha: it is not expected to compile third-party codes yet. It also offers a WebAssembly build that runs in the browser, which is the quickest way to try a few statements without installing anything.

Commercial compilers that are not used in this reference but are widely deployed on clusters are NAG Fortran, the HPE Cray Compiling Environment, NVIDIA’s nvfortran (the compiler that offloads do concurrent to GPUs) and AMD’s and IBM’s LLVM-based compilers. The community list is on fortran-lang.org: Compilers; treat its entry for ifort as stale.

Installing GNU Fortran

The fortran-lang.org installation page lists the options per platform. On the common ones:

# Debian, Ubuntu, Mint
sudo apt install gfortran

# Fedora, RHEL and derivatives
sudo dnf install gcc-gfortran

# Arch and derivatives
sudo pacman -S gcc-fortran

# macOS with Homebrew (gfortran is part of the gcc formula)
brew install gcc

# Check what you got
gfortran --version

On Windows the fortran-lang.org page recommends the Windows Subsystem for Linux for a Unix-like workflow and also lists MinGW-w64 and MSYS2 builds. The version numbers quoted on that page (GCC 10 to 12) are old: package managers usually offer a newer release, so run gfortran --version and read the release notes of what you have before assuming a feature is available.

Installing LLVM Flang and the others

Flang is distributed with the LLVM releases and by several Linux distributions; the examples of this reference were also checked with the Debian-based flang-23 package from apt.llvm.org (a post-release build of LLVM 23), which needs the LLVM compiler-rt and OpenMP development packages next to the compiler for linking. Intel ifx comes with the oneAPI HPC toolkit. LFortran has its own installation page. In every case the check is the same two lines:

flang --version
ifx --version

The program names, the options and the Fortran 2023 level differ between these compilers, and a program that one accepts another may reject. A portable program is therefore compiled with at least two of them; the heatflow examples of this reference are always checked with gfortran and Flang.

Editors and the fortls language server

For a list of IDEs, editors and tools with links to their official pages, see IDEs & Tools.

Any text editor works: Fortran sources are plain text. The fortran-lang.org guide lists Emacs, Vim and Neovim, Sublime Text, Notepad++ and Visual Studio Code, and, for full IDEs, Code::Blocks, Eclipse with Photran, Geany, Visual Studio with the Intel compiler, and the commercial NAG, Absoft, Plato and Simply Fortran. Several of the names on those pages are no longer maintained (the Atom editor, for example), so prefer the editors that have a language-server client.

The community language server is fortls. It provides completion, hover documentation, go-to-definition, find-references, rename, signature help and diagnostics, and it understands the module structure of a project. It is a Python package:

pip install fortls

Visual Studio Code users install the Modern Fortran extension (published by fortran-lang), which provides syntax highlighting, linting, language-server support (through fortls) and formatting; other editors (Neovim with its built-in LSP client, Emacs with eglot or lsp-mode, Helix, Sublime Text with LSP) configure fortls as the server for the fortran file type. fortls reads project settings from a .fortls file at the project root. The tools around the compiler (formatters, documentation generators, linters) are covered on Documentation and Editor Tooling.

"Hello, world!"

The smallest useful program has a program statement, the specification part (here just implicit none), one executable statement and end program. Save it as 01-hello.f90.

program hello
    implicit none
    print '(a)', "Hello, world!"
end program hello

Compile and run it with any of the compilers:

gfortran 01-hello.f90 -o hello
flang 01-hello.f90 -o hello
ifx 01-hello.f90 -o hello
./hello
Hello, world!

The program compiles without warnings under gfortran -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all and produces the same output with Flang 23.

What each line does:

  • program hello starts the main program (J3/23-007r1, 14.1) and names it. Every executable has exactly one main program. The name has no other use; the matching end program hello repeats it so a compiler can check the pairing.

  • implicit none switches off implicit typing. Without it, an undeclared name that starts with i to n is silently an integer and any other is a real, so a misspelled variable becomes a new, uninitialised one (J3/23-007r1, 8.7). Every program unit in this reference starts with implicit none. It is also available as implicit none (type, external), which additionally requires explicit interfaces for external procedures; both forms are described with the rest of the declarations on Variables, Declarations and Attributes. Fortran 2028 marks default implicit typing obsolescent.

  • print '(a)', "Hello, world!" writes to the default output unit. The first item is a format: '(a)' means "one character item, as is". print *, "Hello, world!" uses list-directed output instead, which lets the compiler choose the layout and starts the line with one blank in practice (the second program below shows it). Formats are covered on Format Specifications and Edit Descriptors.

  • Fortran is case-insensitive: PROGRAM Hello, program hello and Print are the same. In free source form blanks separate tokens but are otherwise not significant, and a line may hold up to ten thousand characters (J3/23-007r1, 6.3.2.1), far more than the 132 characters older texts quote.

  • ! starts a comment that runs to the end of the line. There is no statement terminator: the end of the line ends the statement and & at the end of a line continues it.

The commands above are the whole build: the compiler reads one file, produces an object file, calls the linker and removes the intermediate files. The rest of this page is about what that hides.

How a program is built

Build pipeline of a Fortran program. A solver.F90 source goes through the preprocessor to become solver.f90, or a lowercase .f90 skips the preprocessor. The compiler turns each source file into an object file and, for a module, a .mod file that every file with a use statement must find before it is compiled. The linker joins the object files and the runtime library, libgfortran or libflang_rt, into an executable. Coarray and MPI programs are started with cafrun or mpirun.

The stages are, for one source file:

  1. Preprocessing is optional and, unlike in C, not part of the language. The standard does not define a preprocessor, only the optional include line. Compilers run a C-style preprocessor (cpp) when the file name suggests it, typically a capital F in the extension, or when asked with an option such as -cpp.

  2. Compilation parses and checks the source, optimizes it and generates machine code into an object file (.o, or .obj on Windows). When the file contains a module or submodule, the compiler also writes a module file (.mod) that records the public declarations of the module. A file with use heatflow_kinds cannot be compiled until heatflow_kinds.mod exists, which is why the build order matters and why build tools such as fpm scan the sources for use statements.

  3. Linking joins the object files, the compiler’s runtime library (libgfortran for gfortran, libflang_rt for Flang) and any libraries you name into the executable. The runtime library contains the input/output system, the support for intrinsic procedures that are not inlined, and the error-reporting code that prints the messages shown later on this page.

Programs that use coarrays or MPI are built the same way but are started by a launcher (cafrun for OpenCoarrays, mpirun for MPI) instead of by running the executable directly; see Coarrays, Images and Synchronization and MPI with mpi_f08.

The options used in this reference

All examples of this reference are checked with one fixed set of gfortran options. They are worth adopting for your own code from the first program.

Option Effect

-std=f2023

Accept only Fortran 2023 syntax and intrinsics and diagnose extensions. Other values are f95, f2003, f2008, f2018, gnu (the default, with extensions) and legacy. GNU Fortran 16 also has an experimental -std=f202y for the next revision.

-Wall -Wextra

Warn about unused variables, possibly uninitialised variables, implicit conversions, and other likely mistakes.

-pedantic

Warn about use of extensions and obsolescent features. Together with -std=f2023 it is the conformance check.

-fimplicit-none

Behave as if implicit none were present in every program unit. A safety net: the programs in this reference also say it themselves.

-fcheck=all

Insert run-time checks: array bounds, array temporaries, unallocated and disassociated objects, recursion, loop variable modification, string lengths, memory allocation and invalid arguments to bit intrinsics. It slows the program and is for development builds. -fcheck=bounds is the narrower form (and -fbounds-check is a deprecated alias).

-g

Debug information, so that backtraces show file names and line numbers and debuggers work.

-O2

Optimization for production runs. Do not combine timing measurements with -fcheck=all.

-J dir, -I dir

-J says where to write .mod files (the current directory by default) and adds that directory to the search path for use; -I adds a directory to the search path for use and for include.

-cpp, -nocpp, -D name

Force or disable the preprocessor; define a preprocessor macro.

Backtraces on run-time errors are printed by default by modern gfortran (the environment variable GFORTRAN_ERROR_BACKTRACE and the option -fno-backtrace control them), so the -fbacktrace that older tutorials recommend is a no-op today. Flang 23 has no equivalent of -fcheck: the bounds-violation program in the diagnostics section runs without any message under it. The complete option reference, including the Flang and ifx equivalents, is on Compilers and Compiler Options.

File extensions

The standard says nothing about file names. It states that the means of specifying the source form of a program unit are processor dependent (J3/23-007r1, 6.3.1), so every compiler decides from the extension, and the extension also selects whether the preprocessor runs. The conventions of GNU Fortran, which Flang and Intel ifx largely follow, are these.

Extension Contents Meaning

.f90

Free-form source

The default for new code. The name dates from Fortran 90, which introduced free form, and has stayed even though the content is Fortran 2023. .f95, .f03 and .f08 are accepted by gfortran as free form too.

.F90

Free-form source with preprocessor directives

The capital F makes GNU Fortran and Flang (both tested here) run the C preprocessor first, and ifx follows the same convention. On a case-insensitive file system (the default on macOS and Windows) the compilers cannot tell the two apart, which is why -cpp exists. .fpp is another common name for preprocessed Fortran.

.f, .for, .ftn

Fixed-form source

The 80-column source form with a statement label area, used by Fortran 77 and older code. GNU Fortran treats these as fixed form; -ffree-form overrides it. New code should not use fixed form, and it only matters here so that you recognise old code and can compile it (with -std=legacy, see Legacy Features and Modernization).

.mod

Module file

Written by the compiler when it compiles a module; read when a file says use for that module. Not meant to be edited or committed, and not portable between compilers, as shown below.

.smod

Submodule file (gfortran)

Compiled form of a submodule’s information. Flang uses the .mod extension for submodules too and names them by prefixing the ancestor module (module-submodule.mod).

.o, .obj, .a, .so, .dylib, .dll

Object files and libraries

Output of compilation and of the archiver and linker; platform conventions rather than Fortran ones.

The preprocessor

Because preprocessing is not standard, it is used sparingly, for platform or compiler differences and for __LINE__-style diagnostics. The following file, 04-banner.F90, is preprocessed because of its extension. It picks one of two branches with a macro and prints a line number that the preprocessor fills in.

program banner
    implicit none
#ifdef USE_DEBUG
    print '(a)', "debug build"
#else
    print '(a)', "release build"
#endif
    print '(a, i0)', "line number from the preprocessor: ", __LINE__
end program banner
gfortran 04-banner.F90 -o banner && ./banner
gfortran -DUSE_DEBUG 04-banner.F90 -o banner && ./banner
release build
line number from the preprocessor: 8
debug build
line number from the preprocessor: 8

gfortran -cpp -E -P -DUSE_DEBUG 04-banner.F90 prints the program after preprocessing and shows print '(a, i0)', ... , 8. The program compiles warning-free under the strict options and Flang 23 prints the same two lines for the release build. The same text saved as banner.f90 without -cpp is not preprocessed: GNU Fortran then reports Warning: Illegal preprocessor directive for each # line, because a lowercase .f90 file is parsed as plain Fortran. A GNU Fortran macro is expanded in traditional C preprocessor mode, so do not rely on the more elaborate features of a standard C preprocessor.

Several files and module files

Real programs are split into modules. The heatflow project starts with the smallest one, a module that names the numeric kinds the rest of the code uses. Save the module as 02-heatflow_kinds.f90 and a program that uses it as 03-kinds_demo.f90. (Kinds are explained on Intrinsic Types and Kinds; here dp is the kind of a 64-bit real.)

module heatflow_kinds
    use, intrinsic :: iso_fortran_env, only: int32, int64, real32, real64
    implicit none
    private
    public :: sp, dp, ip, lp

    integer, parameter :: sp = real32
    integer, parameter :: dp = real64
    integer, parameter :: ip = int32
    integer, parameter :: lp = int64
end module heatflow_kinds
program kinds_demo
    use heatflow_kinds, only: dp
    implicit none
    real(dp) :: t

    t = 0.1_dp
    print '(a, f4.1, a, i0)', "temperature = ", t, " kind = ", kind(t)
    print '(a, es12.5)', "machine epsilon  = ", epsilon(t)
end program kinds_demo

The module must be compiled first, because the program needs its .mod file:

gfortran -c 02-heatflow_kinds.f90          # writes heatflow_kinds.o and heatflow_kinds.mod
gfortran -c 03-kinds_demo.f90              # reads heatflow_kinds.mod, writes kinds_demo.o
gfortran 02-heatflow_kinds.o 03-kinds_demo.o -o kinds
./kinds
temperature =  0.1 kind = 8
machine epsilon  =  2.22045E-16

Both files compile warning-free under the strict options, and flang 02-heatflow_kinds.f90 03-kinds_demo.f90 -o kinds (module first on the command line) prints the same two lines. If the order is wrong, or the .mod file is not on the search path, the compiler stops:

03-kinds_demo.f90:2:9:

    2 |     use heatflow_kinds, only: dp
      |         1
Fatal Error: Cannot open module file 'heatflow_kinds.mod' for reading at (1): No such file or directory
compilation terminated.

This is the message to recognise when a Fortran build fails mysteriously: it means "a module this file uses has not been compiled yet, or was compiled into another directory". Compiling the two sources in a single command, as in the Flang line, works only when the module file comes first on the command line: gfortran and Flang compile the files in the order given, and with the two names swapped both stop with the same kind of message (Cannot open module file for gfortran, Source file 'heatflow_kinds.mod' was not found for Flang). For larger projects -J build/modules keeps the module files together and -I build/modules finds them, and a build system should compute the order, as fpm does below.

The module file format is an implementation detail with two practical consequences:

  • GNU Fortran writes a gzip-compressed text file whose first line, GFORTRAN module version '16' created from ..., names the module-file format version. A .mod file written by one GCC major release can be unreadable by another, so libraries distributed as binaries ship the modules for each compiler release. Flang writes the module as readable free-form Fortran source text with comments that carry a version and hash. Neither format is readable by the other compiler, and compilers do not share .mod files: build each compiler’s output into its own directory.

  • The standard does not specify any of this: it only says that a module can be a separate program unit. A file with several modules, or a module and the program that uses it in one file, compiles in a single step on every compiler.

A first fpm project

Calling the compiler by hand does not scale, and Fortran has no build tool in the standard. The community’s answer is the Fortran Package Manager, fpm: a build system and package manager that finds the sources, works out the module dependency order by reading use statements, compiles them in parallel, links executables and runs tests. It is a single program, written in Fortran, installed from binaries on the fpm releases page or from package managers such as pip, Conda, Homebrew, MSYS2 and WinGet (version 0.13.0 at the time of writing, still labelled alpha by its own --version output). The complete treatment is on Fortran Package Manager (fpm); the build tools CMake, Meson and Make are compared on Building with Make, CMake and Meson.

Creating the project is one command; fpm new also initialises a Git repository.

fpm new heatflow
cd heatflow
find . -path ./.git -prune -o -type f -print | sort
./README.md
./app/main.f90
./fpm.toml
./src/heatflow.f90
./test/check.f90

The layout is a convention, not a configuration: src/ holds the library modules, app/ holds the executables (each file with a program unit becomes one), test/ holds the test programs and, later, example/ holds examples. The generated fpm.toml carries placeholder metadata (author, license) that fpm fills from your Git configuration when it exists. The generated sources are a hello-world: a module heatflow with say_hello, an app/main.f90 that calls it and an empty test. fpm run and fpm test already work on them:

fpm run
fpm test
[100%] Project compiled successfully.
 Hello, heatflow!
...
[100%] Project compiled successfully.
 Put some tests in here!

The heatflow starting point

The real project replaces the generated files with a kinds module, a stencil kernel and a small driver. The manifest is plain TOML. It switches off implicit typing and implicit external interfaces for the whole package, so the rule "every unit says implicit none" is also enforced by the build.

name = "heatflow"
version = "0.1.0"
license = "MIT"
author = "Heatflow authors"
description = "2D heat diffusion on a rectangular plate"

[build]
auto-executables = true
auto-tests = true

[install]
library = true

[fortran]
implicit-typing = false
implicit-external = false
source-form = "free"

src/heatflow_kinds.f90 is the module shown above. The kernel in src/heatflow_kernels.f90 performs one Jacobi relaxation sweep of Laplace’s equation: every interior value becomes the average of its four neighbours, and the largest change is returned as a residual. (The pure subroutine, assumed-shape dummy arguments such as u(:, :) and intent are explained on Procedures, Functions and Subroutines and Array Arguments: Assumed Shape, Size and Rank.)

module heatflow_kernels
    use heatflow_kinds, only: dp
    implicit none
    private
    public :: jacobi_sweep

contains

    !> One Jacobi relaxation sweep of the Laplace equation (interior points).
    pure subroutine jacobi_sweep(u, unew, residual)
        real(dp), intent(in) :: u(:, :)
        real(dp), intent(inout) :: unew(:, :)
        real(dp), intent(out) :: residual
        integer :: i, j

        residual = 0.0_dp
        do j = 2, size(u, 2) - 1
            do i = 2, size(u, 1) - 1
                unew(i, j) = 0.25_dp*(u(i - 1, j) + u(i + 1, j) + u(i, j - 1) + u(i, j + 1))
                residual = max(residual, abs(unew(i, j) - u(i, j)))
            end do
        end do
    end subroutine jacobi_sweep
end module heatflow_kernels

app/main.f90 sets up a 21 by 21 plate whose top edge is held at 100 degrees and the other edges at 0, iterates until the largest change drops below 1e-6, and prints two numbers. By symmetry the centre must be the average of the four edges, 25 degrees.

program main
    use heatflow_kinds, only: dp
    use heatflow_kernels, only: jacobi_sweep
    implicit none
    integer, parameter :: n = 21
    integer, parameter :: mid = 11   ! middle point of a 21 x 21 grid
    real(dp) :: u(n, n), unew(n, n), residual
    integer :: step

    u = 0.0_dp
    u(:, n) = 100.0_dp          ! hot top edge, the other edges stay at 0
    unew = u
    do step = 1, 2000
        call jacobi_sweep(u, unew, residual)
        u = unew
        if (residual < 1.0e-6_dp) exit
    end do
    print '(a, i0, a, es9.2)', "steps = ", step, ", residual = ", residual
    print '(a, f8.3)', "centre temperature = ", u(mid, mid)
end program main

test/check.f90 is the first test: a field that is already uniform must not change under a sweep. A test program signals failure with error stop, which makes fpm test report it (see the exit-status example below). Unit-test frameworks such as test-drive come later, on Testing, Debugging and Profiling.

program check
    use heatflow_kinds, only: dp
    use heatflow_kernels, only: jacobi_sweep
    implicit none
    real(dp) :: u(5, 5), unew(5, 5), residual

    u = 1.0_dp                  ! a uniform field is already a solution
    unew = u
    call jacobi_sweep(u, unew, residual)
    if (residual > 0.0_dp) error stop "uniform field must not change"
    print '(a)', "all tests passed"
end program check
fpm run
fpm test
steps = 1060, residual =  9.91E-07
centre temperature =   25.000
...
all tests passed

The files compile warning-free under the strict gfortran options when passed in dependency order (kinds, kernels, then app/main.f90 or test/check.f90), and a Flang 23 build of the same three files prints steps = 1060, residual = 9.91E-07 and centre temperature = 25.000. The ... lines stand for the fpm progress messages.

fpm has build profiles. fpm build --profile release compiles with -O3 and related optimization options, and fpm build --profile debug with -Wall -Wextra -g -fcheck=bounds -fcheck=array-temps -fbacktrace (the exact lists are fpm’s and may change between versions; add --verbose to see the compiler command). A plain fpm build without a profile passed only the options derived from the manifest in the version tested here, so name the profile explicitly. To use your own flags, pass --flag, which is added to the profile’s options when a profile is named and replaces them otherwise:

fpm run --flag "-std=f2023 -Wall -Wextra -pedantic -fcheck=all"
fpm run --profile release

Both runs print the same two result lines. The build output goes to a build/ directory whose subdirectory name hashes the compiler and flags, so a debug and a release build coexist; .mod files live there, outside your sources. Add build/ to .gitignore.

Reading diagnostics and runtime errors

Fortran compilers tell you a lot, but only if you ask. There are three kinds of message: compile-time errors, compile-time warnings and run-time errors. The next four programs are intentionally wrong or sloppy; they exist to show the messages.

A compile-time error

With implicit none, a misspelled name is an error. Without it, temprature would silently be a new, uninitialised real (program 07-typo.f90, intentionally wrong).

! Intentionally wrong: demonstrates a compile-time diagnostic.
program typo
    implicit none
    real :: temperature

    temperature = 20.0
    print *, "T = ", temprature
end program typo
07-typo.f90:7:31:

    7 |     print *, "T = ", temprature
      |                               1
Error: Symbol 'temprature' at (1) has no IMPLICIT type; did you mean 'temperature'?

gfortran prints the file, the line and column, the source line, a marker 1 under the offending token and a hint with the closest declared name; the compiler exits with status 1 and no executable is written. Flang reports the same mistake as error: No explicit type declared for 'temprature' with a caret under the name. Read the first error first, since later errors are often consequences of it. A message that mentions "Symbol …​ has no IMPLICIT type" or "No explicit type declared" is almost always a typo or a missing use.

Warnings

Many mistakes are legal Fortran, so they are warnings, and only the ones you request are printed. Program 10-warnings.f90 (intentionally sloppy) has an unused variable and reads a variable that was never assigned.

! Intentionally sloppy: demonstrates -Wall -Wextra warnings.
program warnings
    implicit none
    integer :: unused
    real :: x, y

    x = 1.0
    print *, x + y
end program warnings

Compiled with gfortran -std=f2023 -Wall -Wextra -pedantic -O1 -c 10-warnings.f90 it produces (optimization is needed for the uninitialised-use analysis):

10-warnings.f90:4:16:

    4 |     integer :: unused
      |                1~~~~~
Warning: Unused variable 'unused' declared at (1) [-Wunused-variable]
10-warnings.f90:8:18:

    8 |     print *, x + y
      |                  ^
Warning: 'y' is used uninitialized [-Wuninitialized]
10-warnings.f90:5:16:

    5 |     real :: x, y
      |                ^
note: 'y' was declared here

Treat warnings as defects: -Werror turns them into errors for a build that must stay clean. -finit-real=snan together with -ffpe-trap=invalid makes an uninitialised real an immediate floating-point trap at run time, which finds the cases the compiler cannot prove statically; floating-point exceptions are covered on Floating-Point and IEEE Arithmetic.

A run-time error: array bounds

Fortran does not check array subscripts unless asked. Program 08-bounds.f90 (intentionally wrong) writes one element past the end of a five-element array.

! Intentionally wrong: demonstrates a run-time diagnostic.
program bounds
    implicit none
    integer, parameter :: n = 5
    real :: u(n)
    integer :: i

    u = 0.0
    do i = 1, n + 1
        u(i) = real(i)
    end do
    print *, u
end program bounds

With -fcheck=all (or just -fcheck=bounds) gfortran reports the error, with a compile-time warning because the loop bound is a constant, and a backtrace at run time:

08-bounds.f90:10:10:

    9 |     do i = 1, n + 1
      |                   2
   10 |         u(i) = real(i)
      |          1
Warning: Array reference at (1) out of bounds (6 > 5) in loop beginning at (2)
At line 10 of file 08-bounds.f90
Fortran runtime error: Index '6' of dimension 1 of array 'u' above upper bound of 5

Error termination. Backtrace:
#0  0xffff9d23196f in ???
#1  0xffff9d2326b3 in ???
#2  0xffff9d232c13 in ???
#3  0x400923 in bounds
	at /work/08-bounds.f90:10
#4  0x400a4f in main
	at /work/08-bounds.f90:13

The addresses and the frames marked ??? (the runtime library) vary from run to run and machine to machine; the useful lines are the At line 10 of file ... message and the frame with your program name and source location, which -g provides. The process exits with status 2. Without -fcheck the write still happens and silently overwrites whatever lives after u in memory, which is the classic Fortran way to get a wrong answer or a crash somewhere else. Flang 23 compiles the same program and prints 1. 2. 3. 4. 5. without complaint (the sixth store goes to memory it does not own), because it has no bounds-check option: use a sanitizer, a debugger, or gfortran’s check on the same source. The standard requires a subscript to be within the bounds of its dimension (J3/23-007r1, 9.5.3) but does not oblige a processor to diagnose a violation, so a clean run is no proof that the code is correct.

A run-time error: allocation

Allocation statements are checked by the compiler without any option. Program 09-allocated-twice.f90 (intentionally wrong) allocates an array that is already allocated.

! Intentionally wrong: demonstrates a run-time diagnostic.
program allocated_twice
    implicit none
    real, allocatable :: u(:)

    allocate (u(10))
    allocate (u(20))
    print *, size(u)
end program allocated_twice
At line 7 of file 09-allocated-twice.f90
Fortran runtime error: Attempting to allocate already allocated variable 'u'

Error termination. Backtrace:
#0  0xffff8d40196f in ???
#1  0xffff8d4026b3 in ???
#2  0xffff8d402c13 in ???
#3  0x400b13 in allocated_twice
	at /work/09-allocated-twice.f90:7
#4  0x400c2f in main
	at /work/09-allocated-twice.f90:9

Flang 23 compiles the same program and prints 20 without a message. Allocating an array that is already allocated is an error in standard Fortran, so a portable program must test allocated(u) first; an allocate with stat= and errmsg= turns the error into a value you can handle (see Error Handling and Program Termination). Use of an unallocated array or a disassociated pointer is covered by -fcheck=pointer, which is part of -fcheck=all, on the pages about allocatable arrays and pointers.

Exit status and stop

A program ends normally at its end program or at stop, and abnormally at error stop or at a run-time error. The standard recommends, but does not require, that an integer stop code become the process exit status, that a normal termination without an integer code give zero, and that an error stop without an integer code give a processor-dependent nonzero value (J3/23-007r1, 11.4 and 5.3.7). That is what shell scripts, fpm test and continuous integration rely on. The program 11-exit-status.f90 shows all three and the difference between list-directed and formatted output mentioned earlier.

program exit_status
    implicit none
    character(len=32) :: arg

    print *, "list-directed output starts with a blank"
    print '(a)', "formatted output does not"
    if (command_argument_count() < 1) then
        write (*, '(a)') "usage: exit_status <ok|fail>"
        stop 3
    end if
    call get_command_argument(1, arg)
    if (trim(arg) == "fail") error stop "requested failure"
    print '(a)', "finished normally"
end program exit_status
./exit_status;      echo "status=$?"
./exit_status ok;   echo "status=$?"
./exit_status fail; echo "status=$?"
 list-directed output starts with a blank
formatted output does not
usage: exit_status <ok|fail>
status=3
 list-directed output starts with a blank
formatted output does not
finished normally
status=0
 list-directed output starts with a blank
formatted output does not
status=1

gfortran prints STOP 3 and ERROR STOP requested failure (the latter followed by a backtrace) on the standard error unit, not in the output above, and Flang prints Fortran ERROR STOP: requested failure; both exit with status 1 for error stop. The stop code and any signalling floating-point exceptions go to the unit error_unit of iso_fortran_env unless quiet=.true. is given. Only the low bits of an integer code may reach the shell on some systems (J3/23-007r1, 11.4, Note 2).

Where the books are outdated for this page

Area Outdated in the books, and the current approach

Source-form limits

Modern Fortran Explained gives the Fortran 2018 limits of 132 characters per line and 255 continuation lines. Fortran 2023 allows 10,000 characters per line and no limit on continuation lines, with 1,000,000 characters per statement (J3/23-007r1, 6.3.2.1, 6.3.2.6 and 6.3.3).

Standard identity

The book never gives the ISO number or date of Fortran 2023. It is ISO/IEC 1539-1:2023 (November 2023), with a first corrigendum in 2026, and the best free text is the final draft J3/23-007r1.

Compilers

The book contains no compiler-support information beyond an old NAG release string. Use the support table on History, Standards and Compilers and check your compiler’s version.

Build tools

Only the make recompilation cascade is mentioned. Today a project uses fpm, CMake or Meson, which discover module dependencies; the file .mod and the order of compilation are explained above.

Installation pages

The fortran-lang.org setup pages still name GCC 10 to 12 and the discontinued Atom editor. Install what your platform currently ships and verify with --version.

Obsolete practice

Older tutorials teach .f fixed-form files, real*8 and implicit typing in the first program. None of this is needed: write free-form .f90, say implicit none and take kinds from a module such as heatflow_kinds.

Common first-day problems

Symptom Cause and remedy

Cannot open module file 'x.mod' for reading

The module was not compiled before the file that uses it, or its .mod is in another directory. Compile modules first, add -I/-J, or use fpm.

Illegal preprocessor directive or #ifdef lines in the output

A .f90 file has # lines. Rename it .F90 or add -cpp.

Symbol ... has no IMPLICIT type

A misspelled name or a missing use under implicit none.

data declaration statement at (1) cannot appear after executable statements

A declaration after an executable statement in the same unit. Declarations come first (apart from block constructs), then statements.

A program runs but prints garbage or crashes elsewhere

Compile with -g -fcheck=all, read the first warning, and try Flang or ifx for a second opinion.

.mod files from another compiler are rejected

Module files are compiler-specific and often version-specific. Rebuild from source with the compiler you use.

A mixed-case file name works on Linux but not on macOS or Windows

Case-insensitive file systems cannot tell .f90 from .F90. Use -cpp explicitly and lowercase names where the compiler allows.

Where to go next

The next pages are History, Standards and Compilers, Fortran for Programmers of Other Languages and Program Structure and Source Form, which begins the language proper. The tools introduced here return later: compiler options on Compilers and Compiler Options, fpm on Fortran Package Manager (fpm) and run-time checking on Testing, Debugging and Profiling.