Search code examples
segmentation-faultfortrangfortranintel-fortran

Segmentation fault by passing "parameter" to subroutine


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:

  • ifort 11.1
  • gfortran 4.1.2
  • gfortran 4.4.7
  • gfortran 4.8.4 (the newest in the repos for Ubuntu 14.04)

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).

  1. Am I wrong for expecting a compile error/warning in such cases? Or at least a run-time error beyond "invalid memory reference".
  2. Does it make sense that using arrays avoids this error for some compilers?
  3. Am I right that the different behaviour encountered on different systems is due to the difference in compilers, or could it be more subtly system-specific?

Solution

  • 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.