ISO/IEC JTC1/SC22/WG5 N2064 PROPOSALS FOR REVISION OF FORTRAN 2008 SUBMITTED TO WG5, AUGUST 2015 Van Snyder, Bill Long, Malcolm Cohen, Dan Nagle, and Daniel Chen We wish to submit the following proposals for the revision of Fortran 2008 for consideration by WG5. These proposals were approved by the US TAG at meeting 206 in February, 2015. 1. Purity enhancement Van Snyder (see J3/14-237r2) Reference: J3/14-007r2 1. Discussion ============= (1) There appears to be no reason that the procedures in ISO_C_Binding, other than C_F_POINTER, are not pure. That they are impure can cause cascades of impurity. (2) What's wrong with a VALUE argument of a pure function appearing in the contexts prohibited by C1296? That it is prohibited seems to be collateral damage caused by prohibiting pointer argument modification; prohibiting VALUE serves no productive purpose. 2. Proposal =========== (1) Specify that procedures in ISO_C_Binding, other than C_F_POINTER, are pure. The reason for not making C_F_POINTER pure is that we would like to constrain against doing things with FPTR, in the same way we do for pointers, but the best we can do is prohibit them. One goal of the description of PURE procedures was to constrain against all impure behavior. (2) Permit modification of VALUE arguments in a pure function. 3. Edits ======== [xviii] Under "Intrinsic procedures and modules," append a sentence "All standard procedures in the ISO_C_BINDING intrinsic module, other than C_F_POINTER, are now pure." [xviii] Under "Program units and procedures", append a sentence "A dummy argument of a PURE function is permitted in a variable definition context, and other contexts, if it has the VALUE attribute." [318:23 C1296] Before the first "dummy argument" insert "pointer". [318:34 C1296(6)] Delete "or" [318:35 C1296(7)] Replace the full stop with ", or" [318:35+ C1296(7)+] Insert a list item " (8) as the actual argument to the function C_LOC from the ISO_C_BINDING intrinsic module. [444:19 15.2.3.1p1] Append a sentence: "The C_F_POINTER subroutine is impure; all other procedures in the module are pure." --------------------------------------------------------------------------- 2. Command line related intrinsic procedures Bill Long (see J3/14-264r1) Discussion ---------- The restriction in F2008 13.5 at 329:1-3 reads "The effects of calling COMMAND_ARGUMENT_COUNT, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, and GET_ENVIRONMENT_VARIABLE on any image other than image 1 are processor dependent." With the introduction of teams, the ambiguity would apply to images other than image 1 of the initial team, leaving some teams with no access at all. Additionally, the limitations for COMMAND_ARGUMENT_COUNT, GET_COMMAND, and GET_COMMAND_ARGUMENT contradict the current the descriptions of the routines. Edits to 14-007r2: [329:1-2] Delete "COMMAND_ARGUMENT_COUNT,", "GET_COMMAND,", and "GET_COMMAND_ARGUMENT,". --------------------------------------------------------------------------- 3. Specification expressions Van Snyder & Malcolm Cohen (see J3/15-101r2) 1. Introduction =============== The question whether such things as integer :: B = bit_size(B) are permitted or prohibited has recently recurred on comp_lang_fortran. Interp F95/000090 included the above example, and several others, including character :: C(20)*(size(C,1)) The answer to the interp was that both of these declarations (and several additional examples) were not intended to be conforming. This resulted in 7.1.11p8, which says, in part "The prior specification may be to the left of the specification inquiry in the same statement, but shall not be within the same ..." which is somewhat ambiguous: It is reasonable that the extents of an array might not be established before before a is examined, as in the second example, but certainly one knows the type and type parameters before processing any in the statement. I.e., is the bit size specified by the or the ? 2. Specification ================ Allow any constant properties of an object to be referenced within an in its . This would allow such things as integer :: B = bit_size(B) real :: E = sqrt(sqrt(epsilon(E))) integer :: Iota(10) = [ ( i, i = 1, size(iota,1) ) ] 3. Syntax ========= The proposal is germane to R503 and R505 , but no syntax rules are changed. 4. Edits ======== [xviii] Introduction, p2, add new bullet at the beginning of the list "- Data declaration Constant properties of an object declared in its can be used in its .". [156:30] 7.1.12 Constant expression, p2 Append to the end of the sentence: "unless the specification inquiry appears within an .". --------------------------------------------------------------------------- 4. Allow mixed kind in SIGN intrinsic Van Snyder & Dan Nagle (see J3/15-108r2) Reference: 15-007 1. Introduction =============== In the SIGN function, the only interesting part of the value of the second argument is its sign. It therefore doesn't seem necessary that the arguments are required to have the same kind. 2. Proposal =========== The arguments of SIGN are not required to have the same kind. 3. Draft Edits ============== [Introduction, under "Intrinsic Functions"] Insert list item "-- The B argument to the SIGN function is not required to have the same kind type parameter as the A argument. " {remove the offending restriction} [396:15 13.7.158p3] Remove "and kind type parameter". --------------------------------------------------------------------------- 5. POINTER arguments to SAME_TYPE_AS Van Snyder (see J3/15-111r1) Reference: 15-007 1. Introduction =============== The dynamic type of a nonpolymorphic pointer is always well defined. Therefore the requirement that a pointer argument to SAME_TYPE_AS shall not have undefined association status is not necessary for nonpolymorphic arguments. The following works: type(t), pointer :: P class(t), intent(in) :: A nullify ( p ) if ( same_type_as ( A, P ) ) ... The following does not work (for no good reason): type(t), pointer :: P class(t), intent(in) :: A ! nullify ( p ) if ( same_type_as ( A, P ) ) ... The same observation and argument apply to EXTENDS_TYPE_OF. 2. Requirements =============== Allow a nonpolymorphic argument to EXTENDS_TYPE_OF or SAME_TYPE_AS to have undefined pointer association status. 3. Syntax ========= No change to existing syntax. 4. Edits ======== [Introduction, under "Intrinsic Functions"] Insert list item "-- Nonpolymorphic pointer arguments to EXTENDS_TYPE_OF or SAME_TYPE_AS are not required to have defined pointer association status." [353:25,27 13.7.61p3] Insert "polymorphic" before "pointer" twice. [391:32,34 13.7.147p3] Insert "polymorphic" before "pointer" twice. --------------------------------------------------------------------------- 6. SELECT RANK Malcolm Cohen (see J3/15-142r2) 1. Introduction This paper contains formal requirements, specifications, and syntax for a SELECT RANK construct as recently discussed on the J3 mailing list. 2. Formal requirements That it be possible in Fortran, without using any C wrapper, to operate on an assumed-rank array variable. In particular, it shall be possible to reference and define elements of the array. Requiring additional "glue" code would be acceptable when processing an assumed-rank array that is eventually associated with an assumed-size array. 3. Specifications There will be a construct to select a block based on the rank of an assumed-rank array that is not associated with an assumed-size array: - within each such block there will be an associate-name of that rank associated with the selector, with the bounds of the selector; - within each such block, it shall be possible to use whole array and array section notation safely, In the case of being associated with an assumed-size array, there will be a single special block that will catch all ranks of assumed-size, and in that block the associate-name will have shape (1:*); this can be reshaped using sequence association. There will be a "default" case, this will be a single special block will catch all other non-matching ranks, and in that block the associate-name is assumed-rank. 4. Syntax select-rank-construct ::= select-rank-stmt [ select-rank-case ]... end-select-stmt select-rank-stmt ::= SELECT RANK ( [ associate-name => ] selector ) Constraint: (select-rank-stmt) selector shall be the name of an assumed-rank array. select-rank-case ::= rank-case-stmt block rank-case-stmt ::= RANK ( int-constant-expr ) | RANK ( * ) | RANK DEFAULT Constraint: int-constant-expr shall be non-negative and less than or equal to the maximum rank supported by the processor. 5. Example SUBROUTINE process(x) REAL x(..) ! SELECT RANK(y=>x) RANK (0) y = 0 RANK (1) y(::2) = 1 RANK (2) y(:,2) = 2 RANK (*) Do i=1,100 If (y(i)==0) Exit y(i) = -y(i) End Do RANK DEFAULT Print *, 'I did not expect rank', RANK(y), 'shape', SHAPE(y) END SELECT 6. Assumed-size handling rationale and example Because an assumed-size object starts out with sequence association, rank is more of a movable feast with assumed-size than it is with other classes of arrays, like assumed-shape, allocatables, and pointers. For all other array types, it is best to get the shape as if it were deferred shape, then we get all the usual whole array operations. If the assumed-size array is wanted with any other rank or bounds than the default of (1:*), sequence association can be used again... for example SELECT RANK (y => x) RANK (*) IF (RANK(x)==2) THEN ! Special code for the rank two case. CALL sequence_assoc_2(y, LBOUND(x,1), UBOUND(x,1), LBOUND(x,2)) ELSE DO i=1,2**20 IF (y(i)==0) EXIT y(i) = -y(i) END DO END IF ... SUBROUTINE sequence_assoc_2(a, lb1, ub1, lb2) REAL a(lb1:ub1,lb2:*) ... do whatever you like with assumed-size array A. 7. Pointer and allocatable Assumed-rank pointer and allocatable variables are permitted, but these can only be allocated and deallocated from C (except via the ultimate non-assumed-rank pointer/allocatable). It would be useful to permit this for assumed-rank inside SELECT RANK; this requires the associate-name attribute rules to be slightly different for SELECT RANK. 8. Edits to 15-007 [xviii] Introduction, Data usage and computation, add new feature "The SELECT RANK construct provides structured access to the elements of an assumed-rank array.". [31:25-] 2.1 High level syntax, R213 executable-construct, Before "<> " Insert "<> ". {Add the construct to the high-level syntax.} [100:4] 5.5.8.5 Assumed-size array, p1, After "effective argument", insert ", or the associate name of a RANK (*) block in a SELECT RANK construct". {New class of assumed-size array.} [101:8] 5.5.8.7 Assumed-rank entity, p1 After "effective argument" insert ", or the associate name of a RANK DEFAULT block in a SELECT RANK construct". {New class of assumed-rank entity.} [101:15-16] Same subclause, C539 "or the first" -> "the first", After "inquiry function" insert ", or the selector of a SELECT RANK statement". {Allow assumed-rank object in SELECT RANK.} [171:10+] 8.1.1 Blocks, p1, bullet list, After "SELECT CASE construct;" Insert new bullet "SELECT RANK construct;". {Add to list of constructs containing blocks.} [173:1] 8.1.3.3 Other attributes of associate names, p1, Before "cobounds" insert paragraph break and change "The" to "Within an ASSOCIATE, SELECT RANK, or SELECT TYPE construct, the". {We don't want to do the rank stuff, but we want to do everything else.} [185:1-] Immediately before 8.1.9 SELECT TYPE construct, insert new subclause " 8.1.8a SELECT RANK construct 8.1.8a.1 Purpose and form of the SELECT RANK construct The SELECT RANK construct selects for execution at most one of its constituent blocks. The selection is based on the rank of an assumed-rank variable. A name is associated with the variable (16.4, 16.5.1.6), in the same way as for the ASSOCIATE construct. R839a <> [ ]... R839b <> [ select-construct-name : ] SELECT RANK ( [ associate-name => ] selector ) C834a (R839b) The in a shall be the name of an assumed-rank array. R839c <> RANK ( ) [ ] <> RANK ( * ) [ ] <> RANK DEFAULT [ ] C834b (R839c) A in a shall be non-negative and less than or equal to the maximum rank supported by the processor. C834c (R839a) For a given , the same rank value shall not be specified in more than one . C834d (R839a) For a given , there shall be at most one RANK ( * ) and at most one RANK DEFAULT . C834e (R839a) If appears on a the corresponding shall specify the same . C834f A SELECT RANK construct shall not have a that is RANK ( * ) if the selector has the ALLOCATABLE or POINTER attribute. R839d <> END SELECT [ ] C834g If the of a specifies a , the corresponding shall specify the same . If the of a does not specify a , the corresponding shall not specify a . The associate name of a SELECT RANK construct is the if specified; otherwise it is the name that constitutes the selector. 8.1.8a.2 Execution of the SELECT RANK construct A SELECT RANK construct selects at most one block to be executed. During execution of that block, the associate name identifies an entity which is associated (16.5.1.6) with the selector. A RANK ( * ) statement matches the selector if the selector is argument associated with an assumed-size array. A RANK ( ) statement matches the selector if the selector has that rank and is not argument associated with an assumed-size array. A RANK DEFAULT statement matches the selector if no other of the construct matches the selector. If a matches the selector, the block following that statement is executed; otherwise, control is transferred to the . It is permissible to branch to an only from within its SELECT RANK construct. 8.1.8a.3 Attribute of a SELECT RANK associate name Within the block following a RANK DEFAULT statement, the associating entity (16.5.5) is assumed-rank and has exactly the same attributes as the selector. Within the block following a RANK ( * ) statement, the associating entity has rank 1 and is assumed-size, as if it were declared with DIMENSION(1:*). Within the block following a RANK ( ) statement, the associating entity has the specified rank; the lower bound of each dimension is the result of the intrinsic function LBOUND (13.7.91) applied to the corresponding dimension of the selector, and the upper bound of each dimension is the result of the intrinsic function UBOUND (13.7.176) applied to the corresponding dimension of the selector. If the selector has the ALLOCATABLE attribute, the associating entity has that attribute. If the selector has the POINTER attribute, the associating entity has that attribute (and not the TARGET attribute). The other attributes of the associating entity are described in 8.1.3.3. 8.1.8a.4 Examples of the SELECT RANK construct This example shows how to use a SELECT RANK construct to process scalars and rank-2 arrays; anything else will be rejected as an error. SUBROUTINE process(x) REAL x(..) ! SELECT RANK(x) RANK (0) x = 0 RANK (2) IF (SIZE(x,2)>=2) x(:,2) = 2 RANK DEFAULT Print *, 'I did not expect rank', RANK(y), 'shape', SHAPE(y) ERROR STOP 'process bad arg' END SELECT The following example shows how to process assumed-size arrays, including how to use sequence association if you want to do multi-dimensional processing of an assumed-size array. SELECT RANK (y => x) RANK (*) IF (RANK(x)==2) THEN ! Special code for the rank two case. CALL sequence_assoc_2(y, LBOUND(x,1), UBOUND(x,1), LBOUND(x,2)) ELSE ! We just do all the other ranks in array element order. i = 1 DO IF (y(i)==0) Exit y(i) = -y(i) i = i + 1 END DO END IF END SELECT ... CONTAINS ... SUBROUTINE sequence_assoc_2(a, lb1, ub1, lb2) REAL a(lb1:ub1,lb2:*) j = lb2 outer: DO DO i=lb1,ub1 IF (a(i,j)==0) EXIT outer a(i,j) = a(i,j)**2 END DO j = j + 1 IF (ANY(a(:,j))==0) EXIT j = j + 1 END DO outer END SUBROUTINE ". {The new construct.} [188:21] 8.2.1 Branch concepts, p1, Between "" and "" insert ", ". {Allow branching to SELECT RANK and its END SELECT.} [301:18] 12.5.2.5 Allocatable and pointer dummy variables, p3, In the first sentence, beginning "The rank", after "dummy argument" insert ", unless the dummy argument is assumed-rank". {This is repairing a bug in assumed-rank.} [305:30] 12.5.2.12 Argument presence and restrictions..., p3, item (10), "SELECT TYPE or ASSOCIATE" ->"ASSOCIATE, SELECT RANK, or SELECT TYPE". {Do not allow rank selection on absent dummies.} --------------------------------------------------------------------------- 7. Locality clauses in DO CONCURRENT Daniel Chen & Malcolm Cohen (see J3/15-150r2) Reference: 15-007 0. Introductory Introduction This revision of the paper has revised syntax, and has edits. Some of the "New rules" have been slightly modified; in particular, rules 7 and 8 are withdrawn for the time being due to semantic difficulties, and there is a new rule 11. 1. Introduction =============== The intent of the restrictions listed in (8.1.6.5) is to allow a processor to parallelize the code inside a DO CONCURRENT construct without causing any race condition. However, there are issues as illustrated in the following cases: Case 1: REAL X X = 1.0 DO CONCURRENT(...) IF (condition) THEN X = something ... ! code using X END IF ... ! code that doesn't use X END DO In case 1, a processor cannot localise X, because if "condition" is true exactly once, the assignment affects the "outer" X. OTOH, if condition is true more than once, a processor cannot execute in parallel without localising X. Case 2: REAL X X = 1.0 DO CONCURRENT(...) CALL sub(X) ... ! code referencing X END DO In case 2, a processor is not able to detect 1. if X is defined (or becomes undefined) in sub, and 2. how many iterations that defines or undefines X as the condition could be in sub. Both Case 1 and 2 are standard conforming. It is understood that users can use a BLOCK construct to localize variables, but a processor still need to be able to handle all the standard conforming code when the BLOCK construct is not used. In order to still be able to parallelize code such as in Case 1 and 2, a processor needs to always localize x and copy-in the initial value and also copy-out the value at every possible point X could be defined or become undefined. The performance impact described at the above is not the intent of the standard, a proposal is made to eliminate the indetermination. 2. Proposal =========== Since a processor is not able to determine if a variable can be referenced by all iterations without causing race condition, it would be critical to provide a mechanism to allow users to explicitly speciify their intent of how a variable is expected to be used. A new syntax as follows is introduced to achieve that: change loop-control to have "concurrent-locality" after "concurrent-header", thus only affecting the DO CONCURRENT statement. R8nn concurrent-locality is [ locality-spec ]... R7xx locality-spec is LOCAL ( variable-name-list ) or LOCAL_INIT ( variable-name-list ) or SHARED ( variable-name-list ) or DEFAULT ( NONE ) New rules: 1. Variables appearing in the variable-name-list of a LOCAL_INIT specifier shall be previously defined. 2. When parallelization is enabled, a local copy of a variable that appears in the variable-name-list of the LOCAL specifier is created for each iteration. Such a local copy is initially undefined. 3. When parallelization is enabled, a local copy of a variable that appears in the variable-name-list of the LOCAL_INIT specifier is created for each iteration. Such a local copy is initialized to the value of the corresponding variable specified by the LOCAL_INIT specifier. 4. Variables that appear in the variable-name-list of the SHARED specifier are common to all iterations; a shared variable shall not be defined or become undefined in more than one iteration. A shared variable that is defined or becomes undefined in one iteration shall not be referenced in any other iteration. In the case of arrays, these requirements apply to each element individually. In the case of derived types, these requirements apply to the ultimate components. 5. If DEFAULT (NONE) appears, all variables referenced or defined within the loop, other than the loop iteration variables, shall have their locality explicitly declared. 6. A variable can only appear in at most one of the LOCAL, LOCAL_INIT, or SHARED specifiers. 9. A LOCAL or LOCAL_INIT variable with the ALLOCATABLE attribute will be automatically deallocated at the end of each iteration if it is allocated. 10. An assumed-size array shall not appear in LOCAL or LOCAL_INIT. 11. A DO CONCURRENT index cannot appear in a locality-spec for that loop. It seems to be ok to let it appear in a locality-spec for an outer DO CONCURRENT when there are nested DO CONCURRENT constructs though. Removed (was new) rules: 7. The name of an array whose subscripts involve index-names of a DO CONCURRENT construct shall not appear in the variable-name-list of the LOCAL or LOCAL_INIT specifier. Because: there is no such restriction in the current text. This would be quite difficult to state accurately, and is broken (not just useless) if stated inaccurately. Of course it's possible that no-one actually implements the current text, which can localise the whole array in some cases... 8. It is recommended that a scalar variable or an array whose subscripts do not involve index-names of a DO CONCURRENT construct that does not appear in any locality-spec not be defined or become undefined in any iteration. Because: There is some controversy over this recommendation, and it is difficult to state accurately. We *could* just do scalars... Note: There are some difficulties with interpreting such things as references to A(MOD(I,2)+1) in a DO CONCURRENT, it clearly depends on the index I but also seems likely that multiple iterations will want their own copies and therefore they should be localisable. If we can work this out, we could reconsider reinstating these rules (with appropriate modifications). 3. Examples =========== IMPLICIT NONE INTEGER :: N = 10 REAL :: X, Y, Z, R X = 1.0 Y = 2.0 Z = 3.0 R = 4.0 DO CONCURRENT (INTEGER :: I = 1 : N) LOCAL (X, R) LOCAL_INIT (Y) SHARED (Z) X = I + 1.0 !! X HAS VALUE OF I + 1.0 R = Y !! R HAS VALUE OF 2.0 IF (I == 1) THEN Z = 4.0 END IF END DO PRINT*, X !! 1.0 PRINT*, Y !! 2.0 PRINT*, Z !! 4.0 PRINT*, R !! 4.0 END 4. Edits to 15-007 ================== [176:23] 8.1.6.2 Form of the DO construct, R817 loop-control, After "" Insert "" [176:29+] Same subclause, after R822 concurrent-spec, insert new BNF "R822a <> [ ]... R822b <> LOCAL ( ) <> LOCAL_INIT ( ) <> SHARED ( ) <> DEFAULT ( NONE ) [176:34+] Same subclause, before R823 end-do, insert constraints "C8xx A in a shall be the name of a variable in the scoping unit that is not an assumed-size array and is not the same as an in the of the same DO CONCURRENT statement. C8xx The name of a variable shall not appear in more than one , or more than once in a , in a . C8xx A variable that is referenced by the of a or by any or in that shall not appear in a LOCAL in the same DO CONCURRENT statement. C8xx If the DEFAULT ( NONE ) appears in a DO CONCURRENT statement, a variable that appears in the of the construct and is not an of that construct shall have its locality explicitly specified.". {Add new syntax and constraints.} [179:21] 8.1.6.5 Restrictions on DO CONCURRENT constructs, heading, rename to "Additional semantics for DO CONCURRENT constructs". [179:30] Same subclause, before p1, replace opening sentence with new paragraphs and opening as follows: "The locality of a variable that appears in a DO CONCURRENT construct is LOCAL, LOCAL_INIT, SHARED, or unspecified. A variable that has LOCAL or LOCAL_INIT locality is a construct entity with the same attributes as the variable with the same name outside the construct, and the outside variable is inaccessible by that name within the construct. At the beginning of execution of each iteration, - if a variable with LOCAL locality is allocatable it is unallocated, if it is a pointer it has undefined pointer association status, and otherwise it is undefined except for any subobjects that are default-initialized; - a variable with LOCAL_INIT locality has the allocation, pointer association, and definition status of the outside variable with that name; the outside variable shall not be an undefined pointer or a nonallocatable nonpointer variable that is undefined. When execution of that iteration completes, an allocatable variable with LOCAL or LOCAL_INIT locality will be automatically deallocated. If a variable has SHARED locality, appearances of the variable within the DO CONCURRENT construct refer to the variable in the scoping unit. If it is defined or becomes undefined during any iteration, it shall not be referenced, defined, or become undefined during any other iteration. If it is allocated or deallocated during an iteration it shall not have its allocation status, dynamic type, or a deferred type parameter value inquired about in any other iteration. If a variable has unspecified locality," [179:31] "A variable that is referenced in an iteration shall" -> "if it is referenced in an iteration it shall", [179:32] ". A variable that is" -> "\item if it is". [179:33] "iteration becomes" -> "iteration it becomes", "terminates." -> "terminates;". [179:34] "A pointer that" -> "if it is a pointer and", [179:35] "nullification, shall" -> "nullification, it shall", [179:36] ". A pointer that has its pointer association" -> "\item if it is a pointer whose pointer association is", [179:37] "iteration has" -> "iteration, it has", [179:38] "If an allocatable object" -> "if it is allocatable and", [179:39] ". An allocatable object that" ->"; \item if it is allocatable and", [179:41] "shall either" -> "it shall either". This makes the 179:31-41 text read: "If a variable has unspecified locality, - if it is referenced in an iteration it shall either be previously defined during that iteration, or shall not be defined or become undefined during any other iteration; - if it is defined or becomes undefined by more than one iteration it becomes undefined when the loop terminates; - if it is a pointer and is used in an iteration other than as the pointer in pointer assignment, allocation, or nullification, it shall either be previously pointer associated during that iteration or shall not have its pointer association changed during any iteration; - if it is a pointer and has its pointer association changed in more than one iteration, it has an association status of undefined when the construct terminates; - if it is allocatable and is allocated in more than one iteration, it shall have an allocation status of unallocated at the end of every iteration; - if it is allocatable and is referenced, defined, deallocated, or has its allocation status, dynamic type, or a deferred type parameter value inquired about, in any iteration, it shall either be previously allocated in that iteration or shall not be allocated or deallocated in any other iteration.". [180:1-3] De-itemise lines 1-2 and prepend to line 3, so that p2 now contains both the requirements on concurrent i/o. {Organisational improvement.} [181:0+lots] Between NOTES 8.15 and 8.16, which are just before 8.1.7 IF construct and statement, insert new NOTE "NOTE 8.15a The following code demonstrates the use of the LOCAL clause so that the X inside the DO CONCURRENT construct is a temporary variable, and won't affect the X outside the construct. X = 1.0 DO CONCURRENT (I=1:10) LOCAL(X) IF (A(I)>0) THEN X = SQRT(A(I)) A(I) = A(I) - X**2 END IF B(I) = B(I) - A(I) END DO PRINT *,X ! Always prints 1.0. ". {Add the simplest example.} ===END===