I've spent the last two days debugging a seemingly nonsensical segfault in a large Fortran project. The problem started when I moved the code to my own computer, and the segfault arised in a part of the code that has been working fine for years on several other systems. I eventually found the source of the segfault, but it is so astonishingly unexpected (and compiler-dependent) that I decided to post it here.
Consider the following MWE:
program dafuq
implicit none
integer :: a=1
integer, parameter :: b=2
call foo(a,b)
end program dafuq
subroutine foo(a,b)
implicit none
integer, intent(inout) :: a, b
a=b !OK
b=a !causes segfault
end subroutine foo
I have access to two HPC clusters, which together with my laptop allows me to check these (occasionally somewhat old) compilers:
It turns out that all four compilers produce a segfault with the above code, because variable b
is declared as a parameter
. Consequently it's a violation to change its value in the subroutine. My problem is that only the newest gfortran shows a warning during compile (even with -Wall), and that too goes away if I omit the intent
specification in the subroutine. I suspect that the same setup in C++ using const
variables would raise a huge red flag.
Now, to make it more obscure, consider the following code, with arrays instead of scalars:
program dafuq_array
implicit none
integer :: a(2)=(/1,1/)
integer, parameter :: b(2)=(/2,2/)
call foo(a,b)
end program dafuq_array
subroutine foo(a,b)
implicit none
integer, intent(inout) :: a(2), b(2)
a=b !OK
b=a !might cause segfault
end subroutine foo
Now, in THIS case, the newest gfortran produces a segfault, while the other three compilers don't! (Actually this is the reason why I didn't encounter this problem earlier: the newest gfortran on the list is the one on my own computer.) In all cases I used essentially no compile switches, i.e. ifort -o mwe mwe.f
and the same for gfortran.
Even though I found the reason for the segfault and I sort of understand it, there are still a few things which bug me (no pun intended).
Generally Fortran function arguments will only be typechecked if they are inside a module. For instance if you put the subroutine in a module:
module m
public
contains
subroutine foo(a,b)
implicit none
integer, intent(inout) :: a,b
a = b
b = a
end subroutine
end module
program p
use m
implicit none
integer :: a
integer, parameter :: b = 2
a = 1
call foo(a,b)
end program
compiling gives the errors:
gfortran 4.6.4
:
test.f90:35.15:
call foo(a,b)
1
Error: Non-variable expression in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)
ifort 13.0.1
:
test.f90(35): error #6638: An actual argument is an expression or constant; this is not valid since the associated dummy argument has the explicit INTENT(OUT) or INTENT(INOUT) attribute. [2]
call foo(a,b)
---------------^
compilation aborted for test.f90 (code 1)
If you're not able to add modules to the code you could also consider enabling automatic interfaces and warnings (-gen-interfaces -warn all
in ifort) to enable argument checking for functions not in modules.