It appears that compilers have different treatment of the final subroutine in OO.
This is an example:
module object_module
type :: Object
contains
final :: destructor
endtype
type, extends(Object) :: ObjectDerived
contains
endtype
type, extends(Object) :: ObjectDerivedWithDummyFinal
contains
final :: destructor_ObjectDerivedWithDummyFinal
endtype
contains
subroutine destructor(this)
type(Object) :: this
write(0,*) 'destructor called'
end subroutine
subroutine destructor_ObjectDerivedWithDummyFinal(this)
type(ObjectDerivedWithDummyFinal) :: this
! dummy, just so destructor will be called
end subroutine
end module
The request is:
When instance of ObjectDerived leaves scope, should the destructor subroutine from Object get called? Some compilers do, others require a dummy āfinalā routine which is empty such as done in ObjectDerivedWithDummyFinal.
From my point of view, the parent finalize should always be calledā¦ Looking at the standard, I donāt see a clear explanation:
Fortran 2023: 7.5.6 and C3.6: each type needs a separated final subroutine, which is not inherited through type extension.
7.5.6.1 Note 2.: the Final Subroutine (if exists) of its parent type (Object) is also called after the Final Subroutine of the extended type (ObjectDerivedWithDummyFinal here) is called.
What exactly do you expect to happen, but isnāt? Can you provide a whole program that demonstrates the behavior?
I will note that compilers have traditionally been really bad at this. Less than a year ago we wrote up a test suite for all the cases where final subroutines are supposed to be run and I think maybe one compiler got them all right. I believe gfortran has subsequently been fixed.
In the case the parent finalize is not called, you get:
> ./main.x
entering
ctor1
ctor2
destructor2 called 2
destructor1 called 2
exit
while it would be more logical (IMO) to have the following output:
entering
ctor1
ctor2
destructor1 called 1
destructor2 called 2
destructor1 called 2
exit
Gfortran is not really used (Iām not sure even 13 is working with finalize, very bad experience with it). Iāve used Cray, Intel, nvidia, NAG compilers and they have different results.
entering
ctor1
destructor1 called 0
destructor1 called 1
ctor2
destructor2 called 0
destructor1 called 0
destructor2 called 2
destructor1 called 2
exit
Which is correct (I believe).
On line obj1 = ObjectDerived(), the constructor is called, the lhs (obj1) is finalized, the assignment is performed and then the rhs is finalized. Then on line obj2 = ObjectDerivedWithDummyFinal(), the constructor is called, the lhs (obj2) is finalized, which involves calling destructor2 then destructor1, and then the rhs is finalized.
With gfortran (13.2) I get:
entering
destructor1 called 32645
ctor1
destructor1 called 0
destructor2 called 335544320
destructor1 called 335544320
ctor2
destructor2 called 0
destructor1 called 0
exit
Which in this case would be valid to call the finalizer for the lhs before evaluating the rhs, but is not valid in the general case (i.e. if the lhs appears on the rhs). Iām not sure why the assigned value from the constructor doesnāt get propagated through to the finalization of the rhs though.
Is there a reason you would expect something different?
OK, letās consider the NAG output (as I said, Iām not interested in gfortran, which I think it is buggy or at least āsuspiciousā).
My initial question was: should we call the destructor1 for the ObjectDerived()? NAG does. Still, the question remains: is this conformant to the standard? Indeed, the Cray compiler doesnāt call destructor1 for the ObjectDerived() and you have to go for the second case (ObjectDerivedWithDummyFinal()) to make sure you call destructor1.
Then Iāve tried other compilers: NVHPC and Intel do like NAG.
Do we go for the majority and assume this is what the standard requires?
Clearly the position of all practitioners should be no. It is what the standard states what matters, the āmajorityā is of no relevance.
Have you referred to the finalization section in Fortran 2023 standard document (or the proxy for it, 23-007r1 or perhaps 24-007r1 - not sure itās been posted yet)? Section 7.5.6 if I recall correctly.
If yes, what in that section is unclear to you and what, if any, do you notice with NAG Fortran that is inconsistent with the standard?
@FortranFan
Thanks for your reply. Please check my first message. I do mention the Fortran standard. Your point about the standard docet is exactly what I would like to know. Iām not interested in what the compilers are doing (and indeed Iām not mentioning them). It is not me who introduced NAG compiler (or any other compiler) in the discussionā¦
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.
That covers the LHS in your assignment statements
If an executable construct references a nonpointer function, the result is finalized after execution of the innermost executable construct containing the reference.
And that covers the RHS.
A derived type is finalizable if and only if it has a final subroutine or a nonpointer, nonallocatable component of finalizable type.
An extended type has a scalar, nonpointer, nonallocatable, parent component with the type and type parameters of the parent type.
When an entity is finalized, the following steps are carried out in sequence.
If the dynamic type of the entity has a final subroutine whose dummy argument has the same kind type parameters and rank as the entity being finalized, it is called with the entity as an actual argument. Otherwise, if there is an elemental final subroutine whose dummy argument has the same kind type parameters as the entity being finalized, or a final subroutine whose dummy argument is assumed-rank with the same kind type parameters as the entity being finalized, it is called with the entity as an actual argument. Otherwise, no subroutine is called at this point.
All nonallocatable 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.
If the entity is of extended type and the parent type is finalizable, the parent component is finalized.
Neither of your types have finalizable components that appear in the type definition, so 2 does not apply. In the first assignment, your entities do not have final subroutines themselves, so 1 does not occur, but 3 does. In the second assignment they do have their own final subroutines, so 1 and 3 occur, in that order, individually for each entity that is finalized. Like I said, NAG is getting this right.
@everythingfunctional
First of all, thanks for your analysis. This is inlined with my guess (see my first message: āFrom my point of view, the parent finalize should always be calledā).
To clarify my last message about NAG, you extracted a sentence from a larger context. I was referring to my first message where I didnāt mention any compiler (and then you started to talk about NAG, so all credits to you for introducing NAG, which is not really of my interest).