Skip to main content
GitHub

ADS02-F. Separate initialization from declaration to avoid implicit variable persistence

Programmers should separate the initialization of a local variable from its declaration to ensure the variable is reinitialized on each invocation. If a variable is intended to retain its value across procedure calls, the save attribute should be explicitly declared.

In Fortran, a variable initialized in its type declaration (e.g., integer :: i = 0 ) implicitly acquires the save attribute. This makes the variable static: it is allocated once at program start and retains its value across multiple calls to the procedure.

This behavior differs from languages like C, C++, or Java, where local variables initialized in a declaration are re-initialized on every scope entry. Failing to account for implicit save can lead to subtle logic errors, as the variable's value depends on previous calls.

Relying on implicit persistence creates ambiguity regarding the programmer's intent. Explicitly declaring the save attribute when persistence is required serves as documentation, distinguishing a deliberate design choice from an accidental initialization error. Conversely, separating initialization from declaration ensures the variable behaves as a standard local variable, improving the readability and maintainability of the code.

Additionally, local variables in pure procedures cannot have the save attribute, so initializing a local variable in its declaration can prevent the procedure from being declared pure .

Noncompliant Code Example

In this noncompliant example, the programmer intends for counter to be a local temporary variable that starts at 0 every time the subroutine is called. However, because it is initialized in the declaration statement, it implicitly has the save attribute.

The variable counter retains its value between calls due to the implicit save . If the subroutine process_data() is called twice with the dummy argument val set to the contanst value 5 , then the printed outputs will be 5 and 10. Without the save attribute, counterwould be set to 0 in each subroutine call and the printed output would be 5 and 5.

Non-compliant code
subroutine process_data(val)
  implicit none
  integer, intent(in) :: val
  ! Noncompliant: The variable implicitly has the save attribute
  integer             :: counter = 0

  counter = counter + val
  print *, "Cumulative internal value:", counter
end subroutine process_data

Compliant Solution

In this compliant solution, the programmer intends for the variable to be local and re-initialized to 0 in each subroutine call. Thus the initialization is moved to the execution statement, away from the declaration statement.

Compliant code
subroutine process_data(val, result)
  implicit none
  integer, intent(in)  :: val
  integer, intent(out) :: result
  integer              :: temp_val

  ! Compliant: The variable is not initialized in the declaration statement
  temp_val = 0
  temp_val = temp_val + val
  result = temp_val
end subroutine process_data

Compliant Solution

In this compliant solution, the programmer intends for the variable to be persistent (a "static" counter). Thus, the save attribute is explicitly declared to make the program logic clear to the reader.

Compliant code
subroutine process_data(val)
  implicit none
  integer, intent(in) :: val
  ! Compliant: The variable has the save attribute explicitly
  integer, save       :: counter = 0

  counter = counter + val
  print *, "Cumulative internal value:", counter
end subroutine process_data

Risk Assessment

Implicit save attributes can cause unintended persistence, introducing hidden side effects. This may lead to incorrect results, non-deterministic behavior in parallel or multi-threaded environments (e.g., OpenMP, Coarrays), and subtle logic errors that are difficult to detect during code reviews.

RecommendationSeverityLikelihoodDetectableRepairablePriorityLevel
ADS02-FHighLikelyYesYesP27L1

Bibliography

[ Fortran 2023 Interpretation Document ]Section 8.4 and 8.5.16

Attachments: