Weird behaviour when using optimization

I see… I agree that allocation status could be changed by intent(out), and I feel it is quite reasonable (it is in the same category as I put “unless it is a derived-type object”). What I feel problematic is that Fortran could change the value of a simple scalar argument (like in the OP’s code) or array elements, because Fortran allows it to be used for code optimization (that is what I’ve read somewhere). I feel this the design of intent(out) in the standard is quite problematic(*1), and I never use it in my codes (except when it is really necessary for some purpose).

(*1) For example, if one wants to revert the status of a derived-type object to the “initial” state, there could be such an intrinsic procedure or built-in method for that purpose, rather than automatically and implicitly doing that upon entry of a routine (which makes the meaning of “intent” unclear IMHO).

The design of intent(out) is both for code safety (detecting a non assigned intent(out) argument whenever possible) and for optimization:

In the OP example it allows the compiler to skip an assignment.

You can also think about the copy-in/copy-out situations: the compiler can skip the copy-in in the case of an intent(out) argument (and it can skip the copy-out in the case of an intent(in) argument).

I don’t agree with the first part of that sentence, but I do agree with the last part. The intent(out) attribute should be used with purpose, not just by whim. Your example of a derived type with default initialization is another good example of the difference between intent(out) and intent(inout). Another situation is a derived type with a finalizer. In this case, the intent(out) actually invokes a function call, while intent(inout) doesn’t. When used with purpose, intent(out) makes the code simpler and clearer, not the opposite.

Yes - fpt will always catch this one. If you simply run fpt on the code with no checking commands you get:

Diagnostic Summary: 1 errors, 0 warnings, 0 notes
=================================================
Errors
======

 No.  Description                                                   Count

4555 INTENT(OUT or INOUT) argument is never assigned                    1

```

and the output code is:

MODULE m
        IMPLICIT NONE
!
        CONTAINS
        SUBROUTINE foo(k)
        INTEGER,INTENT(OUT) :: k
!-------------------------------^-----------------------------------------------
!!! FPT - 4555 INTENT(OUT or INOUT) argument is never assigned
!-------------------------------------------------------------------------------
        END SUBROUTINE foo
END MODULE m
PROGRAM demo
!
        USE m
        INTEGER :: k
!
        k = 55	  !<<<<<<<<<<<<< with optimization this has no effect!!!
        CALL foo(k)
        PRINT *,"k = ",k
!
END PROGRAM demo

It will catch this even if k is passed down to other routines but not assigned in them.

There are also specific commands to look at INTENT in more detail.

2 Likes

Excellent!

Does that also work on the original ill-posed example posted?

module m
    implicit none
contains
    subroutine foo(done)
        logical, intent(out) :: done
        if (10 < 5) then
            done = .TRUE.
        end if
    end subroutine
end module


program demo
    use m
    implicit none
    logical :: done

    done = .FALSE.
    call foo(done)
    print *, "done: ", done

end program demo

I can imagine situations were it would be difficult for a tool to prove if a variable was never assigned to, or that might be only determinable at runtime.

fpt didn’t pick this up, but it should, on two counts:

i. The flow analysis should detect that the code in the IF block is unreachable

ii. Even if it were conditionally reachable it should detect that there is a path where the INTENT(OUT) argument is not assigned. Mea culpa - I shall fix it!

4 Likes

I think that it is allowed (i.e. not an error) for the intent(out) variable to remain undefined. The error in this little example code only occurs later when it is referenced in the print statement. If it had been defined later, or if it were not referenced later in the print statement, then there would be no error, right?

It is up to the programmer to ensure that the values of undefined variables are not used, so this can make for tricky situations. When I write code with intent(out) variables, I sometimes assign some values right away, even before the arguments are tested for correctness, just in case a return occurs before the actual defining code is executed.

2 Likes

It is up to the programmer, when the compiler implements merely what is required as a minimum by the language spec. That spec also includes an exhaustive list of the conditions that result in undefined value and association status. These cost runtime resources to maintain as each statement is executed. But a compiler is free to choose to maintain this extra information. It can then determine, at runtime, to error terminate the program rather than continue execution with a violated condition.