X3J3/96-174 Date: Nov 13, 1996 To: X3J3 From: Rich Maine Subject: Syntax for R.3, Procedure pointers The specifications for procedure pointers (item R.3) were passed as papers X3J3/96-142 and X3J3/96-146. Those papers also included sample syntax, but the samples were preliminary and incomplete. As mentioned in the above-cited papers, some of the syntax is reasonably obvious from the specifications. 1. Declaration of procedure pointers A procedure pointer can be a local variable, a dummy argument, or a structure component. A function can return a procedure pointer. A procedure pointer cannot be an array. The characteristics of a procedure pointer are the explicitness of its interface, its classification as a function or subroutine, its characteristics as a procedure if the interface is explicit, and whether it is an optional dummy argument. 1.1 Existing declaration syntax A syntax for declaring variables as pointers to procedures already exists if we just remove the current restrictions against an entity having both the EXTERNAL and POINTER attributes. Thus a pointer to an implicit interface external function could be declared in either the entity-oriented form of REAL, EXTERNAL, POINTER :: F => NULL() or with separate attribute statements as in REAL :: F EXTERNAL :: F POINTER :: F DATA F /NULL()/ A pointer to an external implicit interface subroutine could be declared as EXTERNAL :: S POINTER :: S A pointer to a procedure with an explicit interface could be declared as INTERFACE SUBROUTINE E(X,Y) REAL :: X,Y END SUBROUTINE END INTERFACE POINTER :: E None of this syntax is new; the only change is to delete the restrictions against these combinations. A pointer to an implicit interface external function can be implicitly typed following the same rules as the non-pointer case. 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. 1.2. Named explicit interfaces As mentioned in paper 96-142, 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. Paper 96-142 suggests the possibility that there might be 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. This is the approach that we are now recommending. We will refer to this symbolic name as a procedure interface name, at least for now. 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 alternative. 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 1.3 Simpler syntax vs minimal change The syntax suggested above for interface blocks could undoubtedly be simplified if considered in isolation. In particular, since the procedure name in the interface body has no real use in this context, it could be omitted. And since there must be exactly one interface body in this form of interface block, the nesting is superfluous - the interface name could be declared with additional syntax in the function or subroutine statement instead of in a separate interface statement. The suggested syntax has the benefit of involving absolutely no syntax changes for interface bodies. The alternatives all would involve additional interface body syntax. Also, there might possibly be parsing complications if an interface body could appear outside of an interface block....Or I presume so; otherwise why does the form of interface block without a generic spec even exist? Perhaps for the same reason that the extra syntax of the interface...end interface containment simplified parsing? 1.4 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. 1.5 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. Components that are pointers to implicit interface functions can be declared with the existing component-decl syntax by adding EXTERNAL to the list of attributes allowed in a component-decl. If EXTERNAL appears, then POINTER must also appear, and there can be no dimension attribute. An example of such a component declaration is REAL, POINTER, EXTERNAL :: F => NULL() 1.6 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 2. Use of procedure pointers The remainder of the syntax will basically follow the samples given in papers 96-142 and 96-146. It will probably be added to the next version of this paper, but isn't written out in detail yet. Topics to be covered include: Nullify, null intrinsic, pointer assignment, associated intrinsic, no target attribute, no allocate/deallocate, sub and func reference, allow component form of ref but not for procs returning proc ptrs, Rules for what associations are allowed. Show examples of dummy proc ptr args and proc returning a proc ptr result. Also, interface names can be imported from modules (or by host assoc).