3

This is a follow up to a post that I found on SO: Difference between intent(out) and intent(inout)

The linked question asked about the difference between intent(out) and intent(inout) in Fortran by asking an invalid program.

Could anyone come up with a simple valid program(s) that give different results by changing intent(inout) to intent(out) or vice versa?

2 Answers2

5

Here you go...

program intent_test
implicit none
integer, allocatable :: a(:)

a = [1,2,3,4,5]

call intent_inout (a)

call intent_out (a)

contains

subroutine intent_inout (a)
integer, allocatable, intent(inout) :: a(:)

if (allocated(a)) then
  print *, a
else
  print *, "Unallocated"
end if

end subroutine intent_inout

subroutine intent_out (a)
integer, allocatable, intent(out) :: a(:)

if (allocated(a)) then
  print *, a
else
  print *, "Unallocated"
end if

end subroutine intent_out

end program intent_test

1 2 3 4 5 Unallocated

Steve Lionel
  • 6,972
  • 18
  • 31
  • 1
    +1. Thanks for the quick response, Steve! An array is used in this example while @francescalus uses `type` to do the "trick". It makes me wonder that if one only uses `integer` variables and subroutines that only perform simple arithmetics or use just the `print` function, then whether it is possible that `out` and `inout` would always give the same result. (That would be another question, of course.) I doubt that it is unlikely so. But having played around with some examples, I don't find any differences. –  Nov 06 '19 at 17:44
  • 1
    It's the `ALLOCATABLE` attribute that matters - the variable can be of any type. Per the standard, `INTENT(OUT)` means that the definition status of the dummy argument is "undefined" on entry to the procedure, but that's hard to detect in other cases. Regarding @francescalus use of "disassociates" - that makes me uneasy, Pointers get disassociated `INTENT(OUT)` causes the pointer to be undefined, not disassociated, so you can't assume or even test that the pointer is disassociated. – Steve Lionel Nov 07 '19 at 18:55
  • Yes, I was using 'disassociate' as (lazy) shorthand for 'becoming of undefined association status' (see my answer for more precise status). Note that 'pointer becomes undefined' is similarly imprecise: it's undefined (which is fine) but its association status is also undefined (which isn't fine). – francescalus Nov 07 '19 at 20:15
3

As Steve Lionel's answer, and the comments in my original answer which perhaps sparked this interest, the effects of intent(out) on the initial state of the dummy argument (and the actual argument) are a way in to answering this question.

intent(inout) has the dummy argument reflect the value of the actual argument on entry to the procedure. intent(out) "resets" the dummy argument. In the case of the linked question, this "undefinition" is the cause of the invalid nature of the program.

More precisely, we can say the following things about intent(out) dummy arguments:

  • allocatable actual arguments become deallocated;
  • the pointer association of pointer actual arguments becomes undefined;
  • for a non-pointer dummy argument any component not default initialized becomes undefined.

The linked question fell foul of the third point by trying to reference such an newly undefined value. However, default initialized components are not undefined, leading us to our first class of valid programs:

  implicit none
  type t
     integer :: x=1
  end type t
  type(t) :: x=t(0)

  call s1(x)
  call s2(x)

contains
  subroutine s1(x)
    type(t), intent(inout) :: x
    print*, x%x
  end subroutine s1

  subroutine s2(x)
    type(t), intent(out) :: x
    print*, x%x
  end subroutine s2

end

Crucially, the default initialization of the component means that x%x isn't undefined even on entry to s2, but it takes a potentially different value from the actual argument's component prior to procedure entry.

Coming up with a suitable program with pointer arguments is tricky: a pointer with undefined pointer association status can't be referenced/queried before its pointer association status is redefined.

Which leaves us looking at allocatable components. Unlike with the pointers, where we can't query pointer association status when that's undefined, we can ask about the allocation status. An actual argument corresponding to an intent(out) dummy is deallocated; with intent(inout) the allocation status is unchanged:

  implicit none
  integer, allocatable :: i
  allocate (i)

  call s1(i); print*, allocated(i)
  call s2(i); print*, allocated(i)

contains

  subroutine s1(i)
    integer, allocatable, intent(inout) :: i
  end subroutine s1

  subroutine s2(i)
    integer, allocatable, intent(out) :: i
  end subroutine s2
end

(This is a simpler version of Steve Lionel's example.)

This all shows that it's possible to have differences. Using intent incorrectly can lead to invalid programs or to significant changes in meaning. Understanding what intent(in), intent(inout), intent(out) and no specified intent mean is a crucial part of being a Fortran programmer.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Thanks! It seems to me that the difference between `intent(inout)` and `intent(out)` is quite subtle and it may be not unusual that they give the same results although the underlying mechanisms are different. –  Nov 06 '19 at 17:44
  • The important thing to remember is that `INTENT` is really meant as an aid to the programmer - it gives the compiler additional information it can use to alert you to possible errors. For example, passing a constant to an `INTENT(INOUT)` argument is an error, because `INTENT(INOUT)` requires that the actual argument be definable..This is different from omitting `INTENT`. (I have lobbied in favor of `INTENT(NONE)` as meaning the same as omitting `INTENT`.) With `INTENT(IN)` the compiler could warn you if the actual was undefined. – Steve Lionel Nov 08 '19 at 01:29
  • I am still somewhat confused, is it correct that in both cases (inout or out) I still need to declare the variable in the main program? With out the variable declaration inside the subroutine can have a different dimension than the equivalent in the main, or not? – Herman Toothrot Jun 08 '20 at 12:14
  • @HermanToothrot, I'm not sure I understand your question. However, let's see whether this helps: with `intent(inout)` and `intent(out)` there must be a corresponding (definable object/) variable in the place where the procedure is referenced (say the main program). With these intents it's not possible to use an expression as the argument. Whether array dimensions match is unrelated to any specified intent. (Note that there's no array in my examples here.) – francescalus Jun 08 '20 at 12:22