Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Finalization subroutine questions

Erik_B_1
Beginner
626 Views

 

Hi,

I'm trying to solve a memory leak in a Fortran program and I'm seeing some issues with finalization routines. The following example illustrates the issue that I found:

[fortran]

module MemLeaksMod
  implicit none
 
  type :: ComponentType 
    private
    real :: value
  contains
    final :: FinalizeComponent
  end type ComponentType  

  type :: ContainerType
    class(ComponentType), allocatable :: component
  contains
    final :: FinalizeContainer
  end type ContainerType
  
contains
  subroutine FinalizeContainer(this)
    type(ContainerType), intent(inout) :: this    
    print *, "Finalizing container class"
  end subroutine FinalizeContainer

  subroutine FinalizeComponent(this)
    type(ComponentType), intent(inout) :: this  
    print *, "Finalizing component class"
    end subroutine FinalizeComponent
end module MemLeaksMod
  
program MemLeaksProg
  use MemLeaksMod
  implicit none
  call TestMemLeaks()
 contains
    ! Shows that the finalizer of the componentType class isn't called if the containerType class doesn't have a finalizer:
  subroutine TestMemLeaks
    type(ContainerType) :: container
    allocate(container%component)
  end subroutine TestMemLeaks
 end program MemLeaksProg
[/fortran]

When running the program, I get the following output: 

 Finalizing container class
 Finalizing component class

This is what I expect. Now if I comment out the 
[fortran]     final :: FinalizeContainer [/fortran]
statement for the container type, I would expect that the finalizer for ComponentType is still called. However, when running the modified code, it shows now output, so the finalizer for ComponentType wasn't called.

Is this a bug in the intel compiler or is it the expected behaviour? For me it seems that calling the ComponentType finalizer shouldn't depend on the presence of a finalizer on the ContainerType.

Another question related to finalizers. If a type has allocatable components and implements a finalizer, should the finalizer deallocate the allocatable components, or will this be done automatically (as is the case if no finalizer was implemented at all)? I tried to find the answer in "Model Fortran Explained", but it wasn't clear on this subject.

Best regards,
Erik

0 Kudos
3 Replies
IanH
Honored Contributor III
626 Views

This is something that I've been wondering about for a while.   See this post from a while back.  And maybe this one.  The latter is reassuring, because at least I've been consistent over a year or two in being confused.

A type is finalizable if it has a final binding or a non-pointer, non-allocatable component of finalizable type (4.5.6.1p2).

With a final binding, as presented, your container type meets the first half of that condition, so it is finalizable.  When you remove the final binding, it is not - there is a component that is of finalizable type, but it is allocatable, so the second half of the condition doesn't apply.  But what does that mean...?  The Intel compiler appears to think that because the component is no longer part of a finalizable type, it isn't finalized. 

But... an allocatable component is a allocatable "entity", entity just being a fancy way of writing "thing".  And "when an allocatable entity is deallocated, it is finalized" (4.5.6.3p1). 

And when an unsaved local variable of derived type goes "out of scope" (using general terminology), any allocatable components are deallocated (6.7.3.2.p2 and p3, basically, being mindful that components of a local variable are themselves a local variable). 

[That actually answers your second question - finalization and deallocation are linked in terms of timing, but they are still separate "actions" - you don't need to deallocate allocatable components in your finalizers.]

So completely independent of the "finalizable" status of the parent (container) object, we've got a trigger for finalization, of something that is finalizable.  Which I take to mean... that `component` in your example should be finalized.  The parent `container` thing being finalizable just gives the programmer more control over the sequence of finalization - the programmer can (if they want to... they don't have to as per the [ ] bit above) explicitly deallocate an allocatable component in the finalizer of the parent to ensure a certain finalization order. 

At least, I hope this is the case, because otherwise there's a nasty and confusing difference between explicit deallocation and automatic deallocation.  My hope does implicitly require that there's a prohibition of finalizing something twice.

But I have to admit, I don't understand why the standard has been written the way it has.  Something doesn't quite seem right with respect to allocatable components.  Why are they significant in determining whether a type is finalizable?  What does the term "finalizable component" mean?  Perhaps the good doctor could shed some light...

0 Kudos
Mark_Lewy
Valued Contributor I
626 Views

I think the key point here is from 4.5.6.3p1 "When an allocatable entity is deallocated, it is finalized." which suggests that the (implicit) deallocation of container%component at the end of TestMemLeaks should call FinalizeComponent regardless of whether ContainerType is finalizable of not.

My interpretation of 4.5.6.1p2 is that it enables a non-allocatable non-pointer component of a type to be finalized as per 4.5.6.2p1 (2) even if the type has no final procedure(s) declared.

For example, if component is declared instead in ContainerType as

type(ComponentType) :: component

you should expect FinalizeComponent to be called at the end of TestMemLeaks regardless of whether ContainerType has a final procedure or not (which seems to be what is happening, at least with XE 2013 SP1 Update 1).

0 Kudos
Erik_B_1
Beginner
626 Views

Thanks for the replies. It's good to know that I'm not the only one who's confused about finalizers. 

I found some other issues with finalizers as well, but since I'm running an older version of the intel compiler, probably I should first try with the latest version. For the moment I won't be doing this since our project runs into this error (http://software.intel.com/en-us/forums/topic/487151) with the latest version of the compiler. 

0 Kudos
Reply