X3J3/94-009 r0 28-Apr-94 TECHNICAL PROPOSALS This document contains technical proposals for revisions to Fortran 90. This is an internal working document of X3J3 and is regularly revised. These technical proposals are all in response to requirements defined by WG5. It requires a vote by X3J3 to add a proposal to this document or to change the status of one of the proposals. There are currently three possible status values for a proposal. D - Draft This status indicates that the general concept of a proposal has been adopted, but that the proposal needs further work. A - Approved This status indicates that a proposal has been approved and is ready for incorporation into the draft standard (document 007). I - Incorporated This status indicates that a proposal has been incorporated into the draft standard. No proposals yet have this status. ************************************************************************ List of Proposals Number Status Title 001 A Changes to the MAXLOC and MINLOC intrinsics 002 A NAMELIST comments 003 A Minimal field widths 004 D FORALL 005 D PURE procedures 005a A Rationale for FORALL and PURE (belongs as part of 004 and 005) 006 D Object Initialization 007 A Language evolution ---------------------------------------------------------------------------- Number: 001 Requirement: Changes to the MAXLOC and MINLOC Intrinsics (B9 Item A5) Status: Approved by X3J3 Technical Description: HPF modified the MAXLOC and MINLOC intrinsic functions to include an optional argument, DIM, which specifies the dimension along which the search is to be performed. This includes this functionality in Fortran 9x. Discussion: The HPF proposal creates an unfortunate incompatibility with Fortran 90. The incompatibility is a result of the new argument being inserted between the original first and second arguments. This was probably done for consistency with the argument location in the MAXVAL and MINVAL intrinsics. To avoid this incompatibility with Fortran 90, these functions are specified so that the two optional arguments may occur in either order. This places the burden on the processor to determine the order of the arguments (if the second argument is a scalar of type integer it is the DIM argument and if it is of type logical and conformable with the ARRAY argument it is the MASK argument). For consistency, this allowance for either order of the two optional arguments is extended to MAXVAL, MINVAL, PRODUCT, and SUM. As part of this proposal, a Fortran 90 compatibility section has been added. It does not replace the Fortran 77 compatibility section and it assumes that Fortran 9x will be upward compatible from Fortran 90. Both of these issues are yet to be decided and if the text below is accepted, it may require revision when these decisions are made. Detailed Edits: [3/3+]: Add the following section: 1.4.1 Fortran 90 Compatibility Except as noted in this section, this International Standard is an upward compatible extension to the preceding Fortran International Standard, ISO/IEC 1539:1991, informally known as Fortran 90, and a standard- conforming processor for this International Standard is a standard-conforming processor for Fortran 90. Any standard-conforming Fortran 90 program remains standard-conforming under this International Standard. The following Fortran 90 features have different interpretations in this International Standard: (1) The interfaces to the MAXLOC, MAXVAL, MINLOC, MINVAL, PRODUCT, and SUM intrinsics have been extended by this standard. This may conflict with a program that has supplied a generic interface to these intrinsics. [3/4]: Change "1.4.1" to "1.4.2". [219/6]: Add "DIM ," before "MASK" and add "or MAXLOC (ARRAY, MASK, DIM)" to the end of the line. [219/7]: Add "DIM ," before "MASK". [219/8]: Add "along dimension DIM" after "ARRAY". [219/12+]: Add the following description of the DIM argument: DIM (optional) must be scalar and of type integer with a value in the range 1 <= DIM <= n, where n is the rank of ARRAY. The corresponding actual argument must not be an optional dummy argument. [219/14]: Change "; it" to ". If DIM is absent the result". [219/15]: Before "." add "; otherwise, the result is an array of rank n-1 and shape (d(1), ..., d(DIM-1), d(DIM+1), ..., d(n)), where (d(1), ..., d(n)) is the shape of ARRAY". [219/17]: Change "If MASK is absent, the result" to "The result of MAXLOC (ARRAY)". [219/24]: Change "If MASK is present, the result" to "The result of MAXLOC (ARRAY, MASK = MASK)". [219/31+]: Add the following case to the description of the result value: Case (iii): If ARRAY has rank one, MAXLOC (ARRAY, DIM = DIM [, MASK = MASK]) has a value equal to that of MAXLOC (ARRAY [, MASK = MASK]). Otherwise, the value of element (s(1), ..., s(DIM-1), s(DIM+1), ..., s(n)) of MAXLOC (ARRAY, DIM = DIM [, MASK = MASK]) is equal to MAXLOC (ARRAY (s(1), s(2), ..., s(DIM-1), :, s(DIM+1), ..., s(n)) [, MASK = MASK (s(1), s(2), ..., s(DIM-1), :, s(DIM+1), ..., s(n)) ] ). [219/36+]: Add the following case to the examples: Case (iii): The value of MAXLOC ((/ 5, -9, 3 /), DIM=1) is 1. If B has the value | 1 3 -9 | | 2 2 6 |, MAXLOC (B, DIM=1) is [ 2 1 2 ] and MAXLOC (B, DIM=2) is [ 2 3 ]. Note that this is true even if B has a declared lower bound other than 1. [219/37]: Add "or MAXVAL (ARRAY, MASK, DIM)" to the end of the line. [220/22,24]: Change "DIM" to "DIM = DIM". [220/22,24]: Change "MASK" to "MASK = MASK". [221/23]: Add "DIM ," before "MASK" and add "or MINLOC (ARRAY, MASK, DIM)" to the end of the line. [221/24]: Add "DIM ," before "MASK". [221/25]: Add "along dimension DIM" after "ARRAY". [221/29+]: Add the following description of the DIM argument: DIM (optional) must be scalar and of type integer with a value in the range 1 <= DIM <= n, where n is the rank of ARRAY. The corresponding actual argument must not be an optional dummy argument. [221/31]: Change "; it" to ". If DIM is absent the result". [221/32]: Before "." add "; otherwise, the result is an array of rank n-1 and shape (d(1), ..., d(DIM-1), d(DIM+1), ..., d(n)), where (d(1), ..., d(n)) is the shape of ARRAY". [221/34]: Change "If MASK is absent, the result" to "The result of MAXLOC (ARRAY)". [222/6]: Change "If MASK is present, the result" to "The result of MAXLOC (ARRAY, MASK = MASK)". [222/13+]: Add the following case to the description of the result value: Case (iii): If ARRAY has rank one, MINLOC (ARRAY, DIM = DIM [, MASK = MASK]) has a value equal to that of MINLOC (ARRAY [, MASK = MASK]). Otherwise, the value of element (s(1), ..., s(DIM-1), s(DIM+1), ..., s(n)) of MINLOC (ARRAY, DIM = DIM [, MASK]) is equal to MINLOC (ARRAY (s(1), s(2), ..., s(DIM-1), :, s(DIM+1), ..., s(n)) [, MASK = MASK (s(1), s(2), ..., s(DIM-1), :, s(DIM+1), ..., s9n)) ] ). [222/18+]: Add the following case to the examples: Case (iii): The value of MINLOC ((/ 5, -9, 3 /), DIM=1) is 2. If B has the value | 1 3 -9 | | 2 2 6 |, MINLOC (B, DIM=1) is [ 1 2 1 ] and MINLOC (B, DIM=2) is [ 3 1 ]. Note that this is true even if B has a declared lower bound other than 1. [222/19]: Add "or MINVAL (ARRAY, MASK, DIM)" to the end of the line. [223/1,3]: Change "DIM" to "DIM = DIM". [223/1,3]: Change "MASK" to "MASK = MASK". [226/22]: Add "or PRODUCT (ARRAY, MASK, DIM)" to the end of the line. [227/5,7]: Change "DIM" to "DIM = DIM". [227/5,7]: Change "MASK" to "MASK = MASK". [235/5]: Add "or SUM (ARRAY, MASK, DIM)" to the end of the line. [235/27,29]: Change "DIM" to "DIM = DIM". [235/27,29]: Change "MASK" to "MASK = MASK". [Rationale]: Add the following sections to the new rationale: R13.13.65 MAXLOC Fortran 90 specified the MAXLOC intrinsic with only the ARRAY and MASK arguments. HPF added the DIM argument between the original two arguments for consistency with the MAXVAL intrinsic. This creates an incompatibility with Fortran 90 unless MAXLOC is specified as a generic interface with two specific interfaces: the first matching Fortran 90 and the second adding a non-optional DIM argument as the second argument. At X3J3 meeting 127, it was decided that this Standard should allow the DIM and MASK arguments to be specified in either order. For consistency, this provision for DIM and MASK to be specified in either order was extended to the other intrinsics, MAXVAL, MINVAL, PRODUCT, and SUM, which have the same arguments. R13.13.66 MAXVAL MAXVAL was extended to allow the DIM and MASK arguments to be specified in either order as part of the extensions to MAXLOC and MINLOC (R13.13.65,R13.13.70). R13.13.70 MINLOC Fortran 90 specified the MINLOC intrinsic with only the ARRAY and MASK arguments. HPF added the DIM argument between the original two arguments for consistency with the MINVAL intrinsic. This creates an incompatibility with Fortran 90 unless MINLOC is specified as a generic interface with two specific interfaces: the first matching Fortran 90 and the second adding a non-optional DIM argument as the second argument. At X3J3 meeting 127, it was decided that this Standard should allow the DIM and MASK arguments to be specified in either order. For consistency, this provision for DIM and MASK to be specified in either order was extended to the other intrinsics, MAXVAL, MINVAL, PRODUCT, and SUM, which have the same arguments. R13.13.71 MINVAL MINVAL was extended to allow the DIM and MASK arguments to be specified in either order as part of the extensions to MAXLOC and MINLOC (R13.13.65,R13.13.70). R13.13.81 PRODUCT PRODUCT was extended to allow the DIM and MASK arguments to be specified in either order as part of the extensions to MAXLOC and MINLOC (R13.13.65,R13.13.70). R13.13.103 SUM SUM was extended to allow the DIM and MASK arguments to be specified in either order as part of the extensions to MAXLOC and MINLOC (R13.13.65,R13.13.70). History: X3J3/93-275r1 (meeting 127) X3J3/94-037 (meeting 128) ---------------------------------------------------------------------------- Number: 002 Requirement: WG5 B9, B4.3: NAMELIST comments Technical Description: Provide a mechanism to allow comments in namelist input records. Discussion: The straw votes taken at meeting 127 indicated a preference for allowing comments, of the "to end of line" variety. Comments are allowed before and after "name-value" subsequences as well as before the initial "&" defining a particular namelist group name. Detailed Edits: On page 151, section 10.9.1, change item 1 to read: 1) Optional blanks and namelist comments, On page 152, section 10.9.1.2, add the following paragraph after the 4th paragraph: A namelist comment may appear after any value separator except a slash. A namelist comment is also permitted to start in the first position of an input record except within a character literal constant. On page 154, renumber section 10.9.1.6 to be 10.9.1.7, and add a new 10.9.1.6: 10.9.1.6 Namelist Comments Except within a character literal constant, a "!" character after a value separator or in the first non-blank position of a namelist input record initiates a comment. The comment extends to the end of the current input record. The comment is ignored. A slash within the namelist comment does not terminate execution of the namelist input statement. History: WG5/N901, X3J3/93-204r4, item 4 in X3J3 SD004, WG5/N840, X3J3/93-272, X3J3/94-021 ---------------------------------------------------------------------------- Number: 003 Requirement: WG5 B9 item: B4.1. Minimal Field Widths Status: Technical Description: Allow a field width of zero for I, B, O, Z, and F edit descriptors (in formatted output) to request that the processor select the smallest field width which will avoid "*****"s (field overflow). For the I, B, O, and Z edit descriptors, no leading blanks are produced, and a leading "+" is never written. When an "m" (minimal printable digit count) is specified, the appropriate number of leading zeros is still produced, i.e., the processor will chose a width >= m. For the F edit descriptor, no leading blanks are produced, and a leading "+" is never written. The optional leading zero just before the decimal point is not produced unless d was specified to be zero. A field width of zero is not permitted for other edit descriptors. Discussion: The straw votes taken at meeting 127 indicated a preference for this approach, rather than new edit descriptors (EX/LT, item 10 in SD004) to toggle the desired behavior. The processor, when it sees a zero field width specified, choses the smallest possible value for the field width, such that, if the user had specified that particular value, the processor would have printed "useful" data (not "*"s), and chosing any smaller field width would have resulted in the processor printing "*"s (field width overflow). This feature provides the ability to reduce the number of characters in an output file. Also, the user can maximize how many values can be printed on a single line and easily viewed on a terminal, while avoiding overflow "****"s in the output fields. Detailed Edits: On page 136, change the 3rd constraint to be: Constraint: e must be positive. Add this constraint after the 3rd constraint on page 136: Constraint: w must be zero or positive for the I, B, O, Z, and F edit descriptors. w must be positive for all other edit descriptors. In section 10.5.1.1, in the first paragraph, insert the following before the "." ending the first sentence [139:34]: , except when w is zero. On input, w must not be zero. When w is zero, the processor selects the field width In section 10.5.1.1, add the following to the end of the 4th paragraph [139:46]: When w is zero, the processor choses a positive field width such that no leading blanks are produced, nor a leading plus, and the characters produced do not exceed the processor selected field width. In section 10.5.1.1, add the following to the end of the 5th paragraph [140:4]: When w is zero, the processor choses a positive field width such that no leading blanks are produced, and the characters produced do not exceed the processor selected field width. In section 10.5.1.1, in the 6th paragraph, change the phrase "value of w" to be [140:8]: value of w, except when w is zero In section 10.5.1.1, add the following to the end of the 6th paragraph [140:9]: When w is zero, the processor choses a positive field width such that no leading blanks are produced, nor a leading plus, and the characters produced do not exceed the processor selected field width. When m and w are both zero, and the value of the internal datum is zero, no characters are produced, regardless of the sign control in effect. In section 10.5.1.2.1, in the 1st paragraph, insert the following before the 1st ",": (except when w is zero) In section 10.5.1.2.1, in the 1st paragraph, insert the following after the last sentence: When w is zero, the processor selects the field width. In section 10.5.1.2.1, in the last paragraph, insert the following after the last sentence [140:36]: When w is zero, the processor choses a positive field width such that no leading blanks are produced, nor a leading plus, and the characters produced do not exceed the processor selected field width. End of EDITS History: WG5/N901, X3J3/93-204r4, items 9 and 10 in X3J3 SD004, X3J3/93-273, X3J3/94-022 ---------------------------------------------------------------------------- Number: 004 X3J3 94-096 TO: X3J3 FROM: Dick Hendrickson SUBJECT: FORALL proposal REFERENCE: 94-013, 94-054, B9 resolution items A2 and A3 This is the FORALL proposal modified by the straw votes taken early in the week. I added a paragraph to section 14.1.3 defining the scoping of the index-name variables in the same way as they are defined for array constructors. They are local to the FORALL statement or construct and therefore don't conflict with other user defined variables of the same name although they inherit the user declared (or implicit) type. The Rationale is in paper 94-097 since in covers both the FORALL and PURE procedure proposal. FORALL proposal. [add to 14.1.3 Statement entities after line 22. We need to define the index- name variables as statement entities to the forall statement or construct. This was not in the original HPF draft nor in the original paper. The intent is to say that the index name is purely local and its use does not affect the value of other variables with the same name in the rest of the program. This is the same as the implied Do index in an array constructor. Note that there are several other edits to this section from interpretations.] The name of a variable that appears as an index name in a FORALL statement or FORALL construct has a scope of the statement or construct. It has the type and type parameters that it would have if it were the name of a variable in the scoping unit that includes the FORALL. Change the section name for 14.1.3, [245:15] to "Statement and construct entities". In 14.1.2 change the reference to "statement entities" to "statement or construct entities". [Extend rule R215 for executable-construct to include the forall-construct and R216 to include the forall-stmt:] Page 8, add to R215 executable construct: OR forall-construct Page 9, add to R216 action-stmt OR forall-stmt [Add at the end of the first paragraph in section 7.5:] Execution of a FORALL statement or FORALL construct controls the assignment to elements of arrays with a set of index variables and a mask expression. [Add a new section to chapter 7 at the end 7.5.3 [page 94] after the WHERE assignment section:] 7.5.4 Element array assignment - FORALL The element array assignment is used to mask the evaluation of expressions and assignment of values in assignment statements according to selection of index values and a mask expression. 7.5.4.1 The FORALL Construct The FORALL construct allows multiple assignments, masked array (WHERE) assignments, and nested FORALL statements and constructs to be controlled by a single forall-triplet-spec-list and scalar-mask. General Form of the FORALL Construct R781 forall-construct IS [forall-construct-name:] FORALL forall- header forall-body-stmt [ forall-body-stmt ] END FORALL[forall-construct-name] R782 forall-header IS (forall-triplet-spec-list & [,scalar-mask-expr]) R783 forall-triplet-spec IS index-name= & subscript:subscript[:stride] R784 forall-body-stmt IS forall-assignment OR where-stmt OR where-construct OR forall-stmt OR forall-construct R785 forall-assignment IS assignment-stmt OR pointer-assignment-stmt Constraint: Any procedure referenced in the scalar-mask-expr of a forall- header, including one referenced by a defined operation, must be PURE (12.xxx). Constraint: scalar-mask-expression must be scalar and of type logical. Constraint: A forall-body-stmt must not define any of the index-names. Constraint: index-name must be a scalar integer variable. Constraint: A subscript or stride in a forall-triplet-spec-list must not contain a reference to any index-name in the forall-triplet-spec-list in which it appears. Constraint: If the forall-header has a construct-name the end-forall must have the same construct-name. If the end-forall has a construct- name. the forall-header must have the same construct-name. Constraint: Any procedure referenced in a forall-body-stmt, including one referenced by a defined operation or assignment, must be PURE. To determine the set of permitted values for each index-name in the forall-header let m1 be value of the first subscript ("lower bound"); m2 be value of the second subscript ("upper bound"); m3 be the stride; and max be INT((m2 - m1 + m3)/m3) If stride is missing, it is as if it were present with the value 1. Stride must not have the value 0. The set of permitted values is m1 + (k-1) * m3, k = 1, 2, ..., max. If max <= 0 for some index-name, the forall-construct is not executed. Each assignment-statement or pointer-assignment-statement nested within a forall- construct assigns a value to a variable specified by the assignment for permitted values of the index-name variables. A program that causes multiple values to be assigned to the same variable by a single statement is not standard-conforming. A standard-conforming program may, however, assign to the same variable in different assignment statements. A forall-construct is not standard-conforming if a single assignment-statement or pointer-assignment-statement nested within it causes any atomic data object to be assigned more than one value. A data object is atomic if it contains no subobjects. For the purposes of this restriction, any assignment (including array assignment or assignment to a variable of derived type) to a non-atomic object is considered to assign to all subobjects contained by that object. begin footnote The scalar-mask-expr may depend on the index-name values. This allows a wide range of masking operations. A syntactic consequence of the semantic rule that no two execution instances of the body may assign to the same atomic data object is that each of the index-name variables must appear on the left-hand side of a forall-assignment. The converse is not true (i.e., using all index-name variables on the left-hand side does not guarantee there will be no interference). Because the condition is not sufficient, it does not appear as a constraint. Right-hand sides and expressions on the left hand side of a forall-assignment are defined as evaluated only for combinations of index-names for which the scalar-mask-expr evaluates to .TRUE. This has implications when the masked computation might create an error condition. For example, FORALL (i=1:n, y(i).NE.0.0) x(i) = 1.0 / y(i) does not cause a division by zero. end footnote begin footnote For example, an integer variable is an atomic object, but an array of integers is an object that is not atomic. Similarly, assignment to an array section is equivalent to assignments to each individual element (which may require further reductions when the array contains objects of derived type). This restriction allows cases such as FORALL ( i = 1:10 ) a(indx(i)) = b(i) if and only if indx contains no repeated values. Note that it restricts FORALL behavior, but not syntax. Syntactic restrictions to enforce this behavior would be either incomplete (i.e., allow undefined behavior) or exclude conceptually legal programs. end footnote The scope of an index-name is the FORALL construct itself(14.1.3). footnote The index variables inherit their type from the host scope, but their use does not modify any host variables of the same name. Given a sequence such as: INTEGER X REAL A(5,4) X=6 J=10 FORALL(X=1:5, J=1:4) A(X,J) = J At this point the variables X and J have the values 6 and 10 and the columns of A have the values 1, 2, 3, and 4. It would be a syntax error to use a name such as XX as an index. end footnote 7.5.4.2 Interpretation of the FORALL Construct Execution of a FORALL construct consists of the following steps: ! Evaluation, in any order, of the subscript and stride expressions in the forall-triplet-spec-list. The set of valid combinations of index- name values is then the Cartesian product of the sets defined by these triplets. ! Evaluation of the scalar-mask-expr for all valid combinations of index-name values. If the scalar mask expression is omitted, it is as if it were present with the value TRUE. The mask elements may be evaluated in any order. The set of active combinations of index-name values is the subset of the valid combinations for which the mask evaluates to TRUE. ! Execution of the forall-body-stmts in the order they appear. Each statement is executed completely (that is, for all active combinations of index-name values) according to the following interpretation: Statements in the forall-assignment category evaluate the expr and all expressions within variable (in the case of assignment-stmt) or target and all expressions within pointer-object (in the case of pointer-assignment-stmt) of the forall-assignment for all active combinations of index-name values. These evaluations may be done in any order. The expr values are then assigned to the corresponding variable locations (in the case of assignment-stmt) or the target values are associated with the corresponding pointer-object locations (in the case of pointer-assignment-stmt). The assignment or association operations may also be performed in any order. Statements in the where-stmt and where-construct categories evaluate their mask-expr for all active combinations of values of index-names. All elements of all masks may be evaluated in any order. The WHERE statement's assignment (or assignments within the WHERE branch of the construct) are then executed in order using the above interpretation of array assignments within the FORALL, but the only array elements assigned are those selected by both the active index-name values and the WHERE mask. Finally, the assignments in the ELSEWHERE branch are executed if that branch is present. The assignments here are also treated as array assignments, but elements are only assigned if they are selected by both the active combinations and by the negation of the WHERE mask. Statements in the forall-stmt and forall-construct categories first evaluate the subscript and stride expressions in the forall-triplet-spec-list for all active combinations of the outer FORALL constructs. The set of valid combinations of index-names for the inner FORALL is then the union of the sets defined by these bounds and strides for each active combination of the outer index-names, the outer index names being included in the combinations generated for the inner FORALL. The scalar mask expression is then evaluated for all valid combinations of the inner FORALL's index-names to produce the set of active combinations. If there is no scalar mask expression, it is as if it were present with the constant value .TRUE. Each statement in the inner FORALL is then executed for each active combination (of the inner FORALL), recursively following the interpretations given in this section. 7.5.4.3 General form of the element array assignment statement An element array assignment statement is a FORALL statement. R786 forall-stmt IS FORALL forall-header & forall-assignment A FORALL statement is equivalent to a FORALL construct where the forall- assignment is the single body statement in the FORALL construct. The scope of an index-name is the FORALL statement itself. FORALL statements cannot have construct names. footnote A FORALL construct means roughly the same thing as does replicating the FORALL header in front of each array assignment statement in the block, except that any expressions in the FORALL header are evaluated only once, rather than being re-evaluated before each of the statements in the body. The exceptions to this rule are nested FORALL statements and WHERE statements, which introduce syntactic and functional complications into the copying. In general, any expression in a FORALL is evaluated only for valid combinations of all surrounding index-names for which all the scalar mask expressions are .TRUE. Nested FORALL bounds and strides can depend on outer FORALL index-names. They cannot redefine those names, even temporarily. Statements can use the results of computations in lexically earlier statements, including computations done for other index-name values. However, an assignment never uses a value assigned in the same statement by another index-name value combination. end footnote 7.5.4.4 FORALL Examples Example 1: FORALL (j=1:m, k=1:n) x(k,j) = y(j,k) FORALL (k=1:n) x(k,1:m) = y(1:m,k) These statements both copy columns 1 through n of array y into rows 1 through n of array x. This is equivalent to x(1:n,1:m) = TRANSPOSE(y(1:m,1:n)) Example 2: This FORALL sets array element x(i,j) to the value 1/(i+j-1) for values of i and j between 1 and n. FORALL (i=1:n, j=1:n) x(i,j) = 1.0 / REAL(i+j-1) Example 3: FORALL (i=1:n, j=1:n, y(i,j).NE.0) x(i,j) = 1.0/y(i,j) This statement takes the reciprocal of each nonzero element of array y(1:n,1:n) and assigns it to the corresponding element of array x. Elements of y that are zero do not have their reciprocal taken, and no assignments are made to the corresponding elements of x. This is equivalent to WHERE (y(1:n,1:n) .NE. 0) x(1:n,1:n) = 1.0 / y(1:n,1:n) Example 4: FORALL ( i=1:n ) x(indx(i)) = x(i) This FORALL statement is equivalent to the array assignment x(indx(1:n)) = x(1:n) If indx contains a permutation of the integers from 1 to n, then the final contents of x will be a permutation of the original values. If indx contains repeated values, the statement is not standard conforming. Example 5: FORALL (i=2:4) x(i) = x(i-1) + x(i) + x(i+1) Has the same effect as the statement x(2:4) = x(1:3) + x(2:4) + x(3:5) Example 6: FORALL (i=1:n) a(i,i) = x(i) This FORALL statement sets the elements of the main diagonal of matrix a to the elements of vector x. Example 7: FORALL (k=1:5) j(k) = SUM(j(1:k)) This FORALL statement computes five partial sums of subarrays of j. (SUM is allowed in a FORALL because intrinsic functions are pure; see Section XXXX.) If before the FORALL j =(/ 1, 2, 3, 4, 5/) then after the FORALL j =(/1, 3, 6, 10, 15/) Example 8: FORALL ( i=2:n-1, j=2:n-1 ) a(i,j) = a(i,j-1) + a(i,j+1) + a(i-1,j) + a(i+1,j) b(i,j) = a(i,j) END FORALL This FORALL is equivalent to the two statements a(2:n-1,2:n-1) = a(2:n-1,1:n-2)+a(2:n-1,3:n) & +a(1:n-2,2:n-1)+a(3:n,2:n-1) b(2:n-1,2:n-1) = a(2:n-1,2:n-1) In particular, note that the assignment to array b uses the values of array a computed in the first statement, not the values before the FORALL began execution. Example 9: FORALL ( i=1:n-1 ) FORALL ( j=i+1:n ) a(i,j) = a(j,i) END FORALL This FORALL construct assigns the transpose of the lower triangle of array a (i.e., the section below the main diagonal) to the upper triangle of a. For example, if n=3 and a originally contained the matrix 0 3 6 1 4 7 2 5 8 then after the FORALL it would contain 0 1 2 1 4 5 2 5 8 This could also be achieved with a single FORALL statement FORALL ( i = 1:n-1, j=1:n, j >i) a(i,j) = a(j,i) Example 10: FORALL ( i=1:5 ) WHERE ( a(i,:) .NE. 0.0 ) a(i,:) = a(i-1,:) + a(i+1,:) ELSEWHERE b(i,:) = a(6-i,:) END WHERE END FORALL This FORALL construct, when executed with the input arrays a = 0 0 0 0 0 1 1 1 0 1 2 2 0 2 2 3 0 3 3 3 0 0 0 0 0 and b = 0 0 0 0 0 10 10 10 10 10 20 20 20 20 20 30 30 30 30 30 40 40 40 40 40 will produce as results a = 0 0 0 0 0 2 2 0 0 2 4 1 0 3 4 2 0 0 2 2 0 0 0 0 0 and b = 0 0 0 0 0 10 10 10 2 10 20 20 0 20 20 30 2 30 30 30 0 0 0 0 0 Note that assignments in the WHERE branch may affect computations in the ELSEWHERE branch. ---------------------------------------------------------------------------- Number: 005 X3J3 94-098 TO: X3J3 FROM: Dick Hendrickson SUBJECT: Pure procedures REFERENCES: 94-013, 94-054, B9 resolution item A4 12.xxxx Pure Procedures 12.xxxx.1 Pure Procedure Declaration and Interface If a user-defined procedure is used in a context that requires it to be PURE, then its interface must be explicit in the scope of that use, and that interface must specify the PURE keyword. This keyword is specified in the function-stmt or subroutine- stmt. Intrinsic functions are always PURE and require no explicit declaration of this fact. Intrinsic subroutines are PURE if they are elemental (e.g., MVBITS) but not otherwise. A statement function is PURE if and only if all functions that it references are PURE. A procedure that is PURE is referred to as a "PURE procedure". The following constraints apply to PURE functions or subroutines. Constraint: The specification-part of a PURE function must specify that all dummy arguments have INTENT (IN) except procedure arguments and arguments with the POINTER attribute. Constraint: The specification-part of a PURE subroutine must specify the intents of all dummy arguments except procedure arguments, alternate return specifiers, and arguments that have the POINTER attribute. Constraint: A local variable declared in the specification-part or internal- subprogram-part of a PURE procedure must not have the SAVE attribute. footnote Note local variable initialization in a type-declaration- stmt or a data-stmt implies the SAVE attribute; therefore, such initialization is also disallowed. end footnote Constraint: The execution-part and internal-subprogram-part of a PURE procedure must not use a dummy argument with INTENT(IN), a global variable, or an object that is storage associated with a global variable, or a subobject thereof, in the following contexts: ! As the assignment variable of an assignment-stmt; ! As a DO variable or implied DO variable; ! As an input-item in a read-stmt; ! As an internal-file-unit in a write-stmt; ! As an IOSTAT= or SIZE= specifier in an I/O statement; ! In an assign-stmt; ! As the pointer-object or target of a pointer-assignment-stmt; ! As the expr of an assignment-stmt whose assignment variable is of a derived type, or is a pointer to a derived type, that has a pointer component at any level of component selection; ! As an allocate-object or stat-variable in an allocate-stmt or deallocate-stmt, or as a pointer-object in a nullify-stmt; or ! As an actual argument associated with a dummy argument with INTENT (OUT) or INTENT (INOUT) or with the POINTER attribute. Constraint: Any procedure referenced in a PURE procedure, including one referenced via a defined operation or assignment, must be PURE. Constraint: A PURE procedure must not contain a print-stmt, open-stmt, close-stmt, backspace-stmt, endfile-stmt, rewind-stmt, or inquire-stmt; Constraint: A PURE procedure must not contain a read-stmt or write-stmt whose io-unit is an external-file-unit or *. Constraint: A PURE procedure must not contain a stop-stmt. begin footnote The above constraints are designed to guarantee that a PURE procedure is free from side effects (i.e., modifications of data visible outside the function), which means that it is safe to reference in any order, for example in a FORALL assignment statement. The constraints for PURE subroutines are based on the same principles as for PURE functions, except that side effects to INTENT (OUT) and INTENT (INOUT) dummy arguments are permitted. Pointer dummy arguments are always treated as INTENT (INOUT). Pure subroutines are included to allow subroutine calls from PURE procedures in a safe way, and to allow forall- assignments to be defined assignments. The constraint requiring explicit INTENT (IN) for function arguments declares behavior that is ensured by the following rules. It is not technically necessary, but is included for consistency with the explicit declaration rules for defined operators. Note that POINTER arguments may not have the INTENT attribute; the restrictions below ensure that POINTER arguments also behave as if they had INTENT (IN), for both the argument itself and the object pointed to. The constraint disallowing SAVE variables ensures that a PURE procedure does not retain an internal state between calls, which would allow side-effects between calls to the same procedure The constraint giving the restrictions on use of global variables and dummy arguments ensures that dummy arguments and global variables are not modified by the procedure. In the case of a dummy or global pointer, this applies to both its pointer association and its target value, so it cannot be subject to a pointer assignment or to an ALLOCATE, DEALLOCATE, or NULLIFY statement. Incidentally, these constraints imply that only local variables and the dummy function result variable can be subject to assignment or pointer assignment. In addition, a dummy or global data object cannot be the target of a pointer assignment (i.e., it cannot be used as the right hand side of a pointer assignment to a local pointer or to the result variable), for then its value could be modified via the pointer. (An alternative approach would be to allow such objects to be pointer targets, but disallow assignments to those pointers; syntactic constraints to allow this would be even more draconian than these.) In connection with the last point, it should be noted that an ordinary (as opposed to pointer) assignment to a variable of derived type that has a pointer component at any level of component selection may result in a pointer assignment to the pointer component of the variable. That is certainly the case for an intrinsic assignment. In that case, the expression on the right hand side of the assignment has the same type as the assignment variable, and the assignment results in a pointer assignment of the pointer components of the expression result to the corresponding components of the variable (see section 7.5.1.5 of the Fortran 90 standard). However, it may also be the case for a defined assignment to such a variable, even if the data type of the expression has no pointer components; the defined assignment may still involve pointer assignment of part or all of the expression result to the pointer components of the assignment variable. Therefore, a dummy or global object cannot be used as the right hand side of any assignment to a variable of derived type with pointer components, for then it, or part of it, might be the target of a pointer assignment, in violation of the restriction mentioned above. (Incidentally, the last two paragraphs only prevent the reference of a dummy or global object as the only object on the right hand side of a pointer assignment or an assignment to a variable with pointer components. There are no constraints on its reference as an operand, actual argument, subscript expression, etc. in these circumstances.) Finally, a dummy or global data object cannot be used in a procedure reference as an actual argument associated with a dummy argument of INTENT (OUT) or INTENT (INOUT) or with a dummy pointer, for then it may be modified by the procedure reference. This constraint, like the others, can be statically checked, since any procedure referenced within a PURE function must be either a PURE function, which does not modify its arguments, or a PURE subroutine, whose interface must specify the INTENT or POINTER attributes of its arguments (see below). Incidentally, notice that in this context it is assumed that an actual argument associated with a dummy pointer is modified, since Fortran 95 does not allow its intent to be specified. The constraint that only PURE procedures may be called ensures that all procedures called from a PURE procedure are themselves side-effect free, except, in the case of subroutines, for modifying actual arguments associated with dummy pointers or dummy arguments with INTENT (OUT) or INTENT (INOUT). As we have just explained, it can be checked that global or dummy objects are not used in such arguments, which would violate the required side-effect freedom. A constraint prevents external I/O and file operations, whose order would be non-deterministic in the context of concurrent execution. Note that internal I/O is allowed, provided that it does not modify global variables or dummy arguments. Finally, a constraint disallows STOP statements. A STOP brings execution to a halt, which is a rather drastic side effect. end footnote footnote The constraints for a PURE procedure guarantee freedom from side-effects, thus ensuring that it can be invoked concurrently at each "element" of an array (where an "element" may itself be a data structure, including an array). The constraints on PURE procedures may appear complicated, but it is not necessary for a programmer to be intimately familiar with them. From the programmer's point of view, these constraints can be summarized as follows: a PURE procedure must not contain any operation that could conceivably result in an assignment or pointer assignment to a global variable or INTENT (IN) dummy argument, or perform any I/O or STOP operation. Note the use of the word conceivably; it is not sufficient for a PURE procedure merely to be side-effect free in practice. For example, a function that contains an assignment to a global variable but in a branch that is not executed in any invocation of the function is nevertheless not a PURE function. The exclusion of functions of this nature is unavoidable if strict compile-time checking is to be used. In the choice between compile-time checking and flexibility, the committee decided in favor of enhanced checking. It is expected that most library procedures will conform to the constraints required of PURE procedures (by the very nature of library procedures), and so can be declared PURE and referenced in FORALL statements and constructs and within user-defined PURE procedures. It is also anticipated that most library procedures will not reference global data, whose use may sometimes inhibit concurrent execution. The constraints on PURE procedures are limited to those necessary to check statically for freedom from side effects, processor independence, and for lack of saved internal state. Subject to these restrictions, maximum functionality has been preserved in the definition of PURE procedures. This has been done to make function calls in FORALL as widely available as possible, and so that quite general library procedures can be classified as PURE. A drawback of this flexibility is that PURE procedures permit certain features whose use may hinder, and in the worst case prevent, concurrent execution in FORALL (that is, such references may have to be implemented by sequentialization). Foremost among these features are the access of global data, particularly distributed global data, and the fact that the arguments and, for a PURE function, the result may be pointers or data structures with pointer components, including recursive data structures such as lists and trees. The programmer should be aware of the potential performance penalties of using such features. end footnote Replace BNF rules with: R1217 prefix IS prefix-spec [ prefix-spec ] R1217A prefix-spec IS type-spec OR RECURSIVE OR PURE R1220 subroutine-stmt IS [ prefix ] SUBROUTINE & subroutine-name [ ( [ dummy-arg-list ] ) ] Constraint: A prefix must contain at most one of each variety of prefix-spec. Constraint: The prefix of a subroutine-stmt must not contain a type-spec. Pure procedure interfaces To define interface specifications for PURE procedures, the following constraints are added to Rule R1204 in Section 12.3.2.1 of the Fortran 90 standard (defining interface-body): Constraint: An interface-body of a PURE procedure must specify the intents of all dummy arguments except POINTER, alternate return, and procedure arguments. The procedure characteristics defined by an interface body must be consistent with the procedure's definition. Regarding PURE procedures, this is interpreted as follows: ! A procedure that is declared PURE at its definition must be declared PURE if it appears in an interface body, but this is not required. ! A procedure that is not declared PURE at its definition must not be declared PURE in an interface body. That is, if an interface body contains a PURE keyword, then the corresponding procedure definition must also contain it, though the reverse is not true. When a procedure definition with a PURE keyword is compiled, the compiler may check that it satisfies the necessary constraints. Pure Procedure Reference To define PURE procedure references, the following extra constraint is added to Rules R1209 and R1210 in Section 12.4.1 of the Fortran 90 standard (defining function-reference and call-stmt): Constraint: In a reference to a PURE procedure, a procedure-name actual- arg must be the name of a PURE procedure. footnote This constraint ensures that the purity of a procedure cannot be undermined by allowing it to call a non-PURE procedure. end footnote Examples of Pure Procedure Usage Pure functions may be used in expressions in FORALL statements and constructs, unlike general functions. Several examples of this are given below. ! Intrinsic functions are always PURE FORALL ( i = 1:n ) a(i,i) = log( abs( a(i,i) ) ) Because a forall-assignment may be an array assignment, the PURE function can have an array result. Such functions may be particularly helpful for performing row-wise or column-wise operations on an array. The next example illustrates this. INTERFACE PURE FUNCTION f(x) REAL, DIMENSION(3) :: f REAL, DIMENSION(3), INTENT (IN) :: x END FUNCTION f END INTERFACE REAL v (3,10,10) ... FORALL (i=1:10, j=1:10) v(:,i,j) = f(v(:,i,j)) Because PURE procedures have no constraints on their internal control flow (except that they may not use the STOP statement), they also provide a means for encapsulating more complex operations than could otherwise be nested within a FORALL. For example, the fragment below performs an iterative algorithm on every element of an array. Note that different amounts of computation may be required for different inputs. Some machines may not be able to take advantage of this flexibility. PURE INTEGER FUNCTION iter(x) COMPLEX, INTENT (IN) :: x COMPLEX xtmp INTEGER i i = 0 xtmp = -x DO WHILE (ABS(xtmp).LT.2.0 .AND. i.LT.1000) xtmp = xtmp * xtmp - x i = i + 1 END DO iter = i END FUNCTION ... FORALL (i=1:n, j=1:m) & ix(i,j) = iter(CMPLX(a+i*da,b+j*db)) Add to appendix B, the deleted features appendix. Constraint: A PURE procedure must not contain a pause-stmt. footnote A pause requires some form of Input or output and is disallowed for the same reasons that other I/O statements are disallowed. end footnote ---------------------------------------------------------------------------- Number: 005a X3J3 94-097 TO: X3J3 FROM: Dick Hendrickson SUBJECT: Rationale for FORALL and PURE The following is the rationale section for the FORALL and PURE features. NOTE that the HPF document is copyrighted by Rice University. Rice has given free permission to copy from the document, but the copyright must be mentioned. Paper 94-013 should have mentioned this fact also. Rational: The FORALL statement and construct and PURE procedures were added to Fortran 95 to allow the majority of programs coded in High Performance Fortran (HPF) to run on a standard conforming Fortran 95 processor with little change. These added concepts are the major new syntatic features of HPF. HPF was primarily designed to allow programs to execute efficiently on multi-processor systems. Adding these features to Fortran 95 does not imply that a Fortran 95 processor is a multi- processor nor that any features of the language are safe or consistent on a multi- processor system. Some of the text describing the FORALL and PURE features was taken directly >from the High Performance Fortran Language Specification, Version 1.0, May 3, 1993 c1993 Rice University, Houston Texas. The text was copied with the permission of Rice University. The purpose of the FORALL statement and construct is to provide a convenient syntax for simultaneous assignments to large groups of array elements. Such assignments lie at the heart of the data parallel computations that HPF is designed to express. The multiple assignment functionality it provides is very similar to that provided by the array assignment statement and the WHERE construct in Fortran 90. FORALL differs from these constructs in its syntax, which is intended to be more suggestive of local operations on each element of an array, and in its generality, which allows a larger class of array sections to be specified. In addition, a FORALL may invoke user-defined functions on the elements of an array, simulating Fortran 90 elemental function invocation (albeit with a different syntax). HPF defines a new procedure attribute, PURE, to declare the class of functions that may be invoked in this way. Both single-statement and block FORALL forms are defined in this section, as well as the PURE attribute and constraints arising from the use of PURE. Fortran 90 places several restrictions on array assignments. In particular, it requires that operands of the right side expressions be conformable with the left hand side array. These restrictions can be relaxed by introducing the element array assignment statement, usually referred to as the FORALL statement. This statement is used to specify an array assignment in terms of array elements or groups of array sections, possibly masked with a scalar logical expression. In functionality, it is similar to array assignment statements and WHERE statements. The FORALL statement essentially preserves the semantics of Fortran 90 array assignments and allows for convenient assignments like FORALL ( i=1:n, j=1:m ) a(i,j)=i+j as opposed to standard Fortran 90 a = SPREAD((/(i,i=1,n)/), DIM=2, NCOPIES=m) + & SPREAD((/(i,i=1,m)/), DIM=1, NCOPIES=n) It can also express more general array sections than the standard triplet notation for array expressions. For example, FORALL ( i = 1:n ) a(i,i) = b(i) assigns to the elements on the main diagonal of array a. It is important to note, however, that FORALL is not intended to be a general parallel construct; for example, it does not express pipelined computations or MIMD computation well. This was an explicit design decision made in order to simplify the construct and promote agreement on the statement's semantics. A PURE function is one that obeys certain syntactic constraints that ensure it produces no side effects. This means that the only effect of a pure function reference on the state of a program is to return a result---it does not modify the values, pointer associations, or data mapping of any of its arguments or global data, and performs no external I/O. A pure subroutine is one that produces no side effects except for modifying the values and/or pointer associations of INTENT (OUT) and INTENT (INOUT) arguments. These properties are declared by a new attribute (the PURE attribute) of the procedure. A pure procedure (i.e., function or subroutine) may be used in any way that a normal procedure can. However, a procedure is required to be pure if it is used in any of the following contexts: ! In the mask or body of a FORALL statement or construct; ! Within the body of a PURE procedure; or ! As an actual argument in a PURE procedure reference. The freedom from side effects of a pure function allows the function to be invoked concurrently in a FORALL without such undesirable consequences as nondeterminism, and additionally assists the efficient implementation of concurrent execution. Syntactic constraints (rather than semantic constraints on behavior) are used to enable compiler checking. ---------------------------------------------------------------------------- Number: 006 Requirement Title: B9/B1 Object Initialization Status: X3J3 consideration in progress Technical Description: Currently all pointers are created with an undefined association status. There is a requirement to change this to allow some pointers to be created with a disassociated association status. The means chosen by straw vote within X3J3 to specify an initial association status of disassociated involves the appearance in initialization contexts of a new intrinsic function NULL with a single optional argument. The syntax "=> NULL( )" may appear in a type declaration statement. The intrinsic function may also appear in a structure constructor to correspond with a pointer component. There is a further requirement to extend a type definition to contain the specification of a default initial value for a nonpointer component and the specification of disassociated status as the default for a pointer component. It is not necessary for a default value to be specified for each nonpointer component in a definition nor to change the initial association status of each pointer component from undefined to disassociated. If a component is of derived type, its initial state may be specified in the type definition of that type. Conversely, even though a derived- type component has an initial state specified in its type definition, the initial state may be overridden by the use of a structure constructor for that type in the (higher- level) component specification. The effect of specifying default initial values for nonpointer components and the disassociated status for pointer components is that whenever an object of the type is created, by declaration or allocation, it will be automatically initialized as indicated in the type definition. As is the case with objects of intrinsic type, it is possible to specify initial values for objects of derived type in type declarations and DATA statements. For an object of derived type, an initial value so specified overrides any default initialization contained in the type definition. An object of a derived type with default initialization specified must not appear in a DATA statement. Note that whereas initialization in a type declaration statement or a DATA statement implies that the object initialized has the SAVE attribute, specification of default initialization carries no such implication for objects of the type. The syntax needed to meet this requirement for nonpointer components, would seem to be a natural extension of the initialization mechanism used for an object in a type declaration. It is merely a matter of allowing = initialization to appear in a component specification. The means described above for initializing a pointer to have an initial status of disassociated can then be used to specify a default disassociated status for a pointer component. To summarize these extensions: - => NULL( ) can appear in a type declaration. - NULL([]) can appear in a structure constructor. - Initialization specifications that can appear in a type declaration statement can also appear in a component specification to specify default initialization. - Default initialization can be overridden by a higher level default initialization or by explicit initialization. Discussion: The specification of default initialization is a facility that is not available for objects of intrinsic type. The current proposal does not allow a pointer to have a default association status of defined, although there is no conceptual problem with doing this so long as the target has been declared with the TARGET and SAVE attributes. Detailed Edits:[94-006r0 was integrated into these edits. The only collisions were items 87, and 97 as noted below. Text to be placed in italics appears as <.....>; text to placed in bold font appeas as <<.....>>.] [32:36+] add a new paragraph <>is specified for a component of an object of derived type when initialization appears in the component declaration. When an object of the type is created by declaration (5.1) or allocation (6.3.1), that object will be initialized as specified in the derived type definition even if the definition is private and inaccessible in the scope of the declaration or ALLOCATE statement. Unlike explicit initialization, default initialization does not imply that the object has the SAVE attribute. [33:29-30] replace with R429 <> [ ( ) ] [ * ] [ ] R429a <> = <> => NULL ( ) [33:38+] add constraints Constraint: If appears, a double colon separator must appear before the Constraint: If => appears in , the POINTER attribute must appear in the If = appears in , the POINTER attribute must not appear in the . [34:1-2] replace with The double colon separator in a is required if the DIMENSION attribute, the POINTER attribute, or is specified; otherwise, it is optional. If appears, an object of the type becomes defined with the default initial value determined from in the scoping unit of the type definition in accordance with the rules of intrinsic assignment (7.5.1.4) unless the default initial value is overridden by explicit initialization. [34:6+] add Default initialization of an array component may be specified by a constant expression consisting of an array constructor or a single scalar that becomes the value of each array element. A component is a pointer if its contains the POINTER attribute. Pointers have an association status of "undefined", "disassociated", or "associated". If no default initialization is specified, the initial status is "undefined". <> sets the status of a pointer to "disassociated". To specify that the default initialization of a pointer component is to be "disassociated", the pointer assignment symbol (=>) must be followed by a reference to the intrinsic function NULL( ) with no argument. No mechanism is provided to specify a default initial status of "associated". [35:30] insert after "component." The type definition may specify that in objects declared to be of this type, such a pointer is initially disassociated. [35:33] replace with TYPE(NODE), POINTER :: NEXT_NODE => NULL( ) [35:34] Add It is not required that initialization be specified for each component of a derived type. If a component is of intrinsic type and is not a pointer, a default initial value may be specified by an initialization expression. If the component is an array, the initialization expression must be conformable; it may be a scalar or an array constructor (4.5). If the component is a pointer, a limited form of pointer assignment (7.5.2) may be used to specify that in objects of the type, the pointer component is initially disassociated. If the component is of derived type and does not have the pointer attribute, its default initial value may be specified in the type definition for that derived type or by a structure constructor that may override any lower-level default initialization. For example: TYPE DATE INTEGER DAY CHARACTER (5) MONTH INTEGER :: YEAR = 1995 ! Partial default initialization END TYPE DATE TYPE (DATE), PARAMETER :: TODAY = DATE (21, "Feb.", 1995) The default initial value for the YEAR component of TODAY is overridden by explicit initialization in the type declaration statement. The default initial value of a component of derived type may be overridden by a higher level default initialization. For example: TYPE SINGLE_SCORE TYPE(DATE) :: PLAY_DAY = TODAY INTEGER SCORE TYPE(SINGLE_SCORE), POINTER :: NEXT => NULL( ) END TYPE SINGLE_SCORE TYPE(SINGLE_SCORE) SETUP The PLAY_DAY component of SETUP receives its initial value from TODAY overriding the lower-level initialization for the YEAR component. Arrays of structures may be declared whose elements are partially or totally initialized by default. For example: TYPE MEMBER CHARACTER (20) NAME INTEGER :: TEAM_NO, HANDICAP = 0 TYPE (SINGLE_SCORE), POINTER :: HISTORY => NULL( ) END TYPE MEMBER TYPE (MEMBER) LEAGUE (36) ! Array of partially initialized ! elements TYPE (MEMBER) :: ORGANIZER = MEMBER ("I. Manage", 1, 5, NULL( )) ORGANIZER is explicitly initialized, overriding the default initialization for an object of type MEMBER. Allocated objects may also be initialized partially or totally. For example: ALLOCATE (ORGANIZER % HISTORY) ! A partially initialized object of ! type SINGLE_SCORE is created. [37:11] replace "constant expressions" with constant expressions or pointer nullifications [39:36-37] replace with R504 <> [ ( ) ] [ * ] [ ] [39:38+] add R504a <> = <> => NULL ( [ ] ) [40:1] = -> [40:3] The = -> [40:5] delete "a pointer," [40:6+] add constraints Constraint: If NULL (] appears in , must be the name of an object with the same type, kind type parameter, and rank as . [40:34] = -> [40:44] an = -> explicit initialization. [41:7] (7.5.1.4) -> (7.5.1.4) or pointer assignment (7.5.2) [41:9-10] change sentence to [includes edit from defect item 87] The presence of implies that is saved, except for an in a named common block or an with the PARAMETER attribute. [41:12+] add paragraph If => appears in initialization, for an object, the object must have the POINTER attribute; it may be specified in the type declaration statement or in a previous POINTER statement. If the optional argument to the NULL intrinsic function is present, it must have the same type parameters as the object. [41:22+] add TYPE(CHAIN), POINTER :: HEAD => NULL( ) [44:6+] add TYPE(NODE), PARAMETER :: DEFAULT = NODE(0, NULL( )) [51:35+] add sentence, "If an object or subobject has been specified with default initialization in a type definition, it must not appear in a ." [76:22+] add The intrinsic function NULL returns a disassociated pointer. A disassociated pointer has no shape but does have rank. The data type, type parameters, and rank of the result of the intrinsic function NULL when it appears without an argument are determined by the pointer that becomes associated with the result. If the intrinsic function appears on the right of a pointer assignment statement, the type, type parameters, and rank of the result are those of the pointer on the left. If the intrinsic function appears in for an object in a type declaration statement, the type, type parameters, and rank of the result are those of the object. If the intrinsic function appears as a default initialization specification in a , the type, type parameters, and rank of the result are those of the component. If the intrinsic function appears in a structure constructor, the type, type parameters, and rank are determined by the corresponding pointer component. If the intrinsic function appears as an actual argument in a procedure reference, the type, type parameters, and rank of the result are those of the corresponding dummy argument. The optional argument is required when the intrinsic function NULL appears as an actual argument in a reference to a generic procedure if the argument type, type parameters, and rank are required to resolve the reference. For example: INTERFACE GEN SUBROUTINE S1 (J, PI) INTEGER J INTEGER, POINTER :: PI END SUBROUTINE S1 SUBROUTINE S2 (K, PR) INTEGER K REAL, POINTER :: PR END SUBROUTINE S2 END INTERFACE REAL, POINTER :: REAL_PTR CALL GEN (7, NULL (REAL_PTR) ) ! Invokes S2 [77:24-28] replace with [replacement includes edit in 006, item 97] (6) A reference to an intrinsic function which is: a) an array inquiry function (13.10.15) other than ALLOCATED, b) the bit inquiry function BIT_SIZE, c) the character inquiry function LEN, d) the kind inquiry function KIND, e) the pointer nullification function NULL, or f) a numeric inquiry function (13.10.8) and where each argument of the function is a) a constant expression, or b) a variable whose properties inquired about are not: 1) assumed 2) defined by an expression that is not a constant expression or 3) definable by an ALLOCATE or POINTER assignment statement [78:6-10] replace with [replacement includes edit in 006, item 97] (6) A reference to an intrinsic function which is: a) an array inquiry function (13.10.15) other than ALLOCATED, b) the bit inquiry function BIT_SIZE, c) the character inquiry function LEN, d) the kind inquiry function KIND, e) the pointer nullification function NULL, or f) a numeric inquiry function (13.10.8) and where each argument of the function is a) an initialization expression, or b) a variable whose properties inquired about are not: 1) assumed 2) defined by an expression that is not an initialization expression or 3) definable by an ALLOCATE or POINTER assignment statement [78:23+] add If an initialization expression includes a reference to the NULL intrinsic function, the argument, if any, must be specified in a prior specification in the same . The prior specification may be to the left of the NULL function reference in the same statement. [79:11-15] replace with [replacement includes edit in 006, item 97] 9) A reference to an intrinsic function which is: a) an array inquiry function (13.10.15) other than ALLOCATED, b) the bit inquiry function BIT_SIZE, c) the character inquiry function LEN, d) the kind inquiry function KIND, e) the pointer nullification function NULL, or f) a numeric inquiry function (13.10.8) and where each argument of the function is a) a restricted expression, or b) a variable whose properties inquired about are not: 1) dependent on the upper bound of the last dimension of an assumed-size array 2) defined by an expression that is not a restricted expression or 3) definable by an ALLOCATE or POINTER assignment statement [92:17] Replace "If the target is a pointer that is disassociated," with "If the target is a pointer that is disassociated or a reference to the NULL intrinsic function," and delete "also". [92:28+] add PTR => NULL ( ) [187:32] change title to <<13.8.10 Pointer association status functions>> [187:33] The function -> The function NULL returns a disassociated pointer. The inquiry function [192:13] change title to <<13.10.20 Pointer association status functions>> [192:14] Association status or comparison -> Association status inquiry or comparison [192:15+] add NULL (MOLD) Returns disassociated pointer Optional MOLD [225:19+] add <<13.13.77a NULL (MOLD)>> <>. MOLD <>. Returns a disassociated pointer. <>. Transformational function. <>. MOLD must be a pointer and may be of any type. Its pointer association status may be undefined, disassociated, or associated. If its status is associated, the target need not be defined with a value. <>. Determined by context (7.1.4.1) if MOLD is not present; otherwise, the same as MOLD, and also the same rank as MOLD. <>. The result is a pointer with disassociated association status. <>. REAL, POINTER, DIMENSION(:) :: VEC => NULL( ) sets the initial association status of VEC to disassociated. [246:35] becomes -> is [246:35+] add (and reletter remaining items) (a) The pointer is explicitly initialized in a type declaration (5.1) (b) Default initialization is specified for the pointer in a type definition (4.4) [256:10+] add new glossary item <> (4.4) : If initialization is specified in a type definition, an object of the type will be automatically initialized when declared or allocated. Nonpointer components may be initialized with values by default; pointer components may be initially disassociated by default. Default initialization is not provided for objects of intrinsic type. [256:42+] add <> : Explicit initialization may be specified for objects of intrinsic or derived type in type declaration statements or DATA statements. An object of a derived type that specifies may not appear in a DATA statement. [268:2-3] replace with INTEGER :: VAL = 0 TYPE(CELL), POINTER :: NEXT_CELL => NULL( ) [268:5] replace with TYPE(CELL), TARGET :: HEAD ! Automatically initialized [268:9] delete [268:15] delete Add to new Rationale Section: <<4.4 Derived types>> The prime motivation for adding a means to specify default initialization for objects of derived type is a need to eliminate situations in which dynamic memory becomes unavailable. This can occur in applications that manipulate objects of derived type with pointer components. Most memory leakage can be avoided if it is possible to specify that pointers be created with an initial status of disassociated. In order to allow the initial status of a pointer to be specified as disassociated in declarations, structure constructors, or type definitions, a new intrinsic function, NULL, with a single optional argument is provided. When the intrinsic function NULL appears in a type definition, the optional argument must not appear. When the intrinsic function NULL appears elsewhere, the argument is optional if the type, type parameters, and rank of the required disassociated pointer are apparent from the context. If this is not the case, a pointer object name must appear as the argument to indicate the type, type parameters, and rank of the required disassociated pointer. The argument acts as a mold. Such an argument may be necessary when a disassociated pointer is used as an actual argument in a generic procedure reference (7.1.4.1). An example of the initialization of a pointer as disassociated is: REAL, POINTER, DIMENSION (:) :: VEC => NULL( ) The following code fragment illustrates the memory leakage problem: MODULE CHARACTER_VARYING TYPE VARYING PRIVATE CHARACTER, POINTER :: CHARS(:) END TYPE VARYING ! Generic interfaces for assignment, concatenation, conversion, etc. CONTAINS SUBROUTINE VS_ASS_VS (VAR, EXPR) ! Assignment subroutine TYPE(VARYING) VAR, EXPR ALLOCATE (VAR % CHARS (1:SIZE(EXPR % CHARS))) VAR % CHARS = EXPR % CHARS END SUBROUTINE VS_ASS_VS ... END MODULE CHARACTER_VARYING MODULE VOCABULARY USE CHARACTER_VARYING PRIVATE TYPE(VARYING), PUBLIC :: WORDS (5000) ! Words in random order WORDS(1) = "attack" WORDS(2) = "at" WORDS(3) = "dawn" ... END MODULE VOCABULARY PROGRAM DECIPHER_MESSAGE USE CHARACTER_VARYING USE VOCABULARY ! Get WORDS TYPE(VARYING) MSG ! To collect message INTEGER MSG_WDS(100) ! Indices to WORDS INTEGER N, I ! No. of indices, loop index READ *, N, MSG_WDS MSG = "" ! Set to empty string DO I = 1, N MSG = MSG // WORDS(MSG_WDS(I)) ! Collect message END DO ... END PROGRAM DECIPHER_MESSAGE Suppose the string of indices read into MSG_WDS is 125, 2, 3005, 7, 333, 4002, 66, 222, 2, 6, 901 and at the conclusion of the loop, MSG contains "meeting at five on morning of may eighteen at abandoned mine". Dynamic memory will contain the following: meeting meeting at meeting at five meeting at five on meeting at five on morning . . . Because the pointer VAR % CHARS in the subroutine VS_ASS_VS may be undefined, it cannot be tested for association so that space no longer needed can be released. In applications manipulating objects of derived type with pointer components, most memory leakage can be avoided if initialization of pointers in the derived type can be specified in the type definition. Objects of the type can then be initialized automatically when created by either declaration or allocation. If the module CHARACTER_VARYING can be specified as: MODULE CHARACTER_VARYING TYPE VARYING PRIVATE CHARACTER, POINTER :: CHARS(:) => NULL( ) END TYPE VARYING ! Generic interfaces for assignment, concatenation, conversion, etc. CONTAINS SUBROUTINE VS_ASS_VS (VAR, EXPR) ! Assignment subroutine TYPE(VARYING) VAR, EXPR IF (ASSOCIATED (VAR % CHARS)) DEALLOCATE (VAR % CHARS) ALLOCATE (VAR % CHARS (1:SIZE(EXPR % CHARS))) VAR % CHARS = EXPR % CHARS END SUBROUTINE VS_ASS_VS ... END MODULE CHARACTER_VARYING Then at the conclusion of the loop, dynamic memory would not be needlessly cluttered. The previous standard allowed pointer assignment to occur when an object of a type containing a pointer is defined with a structure constructor; see the example in section 4.4.4 [37:17-26]. It is only a small extension to allow pointer nullification in derived-type constant expressions used for named constants or data initialization. For example: TYPE LINK REAL :: VALUE TYPE(LINK), POINTER :: NEXT END TYPE LINK TYPE(LINK) :: HEAD = LINK(0.0, NULL( )) TYPE(LINK), PARAMETER :: DEFAULT = LINK(0.0, NULL()) TYPE(LINK) :: END_OF_CHAIN = DEFAULT This language extension does not completely solve the memory leakage problem; for that, an automatic destructor is needed that would be invoked for local pointers and structures with pointer components when the procedure in which they are created terminates. Such a facility is not included in this standard; it could be provided automatically by a processor that strove to conserve dynamic memory. History: WG5-N930 Resolution of the Berchtesgaden WG5 Meeting WG5-N932 Requirement for the Initialization of Pointers and Objects X3J3/93-207 Pointer and Derived Type Initialization X3J3/93-259 Proposal for Object Initialization X3J3/94-031r2 Proposal for Object Initialization - moved to X3J3/009 with status "X3J3 consideration in progress" by unanimous consent X3J3/94-___ Text for X3J3/009 re Object Initialization (B1) ---------------------------------------------------------------------------- Number: 007 Requirement: Language evolution (B9 Item B4.2) Status: Approved by X3J3 Text of proposal not available. X3J3 vote at meeting 128 approved the list of deleted features as in paper 94-047r1, amended to remove ENTRY from the list. ----------------------------------------------------------------------------