J3/00-277r1 To: J3 From: Malcolm Cohen Date: 15th September 2000 Subject: Edits for Destructors 1. Introduction This paper contains edits to implement the syntax and specifications of destructors as described in 00-271. 2. Organisation and Terminology The terminology we'll use should be clear from 00-271: "finalization" - whatever it is that we do immediately before destroying a derived-type object (either calling a user procedure or not). "final subroutines" - the user-defined procedures that get called before object destruction. "finalizable" - a type (or object) needs finalization. Most of this will go into chapter 4 (for derived types), since they only apply to derived types. 3. Specs and Syntax Changes and Clarifications The following additional requirements on user-defined final subroutines are proposed: (1) All nonkind type parameters of the dummy argument shall be assumed. - Rationale: It seems desirable to require the finalizer not to have a runtime check before being called or to have potential runtime non-conformance. (2) The dummy argument shall not be INTENT(OUT). - RAT: This would automatically cause an infinite loop, which is undesirable. (3) An extended derived type is also finalisable if it extends a finalisable type (c.f. first sentence of 3.1 in 00-271). (4) Multiple FINAL statements in a derived type definition are treated as if they were concatenated. (5) The dummy argument of a final subroutine shall not have the OPTIONAL attribute. - RAT: Since a final subroutine is always called (by the processor) with an argument, it would seem confusing to have it being optional; that is most likely to be some sort of user error. (6) If a component of a finalizable object is allocatable, the object shall be finalized before any automatic deallocation of the component. 4. Edits to 00-007r2 [41:44+] Insert new constraint "Constraint: If SEQUENCE is present, a shall not be present." {Prevent all type-bound procedures, including final subroutines, from appearing in sequence types.} [43:22+] Insert new bnf "<> FINAL [ :: ] final-subroutine-name-list" [43:28+] Insert constraints "Constraint: shall be a module procedure with exactly one dummy argument. That argument shall be a nonpointer nonallocatable nonpolymorphic nonoptional variable of the derived type being defined. All nonkind type parameters of the dummy argument shall be assumed. The dummy argument shall not be INTENT(OUT). Constraint: A shall not be one that previously had been specified as a final subroutine in that type. Constraint: A final subroutine shall not have a dummy argument of the same kind type parameters and rank as the dummy argument of another final subroutine of that derived type." {Add syntax for user to declare final subroutines. The final constraint requires the final subroutines to be generically disambiguatable a la 14.1.2.3 without actually putting them into a generic set.} [50:44+] Insert new section "4.5.1.8a Final subroutines {NOTE TO EDITOR: Please insert "final subroutines" into the index.} The FINAL keyword specifies a list of <>. These subroutines may be executed when an object of that type is finalized (4.5.7a). A derived type is <> if it has any final subroutines, or if it has any nonpointer nonallocatable component whose type is finalizable. An object is finalizable if its type is finalizable. Note 4.38a: Final subroutines are effectively always "accessible". They are called for object finalization regardless of the accessibility of the type, its other type-bound procedure bindings, or the subroutine name itself. Note 4.38b: Final subroutines are not inherited through type extension and cannot be overridden. The final subroutines of the parent type are called after calling any additional final subroutines of an extended type. " {Basic definitions.} [52:40] Before "procedure bindings" insert "non-final". {Final procedure bindings are not inherited.} [57:12+] Insert new section "4.5.7a The finalization process {NOTE TO EDITOR: Please insert "finalization" into the index.} When a finalizable object is <>, the following steps are carried out in sequence: (1) If the dynamic type of the object has a final subroutine whose dummy argument has the same kind type parameters and rank as the object being finalized, it is called with the object as an actual argument. If there is no such subroutine, but there is an elemental final subroutine with the same kind type parameters as the object being finalized, it is called with the object as an actual argument. If no final subroutine fulfills these requirements, no subroutine is called at this point. (2) Each finalizable component that appears in the type definition is finalized. {NOTE TO J3, NOT THE STANDARD: This excludes inherited components. Alternative wording would be "Each finalizable non-inherited component is..."} If the object is an array, the components of each element are finalized separately. {NOTE TO J3, NOT THE STANDARD: This will finalize zero components if the array is zero-sized.} (3) If the object is of extended type and the parent type is finalizable, the parent component is finalized. If several objects are to be finalized at the same time, the order in which they are finalized is processor-dependent. A final subroutine shall not reference or define an object that has already been finalized. 4.5.7b When finalization occurs A pointer or allocatable object is finalized when it is deallocated. A nonpointer nonallocatable object that is not a dummy argument or function result is finalized immediately before it would become undefined due to execution of a RETURN or END statement (14.7.6, item (3)). If the object is defined in a module and there are no longer any active procedures referencing the module, it is processor-dependent whether it is finalized. If the object is not finalized, it retains its definition status and does not become undefined. If an executable construct references a function whose result is finalizable, the result is finalized after execution of the innermost executable construct containing the reference. {NOTE TO J3, NOT THE STANDARD: An ordinary executable statement - such as an assignment statement - is in itself an executable construct.) If a specification expression in a scoping unit references a function whose result is finalizable, the result is finalized before execution of the first executable statement in the scoping unit. When a procedure is invoked, a nonpointer nonallocatable object that is an actual argument associated with an INTENT(OUT) dummy argument is finalized. When an intrinsic assignment statement is executed, the is finalized before the assignment takes place. If an object is allocated via pointer allocation and later becomes unreachable due to all pointers to that object having their pointer association status changed, it is processor dependent whether it is finalized. If it is finalized, it is processor dependent as to when the final subroutines are called. 4.5.7c Objects that are not finalized If program execution is terminated, either by an error (e.g. an allocation failure) or by execution of a STOP or END PROGRAM statement, no objects existing immediately prior to termination are finalized. A nonpointer nonallocatable object that has the SAVE attribute or which occurs in the main program is never finalized. A variable in a module is not finalized if it retains its definition status and value, even when there is no active procedure referencing the module." [107:35+] Insert new paragraph "If an allocatable component is a subobject of a finalizable object, that object shall be finalized before the component is automatically deallocated." {Specify that automatic deallocation is part of object destruction and thus occurs after calling final subroutines.} [358:23+] Insert "(g) Finalizable variables that are not finalized and are accessed from a module that is not referenced either directly or indirectly by any other scoping unit that is making either a direct or indirect reference to the subprogram." {They don't become undefined if they don't get their finalizers called...} [402:1+] Insert new entries "<> (4.5.1.8a) Subroutines that are called automatically by the processor during . <> (4.5.1.8a) A type that has final subroutines, or that has a finalizable component. An object of finalizable type. <> (4.5.7a) The process of calling user-defined immediately before destroying an object." {Hey, can we really use words like "destroying an object" here? After all, it's not normative text, and "destroying" is much more informative than the standard-speak equivalents...} [419:11+] Insert new section "C.1.4a Final subroutines (4.5.1.8a, 4.5.7a, 4.5.7b, 4.5.7c) Example of a parameterized derived type with final subroutines: MODULE m TYPE t(k) REAL(k),POINTER :: vector(:) => NULL() CONTAINS FINAL :: finalize_t1s, finalize_t1v, finalize_t2e END TYPE CONTAINS SUBROUTINE finalize_t1s(x) TYPE(t(KIND(0.0))) x IF (ASSOCIATED(x%vector)) DEALLOCATE(x%vector) END SUBROUTINE SUBROUTINE finalize_t1v(x) TYPE(t(KIND(0.0))) x(:) DO i=LBOUND(x,1),UBOUND(x,1) IF (ASSOCIATED(x(i)%vector)) DEALLOCATE(x(i)%vector) END DO END SUBROUTINE ELEMENTAL SUBROUTINE finalize_t2e(x) TYPE(t(KIND(0.0d0))),INTENT(INOUT) :: x IF (ASSOCIATED(x%vector)) DEALLOCATE(x%vector) END SUBROUTINE END MODULE SUBROUTINE example(n) USE m TYPE(t(KIND(0.0))) a,b(10),c(n,2) TYPE(t(KIND(0.0d0))) d(n,n) ... ! Returning from this subroutine will effectively do ! CALL finalize_t1s(a) ! CALL finalize_t1v(b) ! CALL finalize_t2e(d) ! No final subroutine will be called for variable C because the user ! omitted to define a suitable specific procedure for it. END SUBROUTINE Example of extended types with final subroutines: MODULE m TYPE,EXTENSIBLE :: t1 REAL a,b END TYPE TYPE,EXTENDS(t1) :: t2 REAL,POINTER :: c(:),d(:) CONTAINS FINAL :: t2f END TYPE TYPE,EXTENDS(t2) :: t3 REAL,POINTER :: e CONTAINS FINAL :: t3f END TYPE ... CONTAINS SUBROUTINE t2f(x) ! Finalizer for TYPE(t2)'s extra components TYPE(t2) :: x IF (ASSOCIATED(x%c)) DEALLOCATE(x%c) IF (ASSOCIATED(x%d)) DEALLOCATE(x%d) END SUBROUTINE SUBROUTINE t3f(y) ! Finalizer for TYPE(t3)'s extra components TYPE(t3) :: y IF (ASSOCIATED(y%e)) DEALLOCATE(y%e) END SUBROTUINE END MODULE SUBROUTINE example USE m TYPE(t1) x1 TYPE(t2) x2 TYPE(t3) x3 ... ! Returning from this subroutine will effectively do ! ! Nothing to x1, it is not finalizable ! CALL t2f(x2) ! CALL t3f(x3) ! CALL t2f(x3%t2) END SUBROUTINE " ===END