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

A Problem about the Final Procedure

史_建鑫
Beginner
464 Views



A Struct Testype2 contained the subobject of Testype1. I defined the final procedure which contain a write statement for both the two Type.

case1: I define a allocatable varaiable of testype2, after I deallocate it, the final procedure of the two type both invoked. I think this is the right result.

case2: I define a allocatable array of testype2 which contained 10 items, after I deallocate it, the testype1 final procedure was invoked 10 times, but the testype2 final procedure wasn't invoked. I don't know why.

can anybody help me? Steve~ Help~~

case1 code:
[fortran]
module mod1
    
    implicit none

    type :: testype1
        
        integer :: i
    
    contains
    
        procedure :: PrintTT1
        Final :: FinalTT1
        
    end type testype1
    
    type :: testype2
       
        type(testype1) :: tt1
        
    contains
    
        Final :: FinalTT2
        
    end type testype2
    
contains

    subroutine PrintTT1(this)
    
        class(testype1) :: this
        write(*,*) "this.x=", this.i
        
    end subroutine PrintTT1
    
    subroutine FinalTT1(this)
    
        type(testype1) :: this
        
        write(*,*) "Final Testype1"
        
    end subroutine FinalTT1
    
    subroutine FinalTT2(this)
    
        type(testype2) :: this
        
        write(*,*) "Final Testype2"
        
    end subroutine FinalTT2
        
end module mod1

subroutine TestFinal()

    use mod1
    
    type(testype2), allocatable :: tt2
    allocate(tt2)
    tt2.tt1.i = 3
    call tt2.tt1.PrintTT1()
    
    deallocate(tt2)
    
end subroutine TestFinal

program main

    implicit none

    call TestFinal()    
    
end program main

[/fortran]

case2 code:

[fortran]

module mod1
    
    implicit none

    type :: testype1
        
        integer :: i
    
    contains
    
        procedure :: PrintTT1
        Final :: FinalTT1
        
    end type testype1
    
    type :: testype2
        
        type(testype1) :: tt1
        
    contains
    
        Final :: FinalTT2
        
    end type testype2
    
contains

    subroutine PrintTT1(this)
    
        class(testype1) :: this
        write(*,*) "this.x=", this.i
        
    end subroutine PrintTT1
    
    subroutine FinalTT1(this)
    
        type(testype1) :: this
        
        write(*,*) "Final Testype1"
        
    end subroutine FinalTT1
    
    subroutine FinalTT2(this)
    
        type(testype2) :: this
        
        write(*,*) "Final Testype2"
        
    end subroutine FinalTT2
        
end module mod1

subroutine TestFinal()

    use mod1
    type(testype2), allocatable, dimension(:) :: tt2
    integer :: m
    
    allocate(tt2(10))
    do m=1,10
        tt2(m).tt1.i = m
        call tt2(m).tt1.PrintTT1()
    end do
    
    deallocate(tt2)
    
end subroutine TestFinal

program main

    implicit none

    call TestFinal()    
    
end program main


[/fortran]

0 Kudos
7 Replies
IanH
Honored Contributor II
464 Views

Resolution of final procedures is based on the rank of the thing being finalised.  In case 2, tt2 is a rank one object.  The only procedure for finalisation of objects of the type of tt2 (testtype2) is a non-elemental and takes a scalar dummy argument - this doesn't match the rank of tt2 hence no final procedure is called.

Inside the type testtype2 is a scalar component tt1 of type testtype1.  Thta type has a final binding that takes a scalar dummy argument.  This matches the rank of the ten scalar things being finalised and hence it is called - once for each scalar thing.

Note that if your final procedure was elemental, it would match an object being finalised of any rank.  External IO in such a procedure would require support for F2008's IMPURE procedure attribute.  Alternatively you can create specific bindings for each rank that you want to support.

0 Kudos
史_建鑫
Beginner
464 Views

Thanks lanH.

your explanation was clear enough that I thought I got it. However, I have one more question:
in case 2, although the final procedure of testype2 was not invoked, but I think the memory space occupied by the variable tt2 was deallocated, am I right?

0 Kudos
IanH
Honored Contributor II
464 Views

Yes.  Deallocation might result in finalisation, but finalisation is not a requirement for deallocation.

0 Kudos
史_建鑫
Beginner
464 Views

lanH, if i change tt1 to allocatable, I monitor the memory, I find the tt1 is released, but the final procedure of testype1 was not invoked. How should i change the final procedure to let the testype1 final procedure invoke automaticaly? Thank you!

[fortran]
module mod1
    
    implicit none
    
    type :: testype1
    
        integer, dimension(10000000) :: i
        
    contains
        
        final :: FinalTT1
        
    end type testype1
    
    type :: testype2
    
        type(testype1), allocatable :: tt1
    
    contains
    
        final :: FinalTT2
        
    end type testype2
    
contains
    
    subroutine FinalTT1(this)
    
        type(testype1), intent(in) :: this
        
        write(*,*) "Final Testype1"
        
    end subroutine FinalTT1
    
    subroutine FinalTT2(this)
    
        type(testype2), intent(in) :: this
        
        write(*,*) "Final Testype2"
        
    end subroutine FinalTT2
    
end module mod1

subroutine Foo()
    
    use mod1
    implicit none
    
    type(testype2), pointer :: tt2
    
    allocate(tt2)
    allocate(tt2.tt1)
    tt2.tt1.i = 4
    deallocate(tt2)
    
end subroutine Foo
    
program main
    
    implicit none

    call Foo()

end program main
[/fortran]

0 Kudos
IanH
Honored Contributor II
464 Views

That's probably a compiler bug.  I've encountered a few gaps with finalization support.

Note that the standard structure reference syntax uses "%", not ".".

0 Kudos
史_建鑫
Beginner
464 Views

lanH,  How should i deal with such case. If I deallocate the tt1 in the testype2 final procedure explicitly, then the final procedure of testype1 would be invoked. So, was the explicitlt deallocate in final procedure always be the best option?

0 Kudos
IanH
Honored Contributor II
464 Views

It depends on what you are trying to do. 

There is no need to explicitly deallocate allocatable components or allocatable variables (unless you are about to reallocate them to something different).  tt1 is an allocatable component.  When its parent tt2 is deallocated, tt1 will also be deallocated.  The final procedure for either tt1 or tt2 need not play any role in this.

If you have pointer components or pointer variables, the first question I would be asking is ... "why?".

The last time I checked in any detail was with some version of 13.0 - but I currently avoid relying on finalization with ifort because there were too many gaps.  If I need to clean up pointers or whatever in derived types then I write and explicitly call a procedure that does that clean-up.  That procedure is written in a way that means that in the future it may easily be made a final binding.  This is a separate issue to deallocation of allocatable components, which is robust unless you are playing with polymorphic components.

0 Kudos
Reply