The question is exactly the title: Fortran allows changing derived type data members in SUBROUTINE
even with INTENT(IN)
, how to protect data? I am quite new to Fortran and this behavior of allowing mutating a derived type's data members despite the INTENT(IN)
attribute on the derived type dummy argument is quite concerning to me. Is there a way to catch these sort of unsafe mutations with the compiler? For example, gfortran -Wall
does not seem to do this, but maybe this is a common enough problem in legacy or even modern codebases to where some static detection of the problem is available somehow?
Example program:
! @file main.f90
!
! @purpose: Show data members of derived types are mutable in SUBROUTINE
!
! @compile: gfortran -Wall main.f90
PROGRAM test
IMPLICIT NONE
INTEGER :: n_states = 1
INTEGER :: len_state = 5
INTEGER :: i, j
TYPE t_states
INTEGER, POINTER, CONTIGUOUS :: state(:) ! (len_state,)
END TYPE t_states
TYPE(t_states), ALLOCATABLE :: states(:) ! (n_states,)
ALLOCATE(states(1:n_states))
! Populate allocated array of states
DO i = 1, n_states
ALLOCATE(states(i)%state(len_state))
DO j = 1, len_state
states(i)%state(j) = j
PRINT *, states(i)%state(j)
END DO
PRINT *
END DO
CALL unsafe_mutation(states(1))
! Print state array after modification
DO j = 1, len_state
PRINT *, states(1)%state(j)
END DO
CONTAINS
! Arbitrary update of state data member even though INTENT(IN)!!!
SUBROUTINE unsafe_mutation(p_states)
TYPE(t_states), INTENT(in) :: p_states
p_states%state(1) = -1
END SUBROUTINE
END PROGRAM test
Output:
1
2
3
4
5
-1
2
3
4
5
Under the rules of the language, the example code did not result in a change in the value of the p_states
dummy argument itself - instead what was changed was the value of something "pointed at" by a component of the p_states
dummy argument.
Something referenced by a pointer component (the target of the pointer component) is not considered part of the value of the object with the component.
Pointers in modern Fortran are typically used to reference things. If you want something that has the dynamic characteristics of a pointer (can be created and destroyed with different characteristics at runtime) but behaves like part of a value rather than a reference, then use ALLOCATABLE components.
Standard wise - see (all F2023 references):
Note 3 under the INTENT attribute description (8.5.10), which pretty much directly answers this question;
how the INTENT attribute propagates to subobjects (also 8.5.10) of a non-pointer object;
that the definition of subobject (9.4.2) has conditions around the number, location and nature of use of components with the pointer attribute; and
that the definition of the value of derived type (7.5.8) excludes the value of objects referenced by pointer components, but includes the pointer association status of those components.