Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PWR072: Suggest splitting the variable initialization #43

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 70 additions & 40 deletions Checks/PWR072/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
# PWR072: Add an explicit save attribute when initializing variables in their declaration
# PWR072: Split the variable initialization from the declaration to prevent the implicit 'save' behavior

### Issue

In Fortran, when a variable is initialized at its declaration, it implicitly
acquires the `save` attribute. This behavior can lead to unintended
consequences if the programmer is unaware of it.
acquires the `save` attribute. This behavior is often unintended by the
programmer and can break the program logic.

### Actions

To improve code clarity, explicitly write the `save` attribute when
initializing variables at their declaration.
Split the initialization of the variable from its declaration to remove the
implicit `save` behavior and enhance code clarity.

>If the `save` behavior is intentional, explicitly add the attribute in the
>variable declaration to clarify the intent:
>
>```f90
>integer, save :: count = 0
>```

### Relevance

Expand All @@ -21,67 +28,90 @@ multiple times during execution.

### Code example

Consider the subroutine below, where a variable `count` is initialized at its
declaration, but without an explicit `save` attribute:
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:

```f90
! example.f90
program test_implicit_save
call counter
call counter
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

subroutine counter()
integer :: count = 0
count = count + 1
print *, count
end subroutine
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 `counter()` is called, one might expect `count` to be set to `0` and
then incremented to `1`. However, due to the implicit `save` attribute, `count`
retains its value between calls:
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:

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

A clearer and more intentional approach would be:
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:

```f90
! solution.f90
program test_explicit_save
call counter
call counter
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

subroutine counter()
integer, save :: count = 0
count = count + 1
print *, count
end subroutine
end program test_explicit_save
```
integer function sum_array(array)
implicit none
integer, intent(in) :: array(:)
integer :: result
integer :: i

Here, the `save` attribute is explicitly declared, making it clear that `count`
is intended to accumulate its value between calls.
result = 0

>**Note:**
>If you simply want to initialize a variable when declaring it, while avoiding
>the `save` behavior, put the initialization in a separate line:
>
>```f90
>integer :: count
>count = 0
>```
do i = 1, size(array)
result = result + array(i)
end do

sum_array = result
end function sum_array

end program test_implicit_save
```

### Related resources

Expand Down
33 changes: 24 additions & 9 deletions Checks/PWR072/example.f90
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
! PWR072: Add an explicit save attribute when initializing variables in their
! declaration
! PWR072: Split the variable initialization from the declaration to prevent the
! implicit 'save' behavior

program test_implicit_save
call counter
call counter
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

subroutine counter()
integer :: count = 0
count = count + 1
print *, count
end subroutine
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
39 changes: 28 additions & 11 deletions Checks/PWR072/solution.f90
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
! PWR072: Add an explicit save attribute when initializing variables in their
! declaration
! PWR072: Split the variable initialization from the declaration to prevent the
! implicit 'save' behavior

program test_explicit_save
call counter
call counter
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

subroutine counter()
integer, save :: count = 0
count = count + 1
print *, count
end subroutine
end program test_explicit_save
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