Skip to main content

PWR072: Explicitly declare the 'save' attribute or split the variable initialization to prevent unintended behavior

Issue

In Fortran, when a variable is initialized at its declaration, it implicitly acquires the save attribute. This behavior obscures the program logic and is often unintended by the programmer, potentially breaking the program logic.

Actions

If the save behavior is intentional, explicitly add the attribute in the variable declaration to clarify the intent:

integer, save :: count = 0

Otherwise, split the initialization of the variable from its declaration to remove the implicit save behavior and prevent unintended effects.

Relevance

A variable with the save attribute retains its value across multiple invocations of the procedure in which it is defined. The implicit save behavior can cause debugging difficulties, even when intended, and lead to hard-to-diagnose errors in program logic, particularly when working in complex codebases where functions are called multiple times during execution.

Code example

Consider the code below, which computes the sum of the elements of various arrays using the sum_array() function. Note how the variable result is set to 0 at its declaration, implicitly acquiring the save behavior:

! example.f90
program test_implicit_save
implicit none
integer, dimension(3) :: A = [1, 1, 1], B = [2, 2, 2]
integer :: result

result = sum_array(A)
print *, "Sum of A:", result ! Expected: 3

result = sum_array(B)
print *, "Sum of B:", result ! Expected: 6

contains

integer function sum_array(array)
implicit none
integer, intent(in) :: array(:)
integer :: result = 0
integer :: i

do i = 1, size(array)
result = result + array(i)
end do

sum_array = result
end function sum_array

end program test_implicit_save

Each time sum_array() is called, one might expect result to be set to 0 and then add the elements of the target array. However, result retains its value between calls, breaking the intended logic for the program:

$ gfortran --version
GNU Fortran (Debian 12.2.0-14) 12.2.0
$ gfortran example.f90
$ ./a.out
Sum of A: 3
Sum of B: 9

While resolving the issue is as simple as splitting the initialization of result to a separate line, this type of bug can be particularly challenging to diagnose in complex codebases:

! solution.f90
program test_implicit_save
implicit none
integer, dimension(3) :: A = [1, 1, 1], B = [2, 2, 2]
integer :: result

result = sum_array(A)
print *, "Sum of A:", result ! Expected: 3

result = sum_array(B)
print *, "Sum of B:", result ! Expected: 6

contains

pure integer function sum_array(array)
implicit none
integer, intent(in) :: array(:)
integer :: result
integer :: i

result = 0

do i = 1, size(array)
result = result + array(i)
end do

sum_array = result
end function sum_array

end program test_implicit_save
tip

Now the function doesn't retain its state between calls, so it can be annotated with the pure keyword.

Check the PWR003 entry for more details!

References