variable = expr
The execution of the assignment shall have the same effect as if the evaluation of expr and the evaluation of all expressions in variable occurred before any portion of the variable is defined by the assignment.
The side-effect in expr (the update to X) occurs before the LHS (X, again) is assigned to, with the result. I would say that it is conforming, after all.
Isn’t it wonderful?
A different beast altogether. Here, I is not the variable in an assignment, it is one of the expressions in the statement, and the same rule applies as before, as if it were an expression on the RHS. It must not be modified during execution of F.
I think here is the difference. In “variable=expr”, the example given in the standard in 10.2.1.3 is
string(2:5) = string(1:4)
The meaning of that assignment is that the RHS is evaluated (and stored somewhere temporarily if necessary) before the assignment to the LHS. That is not a side effect, it is the purpose of the assignment statement.
On the other hand, the earlier text in 10.1.4 is specifically about side effects, about entities in the statement becoming undefined or redefined by function evaluation.
So maybe the difference in the two cases is that in the first case, everything is obvious and clear to a compiler (or to a human), while in the second case it is something that is possibly hidden, so the compiler cannot be expected to always do what the programmer intended.
As I said before, the standard could have defined the behavior of X=B(X) when X is defined/undefined by B(). In fact, that would have been an excellent example to include in the standard text itself if it had wanted to do that. But I don’t think it did that, I think the “associated entities” part of 10.1.4 is intended to cover that case. I.E. it is the programmer’s responsibility to avoid that situation. One way to do so would be
T = B(X)
X = T
Of course, I’m not 100% sure about any of this. This situation could have been explained more clearly I think.
There is only one expression to be evaluated in this statement. We are told the assignment to X is the last thing that happens. Now that I read
if a function reference causes definition or undefinition of an actual argument of the function, that argument or any associated entities shall not appear elsewhere in the same statement.
again, I see that it is more strict that it needs to be. So, I agree with you, now, that although it could be made to make sense, it is currently prohibited. Some “appearances” are innocuous. For instance, one modification might be
shall not appear elsewhere in the same statement, except as the variable in an assignment statement.
Taken strictly, X=B(X) is disallowed because X certainly appears. Taken with my added clause, it would be allowed. I believe it was intended to be allowed, as the sequencing would be clear.
On the other hand,
X(INT(X(1))) = B(X)
where B returns scalar and modifies its dummy and X has dimension (2), would be disallowed because the indexing on the LHS means there are two expressions to be evaluated, and X, (a name), is not the variable in the assignment.
X(SIZE(X,DIM=1)) = B(X)
could also be allowed, under a further extension, because the value of X is not required in this case. Perhaps restricted expressions can be used here.
Ok, this reminds me of another situation that I have always been uncertain about. Lets say that X(:) is an array, and that the function B() modifies elements X(2:) but leaves X(1) unmodified. Then if INT(X(1))==1, we would have the situation where X(1) is referenced on the LHS, and X(1) is unmodified on the RHS. Would that statement be legal?
This is really a question about what an “associated entity” is within the standard. In this case, the question is whether the entire array X(:) is an entity, or whether it is the individual elements that are each an entity. If the former, then I think such a statement would not be legal, but if the latter, then the statement might or might not be legal, depending on which elements are referenced and which are modified.
This situation actually arises fairly often in practice. Say a function modifies the even elements of an array, then is it legal to assign the function value to an odd element of the array. Or if the array is rank 2, can the function modify elements of one row and assign the results to another row? This type of statement also occurs within finite state machines, where the states are defined in arrays, and statements use functions to modify the input state to produce an output state.
Sometimes the Standard talks about names (as in “shall not appear”) and sometimes about variables/entities (that “become defined or undefined”). X is a name and a variable, X(1) is not a name but is an entity (a function result or a variable). If function result B(X) is present in the statement, and B modifies even part of its argument, we have established that X must not appear, even in cases where it looks harmless. In this case, we can rewrite what we intend by making more statements.
Both the entire array and each element or section of it are entities.
Some restrictions, for instance the one that is usually called “no-aliasing”, are only pragmatic. Aliasing of actual arguments, per se, is not forbidden, as long as the compiler can assume that changes through name A cannot affect referenced values behind name B.
Your even/odd example would not have an interpretation in the Standard, because we established that the restriction is not of the pragmatic kind, but of the blanket kind (“appears”).
But a CALL foo(X=A,Y=A), where odd elements of dummy Xare only read and only even elements ofY are written, would be fine.No, no, no, I made a mistake.
In X = B(X) the function reference does not cause definition or undefinition of an actual argument of the function. The assignment does. Therefore IMHO the sentence
if a function reference causes definition or undefinition of an actual argument of the function, that argument or any associated entities shall not appear elsewhere in the same statement.
In this part of the discussion, we are assuming that the function B(X) does modify its arguments, as that is what is discussed in section 10.1.4 of the f2023 standard. In other parts of the discussion, that was not the case.
What is it exactly that makes this legal? If the argument association were copy-in/copy-out for both arguments, then the result afterwards would depend on which of the copy-out arrays was copied last.
On the other hand, something like CALL FOO(X=(A),Y=A) would be legal since this tells the compiler to not do the copy-out on dummy argument X. Or if argument association is by address, then Y gets the actual argument A while X would get the address of a copy of A.
What if FOO() modifies the odd elements of X(:) and it modifies the even elements of Y(:)? Is that legal, or is there any way to make it legal? I think not, but I’m not certain.
Unless we hit one of the exceptions, this would not have an interpretation in the Standard, because action that modifies a subobject of A (the second element, say), which is a subobject of X, is taken through reference to Y.
One way of hitting the exceptions is to have X declared as assumed-shape, and TARGET, and not INTENT(IN), and not CONTIGUOUS, and have A be a TARGET (which together requires an explicit interface for the call).