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.

Reply via email to