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.