Will i=i++ be newly well-defined in C17?
Asked Answered
B

2

11

After stumbling across the question "Why are these constructs using pre and post-increment undefined behavior?" today I decided to grab the newest draft for the next C standard I could find and read more about it.
Shortly after I discovered the following paragraph in a C17 draft:

An expression is a sequence of operators and operands that specifies computation of a value, or that designates an object or a function, or that generates side effects, or that performs a combination thereof. The value computations of the operands of an operator are sequenced before the value computation of the result of the operator
Source: ISO/IEC 9899:2017, Section 6.5 §1 "Expressions" (link broken use web.archive.org)

Now I am a little confused. Doesn't that mean that i = i++ is defined behavior? I looked at another draft, C99 this time:

An expression is a sequence of operators and operands that specifies computation of a value, or that designates an object or a function, or that generates side effects, or that performs a combination thereof.
Source: ISO/IEC 9899:1999, Section 6.5 §1 "Expressions"

It's missing that very sentence!

Questions

  1. Did I misunderstand something?
  2. Are the answers outdated?
  3. Did I look at the wrong draft?

Note: this question is related, it's about C++ though.

Bascom answered 25/11, 2018 at 14:22 Comment(6)
@asynts: C++ is very different language. Identical syntax/grammar does not imply identical semantics.Clicker
Did you even read the C11 version of the standard? It's exactly the same text. VtC as the well know dupe.Clicker
@toohonestforthissite The OP seems well aware of that. The question here is mostly if C17 is in line with C++17, like C11 was in line with C++11.Radar
Reopening, as the question recognizes this is UB in older standards (and linked to the given dup) but is asking about the newest standard.Craniotomy
@dbush: There is only one standard and the question asks if the wording has changed which it apparently did not. In other words: OP asks why something has changed which did not change and let others do the comparison.Clicker
@asynts: The C standard versions since the second have a foreword which withdraws the previous version and lists important changes. In general, when talking about a document it should be studied well, at least the apparent parts. Anyway, you got the question repoened, so I'll leave here.Clicker
C
7

The passage you highlighted only says that the expressions i++ and i are evaluated before the evaluation of the full expression i = i++. It is still undefined behavior because i is being modified more than once in an expression without a sequence point.

That passage first appeared in C11, so there's no change from that version C17.

Craniotomy answered 25/11, 2018 at 14:27 Comment(2)
So the increment isn't tied to the evaluation?Bascom
@Bascom Correct. Any side effects on the right hand side are unsequenced with regard to the assignment.Craniotomy
R
2

The full story. In C99 we had this text for 6.5.16 the assignment operator:

The side effect of updating the stored value of the left operand shall occur between the previous and the next sequence point.

The order of evaluation of the operands is unspecified. If an attempt is made to modify the result of an assignment operator or to access it after the next sequence point, the behavior is undefined.

This was changed in C11 to:

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

This is just different (and worse) wording, the two versions behave the same - the key being the last sentence in the C11 part which still makes this undefined behavior, since evaluation of the left operand is still unsequenced in relation to the right operand. Value computation just refers to the individual operands.

C17 has identical text as C11. So the answer is: no, i = i++; is still undefined behavior in C17.


Just for reference, compare this with C++11 (5.17):

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

This is roughly the same text as C11, without the explicit "the evaluations of the operands are unsequenced". This was a flaw in C++11, it isn't clear if this would make certain expressions well-defined or not.

C++17 provides a clarification (8.5.18):

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand.

So in C++17, i=i++; is definitely well-defined. And as we can see, the wording is explicit, as opposed to "unsequenced" in C11/C17.

Radar answered 26/11, 2018 at 8:3 Comment(5)
In an assignment x=y;, I don't think the left-hand operand is evaluated at all. In a more complicated case like arr[i++]=y;, some sub-expressions within the left-hand operator will be evaluated [in a manner that is unsequenced with regard to the right-hand operand of =], but the lvalue expression as a whole will not be evaluated at all. I don't think that a statement that the evaluations of operands is unsequenced is relevant to an operand that is not evaluated.Devol
@Devol ...unless x is volatile qualified and a hardware flag register for some MCU peripheral, where reading it could cause flags to change. Then even reg = reg & mask; would have poorly-specified behavior (unlike the compount assignment equivalent, but that's another story). Similarly there is a[func()] = b[func()];, where func can have static variables, and so on.Radar
My point is that the lack of sequencing between operands of = would not, in and of itself, cause i=i++; to invoke UB if all side-effects associated with the evaluation of the right-hand side are sequenced before the actual assignment. By contrast, behavior would be undefined for a[i]=i++;, a[i++]=i;, or i+=i++; since in all three of those cases there are other operations on the left-hand side which precede the assignment.Devol
As for the scenario where the target is volatile, there are platforms with read-modify-write instructions where the instruction equivalent to foo|=1; would have semantics which differ from the sequence temp = foo; temp |= 1; foo = temp;, and I would expect that quality implementations for such platforms should stretch the freedom offered by "Implementation-Defined" aspects of volatile to select one of the two behaviors based how the expression is written even though the Standard does not anticipate cases where the forms would not be equivalent given a "simple" left-hand operand.Devol
BTW, I think the C Standard could be clearer if it coined a term "to address an lvalue", to mean "to identify the object identified thereby"--an action which would be the first step of evaluating an lvalue, assigning to it, or taking its address That would make clearer what is done to the left-hand operand of a[i++]=j;..Devol

© 2022 - 2025 — McMap. All rights reserved.