To: J3 J3/25-168 From: John Reid & Hidetoshi Iwashita Subject:Auto-generic subprograms: Syntax Date: 2025-September-23 References: 24-139r2, 24-181, 24-184r1, 25-103, 25-115, 25-128, 25-129, 25-156r1, 25-163, 25-164. 1. Introduction =============== Syntax for auto-generic subprograms was approved by J3 in 24-139r2 and modified in 24-181, 24-184r1, 25-103, and 25-115. The remaining alternatives were resolved by straw votes in 25-156r1, all of which were decisive. 25-128 added the function MAX_RANK to the intrinsic module ISO_FORTRAN_ENV as part of this feature and needs to be incorporated in the syntax. This paper provides the modified syntax in one place. We have included these further changes: 1. To conform with the changes to the requirements and specifications made in 25-163 and 25-164, the constraint at the end of x03 ", but may be a generic-dependent entity" has been removed because the term "generic-dependent entity" is not needed following those changes. 2. 25-128 includes the statement "A reference to the function MAX_RANK may appear in a constant expression provided CORANK does not appear or is itself a constant expression." but a similar statement for restricted expression was not made. We think that this is a simple oversight and have made that addition in x07. 3. 25-128 includes the statement "If the CORANK argument is absent, the result of MAX_RANK is the maximum rank of array that is supported by the processor." We think that a user writing a program without any coarrays should be able to write MAX_RANK() for the maximum rank of an array and not be required to write MAX_RANK(0) to be sure of getting this result. We have made this change in x07. 4. 25-128 includes the specification s20: "A rank, type or kind generic dummy argument may be a coarray." Single edits have been added in x03 and x06 to support this. Change bars against 24-139r2 are shown. The first of a sequence of change bars shows the origin of the changes. 2. Syntax ========= x01. The GENERIC in the or of a module subprogram or internal subprogram specifies the subprogram to be generic. Its name is a generic name and it defines one or more specific procedures with that generic name. Each dummy argument of a specific procedure has a single type, kind, and rank. The interface of each specific procedure is explicit. If the name is already generic, the new specific procedures will be added to the existing set of specific procedures. Any two of these procedures must satisfy the rules of 15.4.3.4.5 to ensure that any reference is unambiguous. Constraint: If a module subprogram is generic, it shall not have an internal subprogram that is generic. Comment This is a simplification to avoid nested generic subprograms, which would generate N**2 specifics (most of which might not even be referenced); that would be an unnecessary burden on the processor. Instead of nested generic subprograms, side-by-side generic subprograms (in the containing scoping unit) can be used, perhaps making some of them PRIVATE if they are module subprograms. Constraint: A generic subprogram shall not have an alternate return. Comment This is to avoid extending an obsolescent feature. There is no other technical reason. Example 1 MODULE mod CONTAINS GENERIC SUBROUTINE my_lift(x) TYPE(INTEGER, REAL) :: x ... END SUBROUTINE END MODULE mod Example 2 PROGRAM main ... CONTAINS GENERIC SUBROUTINE my_lift(x) TYPE(INTEGER, REAL) :: x ... END SUBROUTINE END PROGRAM main Note: Only internal subprograms and module subprograms can have the GENERIC prefix, not external subprograms or interface bodies other than separate module procedure interface bodies. x01a. The GENERIC in the or of a separate module procedure interface declares that the separate module procedure name is generic, and defined by a module subprogram that has both the GENERIC and MODULE prefixes. Comment The mp-subprogram-stmt ("MODULE PROCEDURE name") is not available in this case, as (a) that would have no indication that the separate module subprogram is generic, and (b) if there were more than one generic subprogram interface with the same name, it would be ambiguous which one it was supposed to be implementing. x02. A in the PROCEDURE statement of a generic interface block or in the GENERIC statement is extended to specify generic names, as follows. <> <> A generic name appearing in the is treated as if all specific procedures identified by the generic name were added to the list. Constraint: If a appears in the of a PROCEDURE statement in a generic interface block, the of the shall not be a generic name. If a appears in the of a GENERIC statement, the shall not be a generic name. Comment This constraint prohibits a generic name identifying generic names. It may cause complicated situations due to mutual inclusions, recurrent references, and evaluation order issues of generic names. Invalid Example MODULE bad INTERFACE invalid PROCEDURE xyz PROCEDURE gsub ! Violates the above constraint. END INTERFACE CONTAINS SUBROUTINE xyz() END SUBROUTINE GENERIC SUBROUTINE gsub(a) TYPE(integer,real) :: a a = 999 END SUBROUTINE END MODULE Example MODULE example INTERFACE OPERATOR(.myop.) PROCEDURE fun ! All specific procedures with generic name fun FUNCTION fen(a,b) ! External function fen REAL, INTENT(IN) :: a, b REAL :: fen END FUNCTION fen END INTERFACE CONTAINS GENERIC FUNCTION fun(a) REAL, INTENT(IN), RANK(0) :: a REAL, RANK(0) :: fun ... END FUNCTION fun GENERIC FUNCTION fun(a) RESULT(b) | [25-128] USE ISO_FORTRAN_ENV | REAL, INTENT(IN), RANK(1:MAX_RANK()) :: a REAL, RANK(RANK(a)) :: b ... END FUNCTION fun END MODULE x03. A generic dummy argument is a type-or-kind-generic dummy argument, and/or a rank-generic dummy argument. A type-or-kind-generic dummy argument is declared with a . A rank-generic dummy argument is declared with a . NOTE: An entity (which may be a dummy argument) whose type, kind, or rank depends on those of a generic dummy argument is not itself a generic dummy argument, but is a <>. The syntax for these will be discussed later. A or can appear only in a "generic type declaration statement" . A is a that shall appear only in the specification part of a generic subprogram. R8nn generic-type-decl-stmt is generic-type-spec [ [ , generic-attr-spec ] ... :: ] | [see 25-115] generic-dummy-arg-decl or declaration-type-spec , generic-attr-spec-list :: | generic-dummy-arg-decl C8nn If a generic-type-decl-stmt does not have a generic-type-spec, its generic-attr-spec-list shall contain a generic-rank-spec. R8nn generic-dummy-arg-decl is dummy-arg-name [ ( array-spec ) ] | [see Introduction] [ lbracket coarray-spec rbracket ] [ * char-length ] | [see 25-103] C8nn A generic dummy argument shall be a nonoptional dummy data object. Comment Non-optionality is absolutely required, otherwise the generated specific procedures would be ambiguous. | | C8nn: A dummy procedure of a generic subprogram shall have an explicit interface, and shall not be a generic | [See Introduction] dummy argument. | [see 24-139r2, option (3)] x04. Syntax for kind-generic specifications. R8nn kind-generic-type-spec is generic-intrinsic-type-spec or generic-derived-type-spec R8nn generic-intrinsic-type-spec is nonchar-intrinsic-type-name ( [ KIND = ] int-constant-expr ) or CHARACTER ( gen-char-type-params ) R8nn nonchar-intrinsic-type-name is REAL | INTEGER | LOGICAL | COMPLEX R8nn gen-char-type-params | [see 24-184r1] | is gen-char-len [ , [ KIND = ] int-constant-expr ] | or LEN= gen-char-len [ , KIND= int-constant-expr ] | or KIND= int-constant-expr , LEN= gen-char-len R8nn gen-char-len is * | : C8nn The int-constant-expr in a generic-intrinsic-type-spec shall be an array of rank one. Comment Basically, a kind-generic type-spec looks like a normal one except that it has an array expression for the kind type parameter. Example x04-1 GENERIC SUBROUTINE gensub(x,y) INTEGER([int8,int16,int32]),INTENT(INOUT) :: x CHARACTER(*,KIND=[ascii,iso_10646]) :: y R8nn generic-derived-type-spec is type-name ( gen-tp-spec-list ) R8nn gen-tp-spec is [ keyword = ] gen-tp-value R8nn gen-tp-value is int-constant-expr | * | : C8nn A gen-tp-value shall be * or : if and only if the type parameter is a length type parameter, otherwise the int-constant-expr shall | [see straw vote 1.5 in 25-156r1] | be a scalar or an array of rank one. C8nn A generic-derived-type-spec shall specify at least one kind type | parameter that is an array of rank one. Example x04-2 TYPE T(k1,k2,n) INTEGER,KIND :: k1,k2 INTEGER,LEN :: n REAL(k1) value(k2,n) END TYPE GENERIC SUBROUTINE gensub2(x) TYPE(t([kind(0.0),kind(0d0)],k2=[1,2,4,8],n=*)),INTENT(INOUT) :: x This covers 2 values for k1, and independently 4 values for k2, thus eight specifics: TYPE(t(k1=kind(0.0),k2=1,n=*) TYPE(t(k1=kind(0.0),k2=2,n=*) TYPE(t(k1=kind(0.0),k2=4,n=*) TYPE(t(k1=kind(0.0),k2=8,n=*) TYPE(t(k1=kind(0d0),k2=1,n=*) TYPE(t(k1=kind(0d0),k2=2,n=*) TYPE(t(k1=kind(0d0),k2=4,n=*) TYPE(t(k1=kind(0d0),k2=8,n=*) | [see straw vote 1 in 25-156r1] x04b. | [see straw vote 2 in 25-156r1] | Normative text: "Duplicate kind values in a kind-generic-type-spec are permitted, and treated as if only one appeared." For example, maybe the user wants to specify TYPE(REAL([selected_real_kind(3),selected_real_kind(2)])) x On a processor with no 16-bit real, or which has only one kind of 16-bit real, this would have duplicate values; on a processor that has both IEEE 16-bit and bfloat16, the values would be distinct. | x05. Syntax for type-generic specifications. R8nn generic-type-spec is TYPE ( generic-type-specifier-list ) or CLASS ( generic-type-specifier-list ) or generic-intrinsic-type-spec R8nn generic-type-specifier is intrinsic-type-spec or derived-type-spec or enum-type-spec or enumeration-type-spec or kind-generic-type-spec C8nn If the generic-type-spec keyword is CLASS, each generic-type-specifier shall identify an extensible type. C8nn A generic-type-specifier-list that contains no kind-generic-type-spec shall have more than one item. C8nn A generic-type-specifier shall specify that each length type parameter is assumed or deferred. Comment Length type parameters do not participate in generic resolution, so this simplification is mostly about saving the user's toes. Example x05-1 GENERIC FUNCTION plus(a,b) RESULT(r) TYPE(integer,real,complex),INTENT(IN) :: a TYPEOF(a),INTENT(IN) :: b TYPEOF(a) :: r r = a + b END FUNCTION This will generate three specifics, with signatures integer function(integer a,b) real function(real a,b) complex function(complex a,b) Example x05-2 TYPE t1 ... END TYPE TYPE t2 ... END TYPE GENERIC SUBROUTINE process(x) CLASS(t1,t2),INTENT(IN) :: x ... END SUBROUTINE This will generate two specifics, one with a CLASS(t1) argument, the other with a CLASS(t2) argument. | [see straw vote 3 in 25-156r1] | Normative text "A generic-type-specifier in a generic type declaration statement may specify the same type and type parameters as another. The redundant specification is ignored; however, the dummy arguments remain generic dummy arguments." | Example | USE ISO_FORTRAN_ENV | TYPE(REAL(REAL64), DOUBLE PRECISION) :: x | TYPE(INTEGER, INTEGER(INT32)) :: y | On some processors, the first type-spec will specify the same type | and type parameters as the second in one of those statements, and | on other processors the types will be distinct. x06. Rank-generic specification A dummy argument that is generic by rank is declared using a generic- rank-spec; having a generic-rank-spec makes a type declaration stmt into a generic type declaration statement, even if it is not generic by type/kind. R8nn generic-rank-spec is RANK ( generic-rank-list ) R8nn generic-rank is scalar-int-constant-expr or generic-rank-range | [see 24-181] R8nn generic-rank-range is scalar-int-constant-expr : scalar-int-constant-expr C8nn A scalar-int-constant-expr in a generic-rank-list shall be | nonnegative and less than or equal to the maximum supported rank | [see Introduction] for the corank of the dummy argument. | [see 24-181] C8nn If a generic-rank-list contains no generic-rank-range, it shall contain at least two s. A generic-rank-range specifies all the values that are both greater than or equal to the first expression and less than or equal to the second expression. | [see straw vote 5 in 25-156r1] Duplicate values specified by a generic-rank-list are permitted; the duplicate values are ignored. If that means that only one rank is applicable, the dummy argument still remains generic. Example x06 GENERIC SUBROUTINE lift(x,y) | [see 25-115] | TYPE(INTEGER(int32,int64), REAL), RANK(1:2), ALLOCATABLE :: x | TYPE(INTEGER(int32,int64), REAL), RANK(1:2), ALLOCATABLE :: y TYPEOF(x),RANKOF(y),ALLOCATABLE :: z ... END SUBROUTINE This subroutine defines 36 specific procedures with generic name lift, in which the variables x, y and z are allocatable, and have other characteristics respectively: int32, rank 1 int32, rank 1 int32, rank 1 int64, rank 1 int32, rank 1 int64, rank 1 real, rank 1 int32, rank 1 real, rank 1 int32, rank 2 int32, rank 1 int32, rank 1 int64, rank 2 int32, rank 1 int64, rank 1 real, rank 2 int32, rank 1 real, rank 1 int32, rank 1 int32, rank 2 int32, rank 2 ... etc. Example x06-2 GENERIC SUBROUTINE lift(x,y) TYPE(INTEGER(int32,int64), REAL), RANK(1:2), ALLOCATABLE :: x TYPEOF(x), RANKOF(x), ALLOCATABLE :: y TYPEOF(x),RANKOF(y),ALLOCATABLE :: z ... END SUBROUTINE This subroutine defines only 6 specific procedures with generic name lift. x07. | [see 25-128] | The intrinsic module ISO_FORTRAN_ENV shall contain the | simple scalar default integer function MAX_RANK. This function | has one optional scalar argument CORANK, of type integer. The | value of CORANK shall be non-negative. | | If the CORANK argument is absent, the result of MAX_RANK is the | maximum rank supported by the processor for an array that is | not a coarray. | | If the CORANK argument is present with a value less or equal to | the maximum corank supported by the processor for a scalar, the | result of MAX_RANK(CORANK) is the maximum rank supported for a | coarray with corank CORANK. | If the CORANK argument is present with a greater value, the | result of MAX_RANK is equal to -HUGE(0). | |Comment: Choosing -HUGE(0) here is consistent with some other intrinsic | procedures (e.g. DATE_AND_TIME), and is more resistant to error | if used in the middle of an expression. | |Example: For a processor whose limits are exactly the minimum required by | the standard, | MAX_RANK() = 15 | MAX_RANK(1) = 14 | MAX_RANK(16) = -2147483647 if default integer has 32 bits. | For a processor whose maximum rank is 24, and whose maximum rank | does not depend on the corank, | MAX_RANK() = 24 | MAX_RANK(1) = 24 | MAX_RANK(24) = 24 | MAX_RANK(25) = -2147483647 | |Note: This function version of MAX_RANK thus also enables discovery of | the maximum supported corank, as well as the relationship between | maximum rank and corank. | | A reference to the function MAX_RANK may appear in a constant | expression provided CORANK does not appear or is itself a | constant expression. | A reference to the function MAX_RANK may appear in a restricted | expression provided CORANK does not appear or is itself a | restricted expression. | x08. A RANKOF clause specifies that a local entity, including a dummy argument and function result, has the rank of a previously declared generic dummy argument. | [25-163] R830 rank-clause is RANK( scalar-int-constant-expr ) | (new) or RANKOF( dummy-argument-name ) C8nn The dummy-argument-name in a RANKOF clause shall be the name of a generic dummy argument. C8nn If the RANKOF clause appears in an attr-spec-list, the RANK or DIMENSION clause shall not appear. | Example TYPE (INTEGER, REAL), ALLOCATABLE, RANK(1:2) :: x GENERIC SUBROUTINE lift(x,y) TYPEOF(x), RANKOF(x) :: y ... END SUBROUTINE x09. The SELECT GENERIC RANK construct in a generic subprogram selects at most one of its constituent blocks in each instance of the subprogram. R1150a <> [ ]... R1151a <> [ : ] SELECT GENERIC RANK ( ) C1155a The in a shall be a named generic dummy argument that is generic by rank. R1152a is RANK ( ) [ ] or RANK DEFAULT [ ] | Note: is defined in x06. | [see straw vote 6 in 25-156r1] | C1156a Duplicate values specified by a generic-rank-list are | permitted; the duplicate values are ignored. C1157a For a given , the same value shall not be specified in more than one . C1158a For a given , there shall be at most one RANK DEFAULT . R1153a <> END SELECT [ ] The rules on are similar to those of the SELECT CASE construct. The execution of a SELECT GENERIC RANK construct with SELECT GENERIC RANK statement: SELECT GENERIC RANK ( x ) is similar to that of a SELECT CASE construct with SELECT CASE statement: SELECT CASE ( RANK(x) ) where the rank of x is constant. Comment. The intention is that each instantiation shall contain executable code for at most one block. Unlike the SELECT CASE construct, the SELECT GENERIC RANK statement is expected to have no runtime overhead of selection and branching. Example GENERIC FUNCTION fun(x) RESULT(y) TYPE(type1), RANK(0:7) :: x TYPEOF(x), RANK(RANK(x)) :: y SELECT GENERIC RANK (x) RANK (0) !! code if x is a scalar RANK (1:3) !! code if x is an array of 1 to 3 dimensions RANK DEFAULT !! code if x is an array of 4 to 7 dimensions END SELECT END FUNCTION fun x10. The SELECT GENERIC TYPE construct in a generic subprogram selects at most one of its constituent blocks in each instance of the subprogram. Comment. The selection is based on the declared type of a generic-type or generic-kind entity, not the dynamic type. R1154a <> [ ]... R1155a <> [ : ] SELECT GENERIC TYPE ( ) C1163a The in a shall be a generic dummy argument that is generic by type or kind. R1156a <> TYPE IS ( ) [ ] <> TYPE DEFAULT [ ] C1165a The shall be or and shall specify that each length type parameter is assumed. C1168a For a given , the same type and kind type parameter values shall not be specified in more than one TYPE IS . C1169a For a given , there shall be at most one CLASS DEFAULT . R1157a <> END SELECT [ ] Execution: A SELECT GENERIC TYPE construct selects just one block to be executed. Regardless of whether the selector is polymorphic or not, the block to be executed is selected by the declared type and kind of the . If it matches the of a TYPE IS , the block following that statement is selected. Otherwise, if there is a TYPE DEFAULT , the block following that statement is selected, else, no block is selected. Comment: The block is selected at compile time. The execution of the SELECT GENERIC TYPE construct is expected to be the execution of the selected block if any. Example GENERIC FUNCTION fun(x) RESULT(y) TYPE(type1,type2) :: x, y !! code if x is type1 or type2 SELECT GENERIC TYPE (x) TYPE IS (type1) !! code if x is type1 TYPE IS (type2) !! code if x is type2 END SELECT END FUNCTION fun 3. Further Examples =================== Example 1 MODULE example INTERFACE OPERATOR(.myop.) PROCEDURE s ! All of the specific procedures of s. END INTERFACE CONTAINS GENERIC FUNCTION s(a,b) RESULT(c) | [25-128] USE ISO_FORTRAN_ENV | TYPE(REAL,COMPLEX), INTENT(IN), RANK(1:MAX_RANK()) :: a TYPEOF(a),RANKOF(a), INTENT(IN) :: b TYPEOF(a), RANKOF(a) :: c, temp ... SELECT GENERIC TYPE (a) TYPE IS (REAL) temp = temp * (1-b) TYPE IS (COMPLEX) ! Just this once, we want the conjugate. temp = temp * (1-CONJG(b)) END SELECT ... c = temp END FUNCTION END MODULE Example 2 PROGRAM main INTEGER:: n = 5 WRITE(*,*) factorial(n) CONTAINS GENERIC RECURSIVE FUNCTION factorial(n) RESULT (res) USE ISO_FORTRAN_ENV INTEGER (int_kinds) :: n TYPEOF(n) :: res IF (n >1) THEN res = n*factorial(n-1) ELSE IF (n==1) THEN res = 1 ELSE res =0 END IF END FUNCTION factorial END PROGRAM main