Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing intent(in) variables through a pointer

I'm wondering if it is legal/safe to modify intent(in) dummy arguments through pointers associated with corresponding actual arguments. Specifically when the pointer association was established before the subroutine call.

For example, is the following OK (it seems to work fine with gfortran)?

program test
implicit none

type compound
  integer, allocatable :: A1(:)
  integer, pointer :: A2(:,:)
end type compound

type(compound), target :: my
integer :: n=5, i

allocate(my%A1(n**2))
my%A2(1:n,1:n) => my%A1(:)

do i=1,n**2
  my%A1(i) = i
end do

do i=1,n
  print *, my%A2(i,:)
end do

call assign_A(my)

do i=1,n
  print *, my%A2(i,:)
end do

contains

subroutine assign_A(var)
type(compound), intent(in) :: var
var%A2(:,:) = 42
end subroutine

end program test

The trick is a user-defined type that contains a pointer and a target, the former pointing to the latter. An instance of this is passed as intent(in) and in the subroutine it is modified through the pointer component (the values pointed by intent(in) pointers can be modified). I'm a bit surprised that this works, but maybe it's just a missing diagnostic by the compiler. If I was changing var%A1 directly that would of course fail.

EDIT: Output from the above program (compiled with gfortran 7.5.0):

       1           6          11          16          21
       2           7          12          17          22
       3           8          13          18          23
       4           9          14          19          24
       5          10          15          20          25
      42          42          42          42          42
      42          42          42          42          42
      42          42          42          42          42
      42          42          42          42          42
      42          42          42          42          42
like image 677
Jellby Avatar asked Dec 31 '22 12:12

Jellby


1 Answers

You are not allowed to change the value of an intent(in) object in this way. The compiler does not have to (be able to) tell you that you are doing something wrong. (Further, it is an unreasonable expectation to have on a compiler that it should be able to detect all cases where an illegal change is made.)

The dummy argument var is a non-pointer object, and this means that its components also have the same intent attribute. You are trying to define var and its component var%A1 by the assignment var%A2(:,:) = 42 because var%A2 is pointer associated with my%A1 (through the argument association of var with my); through argument association of var and my, definition of my%A1 is definition of var%A1.

This is a violation of the statement that (F2018 8.5.10 p2):

The INTENT (IN) attribute for a nonpointer dummy argument specifies that it shall neither be defined nor become undefined during the invocation and execution of the procedure.

However, this is not a numbered constraint or syntax rule so the compiler is not required to be capable of diagnosing the violation. Where a compiler is required to be capable of diagnosing a violation for changing var is in (F2018 C844 (R826)):

A nonpointer object with the INTENT (IN) attribute shall not appear in a variable definition context (19.6.7).

A variable definition context is specific, and the assignment statement of the question is not a variable definition context for var or var%A1. An assignment such as var%A1=42 (which you note your compiler complains about) is appearance, of var%A1 (but not var), in a variable definition context.

You can also wonder whether the aliasing restrictions come into play.

In summary, it is possible to hide changes to a dummy variable with intent(in) from your compiler. Just as programmers don't like being tricked, your compiler doesn't have to still be your friend if you do this. The compiler can assume var doesn't change value during the subroutine and it owes you nothing if you find a way to subvert that.

like image 192
francescalus Avatar answered Jan 14 '23 03:01

francescalus