Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question on finalization standards

144 views
Skip to first unread message

Andrew Benson

unread,
Sep 24, 2021, 1:02:10 PM9/24/21
to
I'm working (with much help from Paul Thomas) on adding finalization on intrinsic assignment to gfortran. There are a few instances where I'm unclear on precisely what is required by the standard, so would be very grateful for any insight any one here can offer.

The current issue is related to this example code:

module testmode
implicit none

type :: simple
character(4) :: scope="NONE"
integer :: ind
contains
final :: destructor1
end type simple

contains

subroutine destructor1(self)
type(simple), intent(inout) :: self
print *, "destructor1(", self%scope, ") ", self%ind
end subroutine destructor1

end module testmode

program test_final
use testmode
implicit none

type(simple), allocatable :: MyType
type(simple) :: ThyType = simple("MAIN", 21)

print *, "[1] ifort finalizes here, whereas gfortran does not."
MyType = ThyType

end program test_final

The new implementation of finalization on intrinsic assignment that I have in gfortran does not finalize MyType here, because it is not allocated prior to the assignment. This seems reasonable. But, Intel ifort does call the finalizer for MyType in this case.

Looking in the Fortran 2003 standards, section 4.5.5.2 "When finalization occurs" states:

"When an intrinsic assignment statement is executed, variable is finalized after evaluation of expr and before the definition of variable."

which doesn't really make it clear to me what should happen in this situation.

FortranFan

unread,
Sep 24, 2021, 3:51:04 PM9/24/21
to
On Friday, September 24, 2021 at 1:02:10 PM UTC-4, abe...@carnegiescience.edu wrote:

> I'm working (with much help from Paul Thomas) on adding finalization on intrinsic assignment to gfortran. There are a few instances where I'm unclear on precisely what is required by the standard, so would be very grateful for any insight any one here can offer.
>
> ..
> Looking in the Fortran 2003 standards, section 4.5.5.2 "When finalization occurs" states: ..

@abe...@carnegiescience.edu,

Thank you for taking the initiative to work on this in collaboration with Paul Thomas, that's great news for gfortran.

Here're some quick suggestions:

1) Discuss and decide with the gfortran team the plan for this, specifically which standard edition do you intent to target. I write this because you mention 2003 standard, but note there have been some revisions of significant consequence since, some of which were captured in Fortran 2008 Corrigenda that later went to official 2018 standard but which didn't catch everyone's attention. My suggestion will be to go with 18-007r1 document toward Fortran 2018 as your primary reference, but please do keep an eye an out during your planning and work for the *tweaks* that will indeed go into Fortran 202X when it gets published.

