A questão é exatamente o título: Fortran permite alterar membros de dados de tipo derivado SUBROUTINE
mesmo com INTENT(IN)
, como proteger dados? Sou bem novo em Fortran e esse comportamento de permitir a mutação de membros de dados de um tipo derivado apesar do INTENT(IN)
atributo no argumento fictício de tipo derivado é bem preocupante para mim. Existe uma maneira de capturar esse tipo de mutação insegura com o compilador? Por exemplo, gfortran -Wall
não parece fazer isso, mas talvez esse seja um problema comum o suficiente em bases de código legadas ou mesmo modernas para onde alguma detecção estática do problema esteja disponível de alguma forma?
Programa de exemplo:
! @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
Saída:
1
2
3
4
5
-1
2
3
4
5
De acordo com as regras da linguagem, o código de exemplo não resultou em uma alteração no valor do
p_states
argumento fictício em si; em vez disso, o que foi alterado foi o valor de algo "apontado" por um componente dop_states
argumento fictício.Algo referenciado por um componente de ponteiro (o alvo do componente de ponteiro) não é considerado parte do valor do objeto com o componente.
Ponteiros no Fortran moderno são tipicamente usados para referenciar coisas. Se você quer algo que tenha as características dinâmicas de um ponteiro (pode ser criado e destruído com características diferentes em tempo de execução), mas se comporta como parte de um valor em vez de uma referência, então use componentes ALLOCATABLE.
Em termos de padrões - veja (todas as referências F2023):
Nota 3 na descrição do atributo INTENT (8.5.10), que responde diretamente a esta questão;
como o atributo INTENT se propaga para subobjetos (também 8.5.10) de um objeto não ponteiro;
que a definição do subobjeto (9.4.2) tem condições em torno do número, localização e natureza do uso de componentes com o atributo ponteiro; e
que a definição do valor do tipo derivado (7.5.8) exclui o valor dos objetos referenciados pelos componentes do ponteiro, mas inclui o status de associação do ponteiro desses componentes.