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

Finalizer: only executed on scallar allocatable object.

rudi-gaelzer
New Contributor I
807 Views

In just want to confirm if this is the expected behavior of a finalizer.

In the code below,

module tes_final_mod
implicit none
type :: my_final
   integer :: n
   contains
      final :: destroy
end type my_final
contains
   subroutine destroy(self)
   type(my_final) :: self
   write(*,*), 'Destroy executed!'
   end subroutine destroy
end module tes_final_mod
!=============================
program tes_finalizer
use tes_final_mod
implicit none
type(my_final), allocatable :: a(:), b(:)
type(my_final), allocatable :: c
!
allocate(a(10))
a%n= 1
deallocate(a)
print*, 'a deallocated'
allocate(a(10))
a%n= 3
allocate(b(20))
b(1:10)= a
call move_alloc(b, a)
print*, 'b deallocated'
allocate(c)
c%n= 2
deallocate(c)
print*, 'c deallocated'
call call_final
contains
   subroutine call_final
   type(my_final) :: aa
   print*, 'SUB: Will finalize when returned.'
   return
   end subroutine call_final
end program tes_finalizer

I have the following result, when compiled with ifort Version 15.0.3.187 Build 20150407:

 a deallocated
 b deallocated
 Destroy executed!
 c deallocated
 SUB: Will finalize when returned.
 Destroy executed!

That is, the finalizer only runs when SCALAR allocatable objects are deallocated (object c in the main and aa in the subroutine).  Allocatable arrays are not finalized either by directly killing then with a deallocate (array a, first time) or by the move_alloc (array b).

Is this the expected behavior?

Even stranger, when the very same program is compiled with gfortran, the results are almost the same, with the exception of the last call on the finalizer, when the subroutine is exited.  With gfortran the return statement did not cause the finalizer to run.

0 Kudos
8 Replies
rudi-gaelzer
New Contributor I
807 Views

Never mind. Now I understand what's written in Metcalf's book:

A final subroutine for a type must be a module procedure with a single dummy argument
of that type. All the final subroutines for that type form a generic set and must satisfy the
rules for unambiguous generic references; since they each have exactly one dummy argument
of the same type, this simply means that the dummy arguments must have different kind
type parameter values or rank. Each such dummy argument must be a variable without the

All I need to do is add another procedure on the FINAL :: list.  The new procedure must have an array dummy argument:

   subroutine destroy_arr(self)
   type(my_final) :: self(:)
   write(*,*) 'Destro_arr executed!'
   end subroutine destroy_arr

Now the new finalizer is called both by the deallocate(a) and  move_alloc(b,a).

0 Kudos
IanH
Honored Contributor II
807 Views

With the recent compiler release, you may find use of an impure elemental procedure for the finalizer helpful, as it will be selected regardless of the rank of the entity being finalized.

0 Kudos
rudi-gaelzer
New Contributor I
807 Views

ianh wrote:

With the recent compiler release, you may find use of an impure elemental procedure for the finalizer helpful, as it will be selected regardless of the rank of the entity being finalized.

Ok, thanks.  Soon I'l upgrade the compiler to version 16.

I forgot to mention that the executable compiled with gfortran still fails to run the finalizer on returning from the subroutine.  Perhaps it's a bug on gfortran's implementation?

0 Kudos
IanH
Honored Contributor II
807 Views