2) You may also want to consider other online resources such as the Fortran Discourse site (https://fortran-lang.discourse.group/) and GitHub Fortran Issues (https://github.com/j3-fortran/fortran_proposals) to engage with the Community on any and all topics that come up as you pursue this. This can be especially important toward the topic at hand - finalization - where you would do well to beware of *possible* "bugs" everywhere (!!) including in the standard and other compiler implementations. This is simply due to its history starting 2003 revision. So you can proceed to trust the standard, but please do try to *verify* everything and to crosscheck with the standards committee if anything doesn't make sense to you!

I'll provide my comments on your code example later.

Best,


John

unread,
Sep 24, 2021, 4:25:26 PM9/24/21
to
FYI: nvfortran does not, as well. I do find it unclear as to which it should do. Even though two for and one against from the compilers,
I lean towards towards the destructor being called as it is used in an assignment; but getting the committees to vote would be a nice way to resolve the question. It seems ideally there should be a formal way for compiler writers to petition for clarification.

+ : =====================================================
+ gfortran -O0 final1.f90
+ ./a.out
+ : =====================================================
+ ifort -O0 final1.f90
+ ./a.out
destructor1() 0
+ : =====================================================
+ ifx -O0 final1.f90
+ ./a.out
destructor1() 0
+ : =====================================================
+ nvfortran -O0 final1.f90
+ ./a.out

Andrew Benson

unread,
Sep 24, 2021, 4:30:56 PM9/24/21
to
Thank you for your response. As you suggested, I took a look at the Fortran 2018 standard document, which I think clears up the ambiguity on this issue. Specifically, in section 7.5.6.3 "When finalization occurs", item 1 states:

"When an intrinsic assignment statement is executed (10.2.1.3), if the variable is not an unallocated allocatable variable, it is finalized after evaluation of expr and before the definition of the variable. If the variable is an allocated allocatable variable, or has an allocated allocatable subobject, that would be deallocated by intrinsic assignment, the finalization occurs before the deallocation."

I think the key part here is "if the variable is not an unallocated allocatable variable". So, if the variable is unallocated it should not be finalized.

So, based on my interpretation of that Intel has incorrect behavior here.

-Andrew

Andrew Benson

unread,
Sep 24, 2021, 4:31:55 PM9/24/21
to
Thanks for testing this with nvfortran - it's good to have a third "opinion" here!

Andrew Benson

unread,
Sep 27, 2021, 4:32:41 PM9/27/21
to
Another finalization question (I'll also post this on the Fortran Discourse site and at the GitHub Fortran Issues site).

This example considers finalization of a class array:

module testmode
implicit none

character(4) :: scope = "MAIN"

logical, parameter :: instrument = .false.

type :: simple
character(4) :: scope
integer :: ind
contains
final :: destructor1, destructor2
end type simple

type, extends(simple) :: complicated
real :: rind
contains
final :: destructor3, destructor4
end type complicated

integer :: check_scalar
integer :: check_array(4)
real :: check_real
real :: check_rarray(4)
integer :: final_count = 0

contains

subroutine destructor1(self)
type(simple), intent(inout) :: self
print *, "destructor1(", self%scope, ") ", self%ind
end subroutine destructor1

subroutine destructor2(self)
type(simple), intent(inout) :: self(:)
print *, "destructor2(", self(1)%scope, ") ", self%ind
end subroutine destructor2

subroutine destructor3(self)
type(complicated), intent(inout) :: self
print *, "destructor3(", self%scope, ") ", self%rind
end subroutine destructor3

subroutine destructor4(self)
type(complicated), intent(inout) :: self(:)
if (size(self, 1) .gt. 0) then
print *, "destructor4(", self(1)%scope, ") ", size(self%rind), self%rind
else
print *, "destructor4"
end if
end subroutine destructor4

function constructor1(ind) result(res)
type(simple), allocatable :: res
integer, intent(in) :: ind
scope = "CTR1"
allocate (res, source = simple ("SOUR", ind))
res%scope = scope
end function constructor1

function constructor2(ind, rind) result(res)
class(simple), allocatable :: res(:)
integer, intent(in) :: ind(:)
real, intent(in), optional :: rind(:)
type(complicated), allocatable :: src(:)
integer :: sz
integer :: i
scope = "CTR2"
if (present (rind)) then
sz = min (size (ind, 1), size (rind, 1))
src = [(complicated ("SOUR", ind(i), rind(i)), i = 1, sz)]
allocate (res, source = src)
src%scope = "SRC "
res%scope=scope
else
sz = size (ind, 1)
allocate (res, source = [(simple (scope, ind(i)), i = 1, sz)])
end if
end function constructor2
end module testmode

program test_final
use testmode
implicit none

class(simple), allocatable :: MyClassArray(:)

! *****************
! Class assignments
! *****************

allocate (MyClassArray, source = [complicated(scope, 1, 2.0),complicated(scope, 3, 4.0)])
print *, "[3] ...until here. Both call the rank-1 finalizer for the extended &
type but ifort calls the rank-0 finalizer for the parent type, while &
gfortran uses the rank-1 finalizer."
deallocate (MyClassArray)
end program test_final


With gfortran (including the patches for finalization on intrinsic assignment that I'm working on), this results in:

[3] ...until here. Both call the rank-1 finalizer for the extended type but ifort calls the rank-0 finalizer for the parent type, while gfortran uses the rank-1 finalizer.
destructor4(MAIN) 2 2.00000000 4.00000000
destructor2(MAIN) 1 3

which shows that, when deallocating 'MyClassArray', the rank-1 finalizer for the extended type 'complicated' is called, and then the rank-1 finalizer for the parent type 'simple' is called.

But, under ifort I get:

[3] ...until here. Both call the rank-1 finalizer for the extended type but ifo
rt calls the rank-0 finalizer for the parent type, while gfortran uses the rank
-1 finalizer.
destructor4(MAIN) 2 2.000000 4.000000
destructor1(MAIN) 1
destructor1(MAIN) 3

showing that the rank-1 finalizer is called for the extended type, but then the scalar finalizer of the parent type is called twice, once for each element in the array.

ifort's behavior seems incorrect here, but I'd be interested to hear anyone's opinion on this.

Thanks,
Andrew

Paul Richard Thomas

unread,
Feb 3, 2022, 1:01:33 PM2/3/22
to
Hi Andrew,

> With gfortran (including the patches for finalization on intrinsic assignment that I'm working on), this results in:
>
> [3] ...until here. Both call the rank-1 finalizer for the extended type but ifort calls the rank-0 finalizer for the parent type, while gfortran uses the rank-1 finalizer.
> destructor4(MAIN) 2 2.00000000 4.00000000
> destructor2(MAIN) 1 3
>
> which shows that, when deallocating 'MyClassArray', the rank-1 finalizer for the extended type 'complicated' is called, and then the rank-1 finalizer for the parent type 'simple' is called.
>
> But, under ifort I get:
>
> [3] ...until here. Both call the rank-1 finalizer for the extended type but ifo
> rt calls the rank-0 finalizer for the parent type, while gfortran uses the rank
> -1 finalizer.
> destructor4(MAIN) 2 2.000000 4.000000
> destructor1(MAIN) 1
> destructor1(MAIN) 3
>
> showing that the rank-1 finalizer is called for the extended type, but then the scalar finalizer of the parent type is called twice, once for each element in the array.
>
> ifort's behavior seems incorrect here, but I'd be interested to hear anyone's opinion on this.


I have just posted a large patch on finalization. In the accomanying message, I conclude that ifort is correct. From F2018 7.5.6.3:
(2) All finalizable components that appear in the type definition are finalized in a processor-dependent
order. If the entity being finalized is an array, each finalizable component of each element of that
entity is finalized separately.
(3) If the entity is of extended type and the parent type is finalizable, the parent component is finalized.

The parent type is indeed a component and should apparently be treated as such. I have not yet ahad the intestinal fortitude to correct it in gfortran!

Regards

Paul

Andrew Benson

unread,
Feb 3, 2022, 1:05:32 PM2/3/22
to
Hi Paul,

Thanks - after reading those lines from the standard carefully (a few times over!) that does make sense.

Thanks,
Andrew

Paul Richard Thomas

unread,
Feb 6, 2022, 5:17:05 AM2/6/22
to
On Thursday, 3 February 2022 at 18:05:32 UTC, abe...@carnegiescience.edu wrote:
> Hi Paul,
>
> Thanks - after reading those lines from the standard carefully (a few times over!) that does make sense.
>
> Thanks,
> Andrew
> On Thursday, February 3, 2022 at 10:01:33 AM UTC-8, Paul Richard Thomas wrote:
> > Hi Andrew,
...snip...
> > I have just posted a large patch on finalization. In the accomanying message, I conclude that ifort is correct. From F2018 7.5.6.3:
> > (2) All finalizable components that appear in the type definition are finalized in a processor-dependent
> > order. If the entity being finalized is an array, each finalizable component of each element of that
> > entity is finalized separately.
> > (3) If the entity is of extended type and the parent type is finalizable, the parent component is finalized.
> >
> > The parent type is indeed a component and should apparently be treated as such. I have not yet ahad the intestinal fortitude to correct it in gfortran!
> >
> > Regards
> >
> > Paul

A dissenting voice: NAG Fortran compiler version 7.1 (thanks Damian) does the same as gfortran. Paragraph 3 seems to be somewhat prone to ambiguous interpretation.

It turned out that making gfortran comply with ifort is much easier than I thought it would be.

Paul

0 new messages