X3J3 / 97-103 Date: Jan 24, 1997 To: X3J3 From: R. Maine Subject: Specs and preliminary syntax for R.3, procedure pointers This paper summarizes the specifications and preliminary syntax for procedure pointers, item R.3 on the F2k requirements list of X3J3/97-010. Papers X3J3/96-142 and X3J3/96-146 were passed by X3J3 as the specifications for this requirement. The actual specifications are in paper X3J3/96-142. Paper X3J3/96-146 supplements these specifications with some illustrative example syntax, but the syntax was not part of the motion that passed. Paper X3J3/96-174 further developed the syntax, but was not yet a complete syntax proposal. No formal vote was taken on that paper. A straw vote on the concept of a named interface block (though not necessarily the exact syntax) was unanimously in favor. The proposal in X3J3/96-174 allowed procedure pointers with both explicit and implicit interfaces, but the subgroup was also directed to "reconsider whether to add a requirement that procedure pointers should require an explicit interface." Note that this direction did not say that such a requirement should be added, but just that the subgroup should reconsider the question. The current paper collects the material from papers X3J3/96-142, X3J3/96-146, and X3J3/96-174 into a single document. This includes both specifications and preliminary parts of the syntax. Most of the material about pointers with implicit interfaces has been segregated to make it easy to identify which portions would be affected by that issue. The subgroup has not yet had time to collectively consider its recommendation on this question; presumably such reconsideration will be done at meeting 140. 1. SPECIFICATIONS 1.1 Procedure pointers vs. procedure variables Proposals for procedure pointers and procedure variables offer similar functionality. After much discussion, /data is recommending the procedure pointer formulation because it more naturally offers the null pointer or null association concept. 1.2 Simillar existing features Specifications for this facility are based on analogies with two existing language features. 1.2.1 Dummy procedures. If a set of declarations of a name in conjunction with that name appearing in a dummy argument world make that name the name of a dummy procedure, then that same set of declarations in conjunction with that name being declared with the POINTER attribute will make that name the name of a procedure pointer. The class of procedures which can then be associated with that procedure pointer is exactly the class of procedures which could have been supplied as the corresponding actual argument in the dummy procedure case. Thus, procedure pointers cannot be associated with internal procedures, statement functions, generic procedures, most intrinsic procedures, etc. If the interface of the procedure pointer is explicit, the interface of the procedure being associated must also be explicit (with matching characteristics). This analogy with dummy procedures is what drove the initial proposal that procedure pointers with implicit interfaces be allowed. Disallowing implicit interface procedure pointers would constitute an exception to this analogy. It might be interpreted as suggesting that implicit interface dummy procedures are a deprecated concept. 1.2.2 Data-object pointers. The mechanism for establishing an association is the pointer assignment statement, a disassociated status can be created with the NULLIFY statement or the NULL intrinsic function, the one-argument form of the ASSOCIATED intrinsic function can be used to test for a null association status, and the two-argument form of the ASSOCIATED intrinsic function can be used to test for a particular association. However, we do not proposed to allow one to ALLOCATE or DEALLOCATE a procedure pointer. As with data object pointers, the default initial status for such pointers is "undefined". Just as a data-object pointer can become undefined if it is associated with a module variable and the module goes out of scope, a procedure pointer can become undefined if it is associated with a module procedure and the module goes out of scope. Procedure-pointer dummy arguments, function results, and derived type components would be allowed analogous with the data-object pointers. There are no arrays of procedure pointers, but the usual workaround of having arrays of a derived type with pointer components works as well for procedure pointers as for data- object-pointers. 2. SYNTAX (EXPLICIT INTERFACE) 2.1 Existing declaration syntax One syntax for declaring variables as pointers to explicit interface procedures already exists if we just assign a meaning to the previously meaningless combination of an interface body and a POINTER statement for the same name. An example is INTERFACE SUBROUTINE E(X,Y) REAL :: X,Y END SUBROUTINE END INTERFACE POINTER :: E This declares E to be a pointer to an explicit interface procedure. 2.2 Named explicit interfaces It is less obvious how to declare some forms of procedure pointer components. This is because the existing syntax requires at least 2 separate constructs (the interface block and the pointer statement) to declare a procedure pointer with an explicit interface. All other derived type components are declared with a single statement rather than with multiple attribute statements. Even if the interface body syntax is expanded to allow the pointer specification, this approach would result in interface bodied nested inside of derived type definitions, which is arguably objectionable. An additional problem with the existing syntax is that it does not provide a reasonable way to declare multiple pointers with the same interface. For the non-pointer case, this was not too much of an issue because there were not often large numbers of essentially identical interface bodies in a single scope. There might be at most one for each procedure dummy argument and one for each specific external procedure referenced; those for external procedures could potentially be automaticaly generated. In contrast, it is plausible for there could be an arbitrarily large number of procedure pointers with the same interface in a scoping unit and there is no obvious way to automatically generate the interface bodies. We propose a mechanism to assign a symbolic name to an interface so that procedure pointers can be declared to have this interface by name rather than requiring the matching interfaces to be repeatedly declared in detail. We will refer to this symbolic name as a procedure interface name, at least for now. Note that the procedure interface name is purely syntactic. We specifically avoid attaching semantic significance to the name. In particular, it does not act like a type. You can declare two different procedure pointers with different procedure interface names and they are still compatable as long as the charactersitics of the interfaces are the same. It would be difficult to disallow this without changing the feature of f90/f95 that allows a procedure to have different interface bodies in different scopes, as long as their characteristics agree. (In particular, the dummy argument names need not agree). Interface names can be imported by USE or host association. 2.2.1 Interface block syntax alternative 1 This alternative is as presented in paper X3J3/96-174. To define a named procedure interface while introducing as little new syntax as practical, we propose a third form of interface block. The existing interface blocks either have a generic-spec in the interface statement or not. The proposed third form of interface block would have an interface statement with the syntax INTERFACE NAME() Such a interface block must contain exactly one interface body. The syntax of the interface body is unchanged from the existing interface body syntax, but there is one semantic difference. This kind of interface body defines the characteristics of an interface, but it does not in itself attach those characteristics to any object. The procedure name in such an interface body has scope of only the procedure body; it is not the name of an external or dummy procedure. The procedure name is basically a placeholder that has no significance outside of the interface body. The procedure name could possibly be omitted completely (in which case, a result clause would be needed if it were a function). We don't currently propose allowing the procedure name to be omitted because it seems simpler to leave the syntax of interface bodies completely unchanged, but it would be a concievable syntax variation. An example of a named interface definition for a subroutine is INTERFACE NAME(sub) SUBROUTINE this_name_is_irrelevant(x,y) REAL :: x,y END SUBROUTINE END INTERFACE and one for a function is INTERFACE NAME(fun) FUNCTION so_is_this(x) result(f) REAL :: x,f END FUNCTION END INTERFACE 2.2.2 Interface block syntax alternative 2 Although no vote was taken, several reservations were expressed about the placeholder names in the above syntax. It is also anomalous that the above form of interface block syntax allows 1only one interface body in the block. Therefore, the following syntax is presented as an alternative possibility. The interface statement remains as in the f90/f95 standard. One addition is made to the interface body syntax. We define a new (R1219) tentatively called MODEL. This prefix may appear only on a subroutine and function statement in an interface body, never in the subroutine or function statement of a subprogram. I'm receptive to better terms than "model". The notion is that this defines an abstract interface template rather than the interface of a specific procedure. I've heard objection to the term "template" as having too many other conotations. Terms like "type", "class", and "kind" seem bad for simillar reasons. There is unlikely to be any confusion between model numbers and procedure interface models. I suppose "abstract" might work. When this is present, the interface body defines a named interface rather than the interface of a specific procedure. The interface name is the or in the interface body. An example of an interface block using this syntax alternative is INTERFACE MODEL SUBROUTINE sub(x,y) REAL :: x,y END SUBROUTINE MODEL FUNCTION fun(x) result(f) REAL :: x,f END FUNCTION END INTERFACE This example is equivalent to the examples shown for syntax alternative 1. 2.3 Procedure declaration statement A procedure declaration statement has a syntax modeled after a type declaration statement for a derived type. The keyword PROCEDURE is used instead of TYPE and an interface name is used instead of a derived type name. For example, the statement PROCEDURE(fun), POINTER :: a => NULL(), b, c declares three procedure pointers, all having the interface named fun. Possibly relevant attributes are POINTER, SAVE, OPTIONAL, and INTENT. Note that EXTERNAL is not allowed (because it would be a form of redundancy that we already disallow elsewhere). Although it was the requirements of procedure pointers that most motivated the need for named interfaces and the corresponding procedure declaration statements, there are some non-pointer cases where a procedure declaration statement could naturally be used. We propose to allow these cases. Specifically, a procedure declaration statement without the pointer attribute can be used to declare a dummy or external procedure. 2.4 Procedure pointer component definition Component definition statements would be enhanced to add the form PROCEDURE(), POINTER :: The POINTER attribute is required and no other attributes are allowed. Example: TYPE my_object_type TYPE(my_type) :: x PROCEDURE(my_interface) :: my_procedure END TYPE 2.5 NULLIFY and NULL() Procedure pointers may appear in the NULLIFY statement. The NULL() intrinsic can return a null procedure pointer; the optional argument of NULL() may be a procedure pointer. 2.6. ASSOCIATED() The one-argument form of associated can be used with procedure pointers. The two-argument form of ASSOCIATED is also allowed, but note that two pointers are nominally different if they are associated with the same module procedure but from different instances of the host module. However, it appears that such queries must always involve at least one pointer that is really undefined, so implementations are not required to track this. 2.7 Pointer assignment Pointer assignment for procedure pointers has the same syntax as other pointer assignments. Note that the target for procedure pointer assignment may be a procedure (how else are they going to get associated with anything non-null), but that we do not propose to require that procedures be explicitly declared to have the target attribute. For example INTERFACE FUNCTION RFP(x), RESULT(y) REAL :: x,y END FUNCTION RFP END INTERFACE POINTER RFP INTRINSIC SIN RFP => SIN associates RFP with the (specific) intrinsic function SIN. 2.8 Procedure pointer references Just as with data pointers, procedure pointers are automatically dereferenced when used in an appropriate context. In particular, if RFP is a pointer to a real function and SP is a pointer to a subroutine, then the forms x = rfp(y) CALL sp(z) invoke the function that is associated with rfp and the subroutine that is associated with sp. Execution of these statements is illegal if rfp or sp is not associated. We recommend that procedure reference syntax be extended to allow the procedure to be specified as a structure component, e.g. CALL PARENT%PROC_POINTER(ARGS) On the other hand, we do not recommend a similar extension for functions that return procedure pointers. E.g., we would require PP=>PPF(ARGS1) CALL PP(ARGS2) rather than extending to allow CALL PPF(ARGS1)(ARGS2) This is consistent with data-object pointers, which can be subscripted, substringed, etc. as components, but not as function results. 3. SYNTAX (IMPLICIT INTERFACE) 3.1 Existing declaration syntax A syntax for declaring variables as pointers to implicit interface procedures already exists if we just drop the prohibition against a name having both the POINTER and EXTERNAL attribute. Sample declarations are: REAL, EXTERNAL, POINTER :: FP => NULL() EXTERNAL :: SP POINTER :: SP Implicit interface function pointers can be declared either with a single statement, as illustrated above, or with separate attribute statements. For explicit interface subroutine pointers, there is no way to specify both the external and pointer attributes in one statement with the existing syntax. As with the non-pointer case, the distinction between implicit interface subroutines and functions can type of an implicit interface function pointer may be determined by implicit typing rules. 3.2 Implicit inference of attributes As in the non-pointer case, the distinction between implicit interface function and subroutine pointers can be inferred by the use of the name in a call statement or function reference. A pointer to an implicit interface external function can be implicitly typed following the same rules as the non-pointer case. For example, in the following, sp is a subroutine pointer and fp is a real function pointer. external sp, fp pointer sp, fp ... call sp y = fp(x) In the non-pointer case, the external attribute can also be implicitly inferred by the use of the name in a subroutine call or a function reference. However, a procedure name passed as an actual argument must be explicitly declared to have the external attribute (even if it also appears in a context that implies the external attribute - I didn't know that, but see 12.3.2.2). For procedure pointers, we certainly want the external attribute to be at least as explicit as for the non-pointer case. I would propose that a procedure pointer used on either side of a pointer assigment statement must also be explicitly given the external attribute. I don't think we want the compiler to have to infer that B is a procedure pointer in cases like the following POINTER :: A, B ... B => A ... A => S ... CALL S Because a procedure pointer cannot be referenced unless it is first associated, and since most ways of associating it would require that the externalness be explicit, there aren't very many cases left in which implicit externalness is useful. Note that this is very different from the non-pointer case, where the large majority of the usage is in cases where the externalness is implicitly obvious. Therefore, we recommend that the external attribute must be explicitly declared for procedure pointers. Of course, the pointer attribute must always be explicitly declared. 3.3 Named implicit interfaces The above sections define syntax adequate for declaring all cases of procedure pointers except for one. The missing case is a derived type component that is a pointer to an implicit interface subroutine. For pointers to explicit interface procedures, we have syntax like PROCEDURE(sub), POINTER :: a For pointers to implicit interface functions, we have syntax like REAL, EXTERNAL, POINTER :: a These forms work for both structure component definitions and for variable definitions. But the only syntax we have for pointers to implicit interface subroutines uses two separate attribute statements as in POINTER :: s EXTERNAL :: s This doesn't "work" well for component definitions because it is two separate statements. We could possibly allow a syntax like EXTERNAL, POINTER :: s but this would introduce several confusing inconsistencies. (Would you also allow a POINTER specification in the EXTERNAL attribute statement, or in other attribute statements? Would you allow a function component to be specified in a syntax like "EXTERNAL, REAL, POINTER :: f"?) The basic problem is that subroutines don't have a type, so they can't be declared in a type declaration statement. We propose to address this problem by extending the syntax of named interface definitions to include implicit interfaces as well as explicit ones. An interface block to define a name for an implicit interface subroutine would look like INTERFACE NAME(sub), EXTERNAL END INTERFACE Such an interface block can have no interface bodies, which makes the END INTERFACE look a bit pointless, but it is probably best to retain it for consistency. For completeness, we might as well also provide for named interface definitions for implicit interface functions. A sample syntax for such an interface block would be INTERFACE NAME(func), EXTERNAL, REAL END INTERFACE These forms correspond to interface block syntax alternative 1. If interface block syntax alternative 2 is selected, and named implicit interfaces are also desired, corresponding syntaxes would be needed, but are not yet proposed here. None of the syntaxes for named implicit interfaces look particularly "clean", although they are certainly workable. If implicit interface pointers are disallowed, the issue becomes moot. Even if we allow implicit interface pointers, it would be concievable to do so without defining named implicit interfaces. The only real substantial consequence of this would be that there would be no syntax for declaring implicit interface pointer subroutine components.