fortran

Changing derived type data members in `SUBROUTINE` is allowed even with INTENT(IN). How to protect data?


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

Solution

  • 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):