0

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?

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the quick answer. I do use -gen-interfaces -warn interfaces from time to time during development, however the project is a mixture of f77 and f90-style parts, with most of the older routines missing intent specifications. Unfortunately, automatic interfaces can't find these bugs without intents.
3

You aren't wrong to hope for an error (though you can't expect one for this case) and GNU Fortran 5.1.0 does provide a warning for your second test case.

dafuq.f90:6:13:

   call foo(a,b)
             1
Warning: Named constant ‘b’ in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)

Also note that while some compilers work fine, some don't compile, some segfault, some work with arrays where they didn't work with scalars are all reasonable outcomes. Once you violate the standard, your code isn't Fortran anymore and undefined behavior means any outcome is a correct one.

3 Comments

You can hope for an error, but you cannot expect one - the general case includes the situation where the external procedure is written after the main program has been compiled.
What do you exactly mean by "violating the standard"? The fact that a parameter variable is subsequently assigned to? I agree, that this could (well, and does) lead to undefined behaviour. My point, then, is that the compiler might want to tell you that you are about to do something non-standard. But from the point made by @IanH I suspect that it might be technically impossible for the compiler to check for this consistently.
@AndrasDeak The standard disallows using a parameter in a variable context or as an actual argument with intent out or inout. The compiler can warn for some cases but catching them all harder to do and in some cases probably not possible at all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.