Many people rely on compilers, for languages such as C, C++ and Fortran, to create executable programs from source code. Just like our source code, compilers themselves may have bugs. In this post we look at common forms of compiler bug, with examples, and what we can do when our work is affected by such an issue.
The compilers we use to convert a program’s source code into a usable piece of software are generally very complicated programs and are themselves susceptible to containing bugs. In this post we look at the types of bugs found in Fortran compilers and what we, as users of compilers, can do about them.
What is a compiler bug?
We can broadly categorize compiler bugs by the nature of the resulting undesirable behaviour. For example, here we’ll consider the following cases:
- reporting an internal compiler error/crashing;
- rejecting a valid program source file;
- failing to make a required diagnosis;
- generating an incorrect program.
As well as these obvious bugs there are deficiencies in the user experience which we can call quality of implementation issues:
- providing weak error detection or diagnostics;
- producing a poorly performing program (time or accuracy);
- taking disproportionate resources to compile.
When these latter are sufficiently severe they may also be classed as “bugs”. In this post we’ll look mostly at examples from the first class.
Internal compiler error
The most obvious of bugs in a compiler is where the compiler simply crashes or reports an internal error. We may see messages such as
Please submit a full bug report, with preprocessed source if appropriate. See <file:///usr/share/doc/gcc-8/README.Bugs> for instructions.
catastrophic error: **Internal compiler error: internal abort** Please report this error along with the circumstances in which it occurred in a Software Problem Report. Note: File and line given may not be explicit cause of this error.
PGF90-S-0000-Internal compiler error. get_llvm_name: bad stype for 298
Panic: polite.f90: hello oops VERY_SORRY Internal Error -- please report this bug Abort
/opt/ibm/xlf/16.1.1/bin/.orig/xlf: 1501-230 (S) Internal compiler error; please contact your Service Representative.
A Fortran compiler may compile Fortran source to C source for a C compiler to create a program. An internal error in the Fortran compiler may instead result in C being produced which cannot be compiled:
bad_c.f90: In function ‘s_’: bad_c.f90:1:3: error: ‘x_’ is a pointer; did you mean to use ‘->’? subroutine s(n) ^ ->
Rejecting valid source
A compiler may determine that a source file is invalid, perhaps reporting a syntax error, even when the source is itself allowed according to the language standard. This may not necessarily be a bug: it could be that the compiler doesn’t yet understand a particular feature of the evolving Fortran language. However, if the compiler vendor claims to have support for a feature but then rejects a program, we can call that a bug. For example, given
procedure(double precision) f end program
we are declaring an external procedure
f which is a function with result
double precision and an implicit interface. This is a very basic
form introduced in Fortran 2003. A compiler not supporting this syntax may
report an error in the source, but a compiler message rejecting this source
error #5082: Syntax error, found IDENTIFIER 'PRECISION' when expecting one of: COMPLEX PRECISION procedure(double precision) f -------------------^
is from a somewhat confused compiler.
Compilers may also be confused by the program
procedure(type) a contains function type() end function end program
erroneously reporting a syntax error.
Equally, rejecting the program
interface subroutine sub end end interface end program
with a complaint that
PGF90-W-0155-END statement for internal procedure should be END SUBROUTINE
shows some difficulty in understanding the source. Here, it isn’t necessary
end subroutine [sub] to be used instead of a simple
end, and the
sub isn’t an internal procedure. Not only is rejecting this
source a mistake, but the error message itself doesn’t apply.
Accepting invalid source
The definition of the Fortran language tells us how a Fortran processor must treat a program which conforms to the specification. If instead of providing a valid Fortran program we provide invalid source the compiler may have the ability to treat it like a valid program with extensions. For example, while the source
slksfdlfdlhd0fdlkfdhl035o53iofhd9ufgd098nhd df09fgd05te0 bfdljkfdjkld0985elk end program
is unlikely to be viewed favourably by a compiler, the source
integer i read *, i if (i) then print *, 'You gave me a true value' end if end program
is not valid Fortran but can be understood by many compilers. While it can be seen as desirable for this to be rejected with a message like
Expression in IF construct is not logical
a compiler may document accepting this as an extension to the language. This wouldn’t usually be considered a bug, regardless of how unfortunate that choice may be.
However, certain invalid aspects of a program may require a compiler to be able to diagnose that problem. For example, if we have the source file
integer, allocatable :: i allocate(i,source=0.) end program
which is attempting sourced allocation for the integer
i, we have an
allocation statement which is not allowed in a Fortran program. The source
here is a real constant which is not type compatible with the variable to
If a compiler cannot detect and report such an invalid allocation statement then it does not conform to the Fortran language standards beyond Fortran 2003: the compiler vendor would likely consider this to be a bug.
Generating incorrect program
In some cases, the Fortran standard gives a precise description of the expected result of a Fortran program. As in other sections here, deviation from presenting the required result would be a non-conformance and regarded a bug. For example, the result of the program
type t(n) integer, len :: n=1 integer i(n) end type t type(t(:)), allocatable :: x allocate(t :: x) x%i = 0 associate (y=>x%i) y = 1 end associate print '(I1)', x%i end program
should be the line
The Intel Fortran compiler version 18.0.3, however, does not give this result.
Poor compilation performance
The compilation process should use reasonable resources, proportionate for the complexity of the program to be compiled. When compilation takes much longer to complete than expected, or requires vastly more memory, this may be viewed as a bug.
For example, given the program
type t1(n) integer, kind :: n end type t1 type t(n) integer, kind :: n type(t1(1)) x(n) end type t type(t(1)) x end program
gfortran 8.3.0 (but not gfortran 9.1.0) may consume vast amounts of memory.
What can we do when we find a compiler bug?
As with other software, when we find a bug in a compiler we want to find ways to avoid the problem. There may be a later version of the compiler without this bug that can be used or we can report the bug to the compiler developer and hope for a timely fix.
If you’re using the compilers installed on Apocrita and would like a newer release to be installed, then please contact us. You can also contact us for help reporting the bug to the developer, especially if using one of the commercially supported compilers such as those from Intel and NAG.
The RSE team can also help you in trimming down your large programs which upset a compiler to give smaller examples of code similar to those presented above.
Sometimes, internal compiler errors may come about when we write incorrect code. For example, the following program sees ifort 19.0.5 crash:
type t(n) integer, len :: n real x(n) end type t type(t(k%n)) :: y end program
This isn’t legal Fortran code (because
k hasn’t been declared as of a type
n), but it would be preferable for the compiler not to
crash. In such cases we’d correct our code but we should still consider
reporting the bug to the compiler developer.
Finally, it may be necessary to modify your own (correct) source code to work around the compiler bug: all releases of a compiler may have the problem or it may be necessary for you to support a particular version of a given compiler with your program.
For example, although the valid program
contains subroutine s end end program
results in a fatal error in pgfortran 19.4 we can easily change this to a form happily accepted:
program main contains subroutine s end end program
With a suitable program name such a change may even be the preferred form of a program. In other cases, necessary changes may be less desirable such as trying to have ifort 19.0.5 compile one of our earlier programs:
procedure(double precision) f end program
We can modify this to the equally valid and meaningful
procedure(doubleprecision) f end program
procedure(real(kind(0d0))) f end program
which the Intel compiler accepts. Such forms may be less preferred than the
original and may well require additional documentation if this is inconsistent
double precision declarations in the source code.
Bugs in compilers, although hopefully rare, are sadly a feature of our life developing research software. In this post we’ve seen examples of different types of bugs in Fortran compilers and what “internal compiler error” messages may look like.
Compiler crashes are usually quite obvious and we’ve considered what we can do when we see one. Other types of bugs, such as incorrectly rejecting a valid program, may be more challenging to spot: the RSE team can help advise in cases where it isn’t clear whether it’s the compiler or the program at fault.
In yet other cases, there may be bugs which make the results of the research program incorrect. This shows us how important it is to ensure we have good testing for our software to catch not just our own errors but compiler problems, or even portability pains.
What we shouldn’t conclude is that Fortran compilers are horribly unreliable or that Fortran is a poor language to choose. Bugs are rare and compilers for other languages also have examples of bugs not shown in this post.
Please contact us for advice around issues with potential compiler bugs, software testing or portability of your research software.