https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116829
Bug ID: 116829
Summary: Missing default initialization of finalizable
non-polymorphic intent(out) arguments
Product: gcc
Version: 15.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: fortran
Assignee: unassigned at gcc dot gnu.org
Reporter: trnka at scm dot com
Target Milestone: ---
When a derived type dummy argument with intent(out) is finalizable (e.g. has a
final subroutine), gfortran (at least as far back as GCC 9) triggers
finalization on function entry but fails to default-initialize the argument
afterwards, leaving it in the state where finalization left it. This is in
violation of the Fortran standard, which specifies that subcomponents of
intent(out) arguments should be default-initialized as usual (F2018 8.5.10
paragraph 3) and that finalization should occur right before that (7.5.6.3
paragraph 7).
The following testcase reproduces the issue, printing the invalid value "42"
instead of the expected "0" defined by the initializer:
---------------------------
module MinimalIntentOutTestModule
implicit none
type :: AapType
integer :: i = 0
contains
final :: Finalizer
end type
contains
subroutine Finalizer(self)
type(AapType), intent(inout) :: self
write(*,*) "in Finalizer:", self%i
self%i = 42 ! Nobody should ever see this value after finalization
end subroutine
end module
program uninit_intent_out_minimal
use MinimalIntentOutTestModule
implicit none
type(AapType) :: aap
aap%i = 1
call MakeAap(aap)
contains
subroutine MakeAap(a)
type(AapType), intent(out) :: a
write(*,*) "in MakeAap: ", a%i
end subroutine
end program
-------------------------
This only affects type(X) intent(out) dummy arguments; class(X) intent(out)
arguments are initialized correctly.
The bug seems to be in init_intent_out_dt(), which for non-polymorphic
(DT_DERIVED) arguments forgets to call gfc_init_default_dt() if the symbol is
finalizable. The initialization code is stored in sym->value by
resolve_fl_variable_derived() called from resolve_symbol(), but without a call
to gfc_init_default_dt() it is never assigned to the actual variable.
Polymorphic arguments are unaffected because they use a slightly different
path. Instead of resolve_fl_variable_derived() setting sym->value,
resolve_symbol() will call apply_default_init() because sym->value is unset,
which produces a GFC_INIT_ASSIGN. This is later translated to a memcpy of
_def_init in the function body. init_intent_out_dt() then goes through the
else-if branch and only adds a finalizer call because initialization is already
taken care of.