Fortran function remembers values (newbie help)

I’m trying to implement the fibonacci solution using iterative method. It all works well except that the tmp variable remembers the values of temporary variables for the next function call.
Here’s the code that I’ve written

program fibonacci
    use, intrinsic :: iso_fortran_env, only : i8=>int64, error_unit
    implicit none
    integer :: n=5
    integer(i8) :: f1, f2

    print *, fib(n), fib(n), fib(n)
    print *, fib(n)

    contains
    function fib(n) result(f) ! iterative
        integer,intent(in) :: n
        integer(i8) :: f
        integer(i8) :: tmp=0, f_1=1
        integer :: i=0

        if (n <= 0) then
            write(error_unit, *) 'n must be a positive integer'
            stop 1
        end if
        f=f_1
        do i = 2, n
            tmp=f
            f = f + f_1
            f_1 = tmp
        end do
    end function fib
end program fibonacci

This gives the following output.

                    8                   40                  200
                 1000

It looks like the tmp and f_1 are remembering the values between function calls. What am I doing wrong here? Isn’t it supposed to remember the variable values only if save is mentioned? Please let me know.
Thanks in advance!

4 Likes

The implicit SAVE pitfall…
See Deprecate and remove implicit save behavior · Issue #40 · j3-fortran/fortran_proposals · GitHub

4 Likes

Setting a variable equal to a value in the type declaration statement implicitly gives the variable the SAVE attribute. In this regard, the behavior of local variables is different from the behavior of corresponding variables in languages such as C.

You may benefit from reading this informative page .

1 Like

Assigning a value to a variable in the declaration implicitly adds the save attribute. You should change

    integer(i8) :: tmp=0, f_1=1
    integer :: i=0

to

    integer(i8) :: tmp, f_1
    integer :: i

    tmp = 0
    f_1 = 1
    i = 0
1 Like

Thank you all! I wasn’t aware of this before!

You should initialize variables in the declaration only if they are constants (PARAMETER), or if you are conscious it implies SAVE (but in that case explicit is better than implicit…)

1 Like

Thank you! I’ll remember that.

@Colorizer , just for your reference, here is an option with the recursive method, you will need a modern compiler if you try the code as-is.

1 Like

Thank you! It seems the latest gfortran doesn’t support this yet. Is there any specific advantage in calling elemental instead of normal recursive function? Also, is there any resource for reading about these changes?

What version of gfortran? All the versions I’ve used in the past five years or so support recursive procedures. If you have a somewhat older version, you may have to add the “recursive” attribute explicitly, i.e, recursive function fibonacci(n). Procedures being recursive by default is a relatively recent change in the language.

For a brief overview of new features in each standard, I like John Reid’s writeups:

If you encounter a features that isn’t there, it’s likely from Fortran 95 or earlier, in which case the Wikipedia page is pretty comprehensive: Fortran 95 language features - Wikipedia

1 Like

Since LFortran has not yet implemented the implicit save mis-feature of Fortran, it gives the results an uninitiated Fortran programmer would expect for the slightly modified program

program fibonacci
    use, intrinsic :: iso_fortran_env, only : i8=>int64
    implicit none
    integer :: n=5
    integer(i8) :: f1, f2

    print *, fib(n), fib(n), fib(n)
    print *, fib(n)

    contains
    function fib(n) result(f) ! iterative
        integer,intent(in) :: n
        integer(i8) :: f
        integer(i8) :: tmp=0, f_1=1
        integer :: i=0

        if (n <= 0) then
            write(*,*) 'n must be a positive integer'
            stop 1
        end if
        f=f_1
        do i = 2, n
            tmp=f
            f = f + f_1
            f_1 = tmp
        end do
    end function fib
  end program fibonacci

Ouput:

8 8 8
8

Every compiler has a strict-standard mode, and in that mode LFortran is obliged to give the results produced with implicit save. Maybe the default mode should refuse to compile the program unless the save attribute is added explicitly.

2 Likes

I’m currently on 11.2.1. Yes, I did intend to meant the recursive by default procedures. Seems only intel compiler supports that for now. Thank you very much for the links!

Yes, that will definitely be helpful for newbies!

Implicit save strikes again! I firmly believe that if this stupid feature is removed from the language (and replaced with the expected behavior) it will fix more bugs than it will cause.

I agree. However, considering how difficult, uncertain and lengthy this process would be, more pragmatically we should in parallel lobby compiler vendors to offer a switch linked to a flag.

Yet another time, LFortran does the right thing™ by default: kudos.

In rare cases (this is one of them) it may be worthwhile to delete a feature from the language, but changing what legal code does causes errors and confusion. It’s not hard to write

integer :: i
i = 0

Maybe Fortran could add an init attribute so that
integer, init :: i = 0, j = 1
means the same as

integer :: i,j
i = 0
j = 1

with the ability to change i and j later. So the user could only give a value to a variable within a declaration if the save, parameter, or init attribute appeared. Maybe init is not the right word, but the idea is that the user is required to say what he means.

The flooring behavior of integer division often bites Fortran programmers, and starting from scratch, I’d prefer that 1/2 = 0.5 rather than 0 in Fortran, but Fortran is not Python (which changed the behavior of integer division).

4 Likes

I think that breaking such a “feature” is not a big deal, since I doubt it was used a lot. Except cases when it was used by mistake, so deleting could actually fix some hidden bugs!

@Colorizer indeed, this is a classic “gotcha” that every Fortran programmer encounters: Gotchas — Fortran90 1.0 documentation, and I think it’s time to fix it.

Thanks @Beliavsky for checking LFortran. Indeed, it is a “bug” in LFortran that it actually works as expected. But I am happy that it can actually compile it, we made huge progress lately.

@jacobwilliams will be pleased that we will have to “fix” this “bug” and actually introduce the save attribute.

Or do we?

What would you like LFortran to do?

I have a feeling, as noted by others before, that when any Fortran code actually has integer :: i=0, it is a bug in the code, it was not meant to add the save attribute.

I would like the default mode in LFortran to be the “strict” mode: Create pedantic/strict mode (#450) · Issues · lfortran / lfortran · GitLab, in there it’s already specified that “implied save” (the issue above) would not be allowed. So it would just refuse to compile it, with a very helpful error message how to fix it.

4 Likes

Initializing non-constants in modules and programs is perfectly fine, as save is inconsequential there.

1 Like

I think the best we can hope for as far as the standard is concerned (at least as long as backward compatibility at all cost rules the committee) is that this feature is deleted from the standard. Then it gets flagged as a syntax error when you use the standard conforming compile flag. So we give up any hope of using this syntax for anything else.

If it was up to me, we would change the behavior to be what everyone expects: to be equivalent to a declaration and a normal assignment. It’s a very natural syntax for that, and makes perfect sense for people coming in from other languages. Like I say, this would fix more bugs than it causes. I see it over and over again, year after year after year, people getting bitten by this dumb implicit save behavior. I have never seen anyone use it on purpose.

1 Like