I think there are bugs with both ifort and gfortran in this area.  With a modified test program (so the letters refer to different things), ifort fails to finalize the allocatable entity initially designated by `c` that is deallocated when the allocation of `b` is moved across to `c` (4.5.6.3p1).  gfortran recent trunk fails to finalize the rank one array section of `b` when it is assigned to with the value of `c` (4.5.6.3p9).  This is in addition to your observation that gfortran fails to finalize the local variable `aa` of the internal procedure in your example (which is likely related to the fact that `aa` is neither referenced or defined in that internal procedure, and so the compiler perhaps pretends it doesn't exist) (4.5.6.3p2).  These observations are consistent regardless of whether multiple non-elemental finalizers are used, or a single impure elemental finalizer is used.

module tes_final_mod
  implicit none
  type :: my_final
    integer :: n = 0
  contains
    final :: destroy_scalar
    final :: destroy_rank1_array
  end type my_final
contains
  subroutine destroy_scalar(self)
    type(my_final), intent(inout) :: self
    write (*,"(A,1X,I0)"), 'Destroy executed!', self%n
  end subroutine destroy_scalar
  
  subroutine destroy_rank1_array(self)
    type(my_final), intent(inout) :: self(:)
    integer :: i
    do i = 1, size(self)
      write (*,"(A,1X,I0)"), 'Destroy executed!', self(i)%n
    end do
  end subroutine destroy_rank1_array
end module tes_final_mod

program test_finalizer
  use tes_final_mod
  implicit none
  type(my_final), allocatable :: a(:), b(:), c(:)
  type(my_final), allocatable :: d

  allocate(a(3))
  a%n = 1
  deallocate(a)               ! (3 * n = 1)
  print*, 'a deallocated'
  
  allocate(b(8))
  b%n = 2
  allocate(c(4))
  c%n = 3
  b(1:4) = c                  ! (4 * n = 2)  <<<<< gfortran
  print *, 'c assigned to part of b'
  call move_alloc(b, c)       ! (4 * n = 3)  <<<<< ifort
  print*, 'allocation of b moved to c'
  
  allocate(d)
  d%n = 4
  deallocate(d)               ! (1 * n = 4)
  print*, 'd deallocated'
  
  call call_final
  
  ! c is still allocated here, but note the language standard specifies 
  ! that it is NOT finalized when the program terminates.
contains
  subroutine call_final
    type(my_final) :: aa
    aa%n = 5
    print*, 'SUB: Will finalize when returned.'
    return
  end subroutine call_final   ! (1 * n = 5)
end program test_finalizer

 

(Note windows variant of ifort, but I'd be surprised if behaviour was different for the other operating systems.)

>ifort /check:all /warn:all /standard-semantics "2015-09-07 final.f90" && "2015-09-07 final.exe"
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0 Build 20150815
Copyright (C) 1985-2015 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

"-out:2015-09-07 final.exe"
-subsystem:console
"2015-09-07 final.obj"
Destroy executed! 1
Destroy executed! 1
Destroy executed! 1
 a deallocated
Destroy executed! 2
Destroy executed! 2
Destroy executed! 2
Destroy executed! 2
 c assigned to part of b
 allocation of b moved to c
Destroy executed! 4
 d deallocated
 SUB: Will finalize when returned.
Destroy executed! 5

In the above, I expect to see "Destroy executed! 3" four times immediately before the "allocation of b moved" message.

I will report the gfortran observations to its developers.

0 Kudos
rudi-gaelzer
New Contributor I
807 Views

Dear IanH.

This is the output of your program compiled in linux (Fedora 22) with ifort V. 15.0.3.187 Build 20150407, without any compilation option:

Destroy executed! 1
Destroy executed! 1
Destroy executed! 1
 a deallocated
Destroy executed! 19227248
Destroy executed! 0
Destroy executed! 4
Destroy executed! 0
 c assigned to part of b
 allocation of b moved to c
Destroy executed! 4
 d deallocated
 SUB: Will finalize when returned.
Destroy executed! 5

As you reported, it fails to finalize b after calling move_alloc(b,c).  However, in one of my tests, I've defined the finalizer:

   subroutine destroy_arr(self)
   type(my_final) :: self(:)
   write(*,*) 'Destroy_arr executed!'
   end subroutine destroy_arr

without the intent(inout) and in this case, it is called after the move_alloc.

Another behavior that I've noticed and find strange is that the finalizer is called after a simple copy operation, like your

b(1:4) = c 

Is this expected?  To run the finalizer after a copy operation?  Moreover,  note the strange values of n reported by the routine.  It seems that the program creates a temporary allocation array of type my_final with  random values of n, and these are the ones that are destroyed.

This is the output obtained with gfortran, again with no options:

Destroy executed! 1
Destroy executed! 1
Destroy executed! 1
 a deallocated
 c assigned to part of b
Destroy executed! 3
Destroy executed! 3
Destroy executed! 3
Destroy executed! 3
 allocation of b moved to c
Destroy executed! 4
 d deallocated
 SUB: Will finalize when returned.
Destroy executed! 5

 

0 Kudos
IanH
Honored Contributor II
807 Views

rudi-gaelzer wrote:

Another behavior that I've noticed and find strange is that the finalizer is called after a simple copy operation, like your

b(1:4) = c 

Is this expected?  To run the finalizer after a copy operation?  Moreover,  note the strange values of n reported by the routine.  It seems that the program creates a temporary allocation array of type my_final with  random values of n, and these are the ones that are destroyed.

Yes - finalization is invoked for intrinsic assignment, see F2008 4.5.6.3p9.  Conceptually the lifetime of the value that the array section variable b(1:4) held prior to the assignment comes to an end with that assignment.  Note that you don't need deallocation in order to get finalization.

I am using 16.0 initial release.  I do not see strange values of n for my test cases.  If you are going to use the newer language features, you will want to be as current as possible.

 

0 Kudos
Kevin_D_Intel
Employee
807 Views

I reported the defect to Development that IanH noted with ifort in post #5. The same incorrect results noted in the post occur on Linux with 16.0.

(Internal tracking id: DPD200375877)

(Resolution Update on 02/23/2016): This defect is fixed in the Intel® Parallel Studio XE 2016 Update 2 Release (PSXE 2016.2.062/ CnL 2016.2.181 - Linux)

0 Kudos
Kevin_D_Intel
Employee
807 Views

The finalization defect (detailed in post #5) has been fixed in the recently released Intel® Parallel Studio XE 2016  Update 2 release (PSXE 2016.2.062/ CnL 2016.2.181 - Linux).

0 Kudos
Reply