x=(x=5,11)

G

google

Gordon said:
Do you think this expression causes undefined behavior:

x = x = 11;
?

In this situation, I think it can, since there is no sequence point
and the two side effects on the same variable can occur SIMULTANEOUSLY.
That the two side effects are storing the same value is not relevant.
Yes, I do think it has undefined behaviour.

And rather than
x=(x=5,11);

Consider x=11; x^=(x=5,11);
Obviously this has undefined behaviour.

While it seems a bit bizarre, I know of nothing in the standard that
would prohibit a hardware implementation that needs to read a value
before storing a new value (an example might be hardware that can only
flip bits, not set/reset them) and so I expect this undefined behaviour
to extend to things like x=x=11; and x=(x=5,11);

And on this hardware, x=(x=5,11) wouldn't leave x=5 or x=11, it would
leave x with some undefined value.

This would also mean things like g=f(); where g is global and f()
modifies g would also have undefined behaviour.

Tim.
 
R

Richard Bos

"In simple assignment (=), the value of the right operand is
converted to the type of the assignment expression and replaces the
value stored in the object designated by the left operand."
Now how is it possible to obtain the value of the right operand and
convert it to the type of the assignment expression without evaluting
it before you store the result in x?

Clearly it's not possible to store the result before evaluating it,
but it's possible (indeed, easy!) to determine the value of (x=5,11)
before performing the assignment of 5 to x.[/QUOTE]

Possible, but not allowed. The assignment x=5 _must_ take place (in the
abstract machine) before the sequence point; the evaluation of 11, and
therefore also the final assignment of 11 to x, must take place after
the sequence point.

Richard
 
R

Richard Heathfield

Old Wolf said:
Well, what are you going to subtract 3 from if you have
not yet computed (5 + 6) ?

You could subtract it from 0 and hold it as a temporary value:

tmp = 0 - 3
x = 0 + 5
y = 0 + 6
r = 0
r += x
r += y
r += tmp
Richard Heathfield quoted:
the order of evaluation of subexpressions and the
order in which side effects take place are both unspecified.

Nevertheless, I think there are other constraints, although I
cannot find supporting text in the standard (I posted to
comp.std.c asking). Specifically, that you cannot evaluate
(ie. obtain the value of) an expression containing an operator,
until you have evaluated any applicable operands of that operator.

Oh, but you can - see above, where tmp represents a complete evaluation of
the subtrahend and the subtraction. No further explicit subtraction
operations are carried out, although there is of course an implicit
subtraction involved in adding -3 to r.
Another example:

x = getchar();

Does the user have to press a key before a value can be
stored in 'x'?

No. I'll let you think about that one. :)
 
R

Richard Tobin

Clearly it's not possible to store the result before evaluating it,
but it's possible (indeed, easy!) to determine the value of (x=5,11)
before performing the assignment of 5 to x.
[/QUOTE]
Possible, but not allowed. The assignment x=5 _must_ take place (in the
abstract machine) before the sequence point; the evaluation of 11, and
therefore also the final assignment of 11 to x, must take place after
the sequence point.

After checking the standard, I think you're right. The result must
be as if the x=5 assignment was performed before the evaluation of 11
and therefore before the other assignment.

-- Richard
 
O

Old Wolf

Gordon said:
Old Wolf's lemma does NOT rule out the possibility that evaluating
x (the first one in x=(x=5,11) ) occurs before, not after, the
sequence point forced by the comma operator.

Correct. But the evaluation of the left-hand operand of the
assignment operator does not cause the value of x to be
read or written, so it does not fall foul of the rule about UB
due to multiple reads/writes between sequence points.

I didn't mention this as it is a red herring compared to
the main issue.

However, this code would certainly be undefined, for the reason
you mention:

*p = (p = q, 11);

where p and q are valid pointers to int.
I'd prefer to see a statement proved that at the comma
operator sequence point, x=5's side effect must be complete
and x=11's side effect MUST NOT HAVE STARTED. But the
evaluation of x may have started before the sequence point.

The evaluation of an operator's operands, does not mean that
the evaluation of the operator has started. In fact, my lemma
is that the assignment operator cannot be evaluated until
its right-hand operand has been evaluated (although the left
hand one may be evaluated at any time).

Note that 'evaluating' the left hand operand of the assignment
operator means determining the adddress at which the value
will be stored (it doesn't mean reading or writing that value).
Do you think this expression causes undefined behavior:

x = x = 11;

Yes, x is modified twice between sequence points.
 
G

google

Old said:
Correct. But the evaluation of the left-hand operand of the
assignment operator does not cause the value of x to be
read or written, so it does not fall foul of the rule about UB
due to multiple reads/writes between sequence points.
What about writing to memory that requires erasing before the write can
take place?

Tim.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

The ordering provided by sequence points is manifestly partial.

f((a,b),(c,d));

Assuming a, b, c, d are all expressions with side-effects:

which is evaluated before the other, a or d?

Not claiming to be an expert but 6.5.2.2#10

"The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual call."

would suggest that the order of evaluation of the arguments is
undefined. Regarding the order of evaluation of a and b, the only thing
I can find that seems to apply would be 6.5.16#2

"The left operand of a comma operator is evaluated as a void expression;
there is a sequence point after its evaluation. *Then* the right operand
is evaluated; the result has its type and value.94) If an attempt is
made to modify the result of a comma operator or to access it after the
next sequence point, the behavior is undefined." (emphasis mine)

While it's not very clear I would suggest that the use of "Then" would
imply that the right operand is evaluated after the left.

Erik Wikström
 
G

Guest

What about writing to memory that requires erasing before the write can
take place?

Then this erasing is part of the write, and there may not be a sequence
point during a write (or during any "side effect", even).
 
E

ena8t8si

Jordan said:
Jordan said:
Jordan Abel wrote:
Jordan Abel wrote:
Is this defined or not? Some people in ##c are saying that it has to
result in x being set to 11, i'm saying it's undefined. Who's right?

x=(X=5,11)

My reading [but what do I know!]

X=paraen'ed-expression

So, paraen'ed-expression must be evaluated first

Why? The compiler doesn't need to emit code to evaluate it to know the value.

I think that the statement

char foo[9];
x=(x=sprintf(foo,"hello"),sprintf(foo," world!\n"));

could do things in this order:

x=8
x=5
set foo to "hello"
set foo to "world!\n"

You're falling into the trap of arguing what a compiler
might do rather than what a compiler is obliged to do
by the Standard.

The "as if" rule applies.

Yes, and the code you posted doesn't behave as if
it were executed by the abstract machine.

The behavior of the abstract machine is not defined by the standard in
this case.

I know that's your opinion. The problem is you haven't
supported your statements with reasoning or citations from
the standard. Until you do, I don't see any reason to give
your comments any further consideration.
 
E

ena8t8si

Tim said:
You seem to think that sequence points are the only thing
that affect the partial ordering of expression evaluation.
That's false. In the expression a + b - c, the evaluation
of + must precede the evaluation of - in the abstract
machine. And compiled code must behave as if
the abstract machine would behave.

So you think

x ^= y ^= x ^= y;

has defined behaviour? After all, just as +/- associates L to R, ^=
associates R to L.[/QUOTE]

Of course not. The order of evaluation of the operators
doesn't determine the order in which the side effects
take place. Evaluating an assignment operator causes
a store to take place, but the side effect of updating the
stored value may take place at any point before the
next sequence point (and after the assignment operator
has been evaluated).
 
E

ena8t8si

Richard said:
(e-mail address removed) said:


C99, 6.5(3): Except as specified later (for the function-call (), &&, ||,
?:, and comma operators), the order of evaluation of subexpressions and the
order in which side effects take place are both unspecified.

Right. The subexpressions of - may be evaluated in any order.
But the subexpressions must be evaluated before the minus
operator is evaluated.
In the expression a + b - c, the abstract machine is under no obligation to
evaluate + before - or vice versa.

It is, because the minus operator needs the values of its operands,
and those values are produced by evaluating those subexpressions.
Indeed, even a, b, and c themselves
might be evaluated in any order.

True but irrelevant to my comment.
 
G

google

Harald said:
Then this erasing is part of the write, and there may not be a sequence
point during a write (or during any "side effect", even).

My own view is that the pseudo-code

register = x;
erase_x;
evaluate_new_x(old_x = register);
write_x;

Is allowed by the standard.

In the example in the subject line, the "evaluate_new_x" will itself
contain an erase_x, write_x pair.


"all side effects of previous evaluations shall be complete and no side
effects of subsequent evaluations shall have taken place". I'm arguing
that the outer assignment is neither previous nor subsequent to the
evaluation of the comma operator, it is concurrent with it.


Unfortunately, I suspect it will take a DR to get this satisfied once
and for all. Fortunately, anybody who cares enough about C to be
prepared to argue whether this is, or isn't, defined would never write
code like this in practice anyway.


Incidentally, there is, I think, an appendix "Formal model of sequence
points" http://www.davros.org/c/wg14n822.txt
(http://www.davros.org/c/wg14papers.html - informative, not normative)
which I _think_ would agree that x=(x=5,11); is defined.

x=(X=5,11); /* x and X are the same, I'm just distinguishing them to
make the analysis clearer */

e1 = e2 => W(x) E(X=5,11) S1 { W(x)<S1, E(X=5,11)<S1, E(X=5,11)<W(x) }

e1 , e2 => W(x) E(X=5) S2 E(11) S1 { W(x)<S1, E(X=5)<S1, S2<S1, E(X=5)
< S2; E(X=5)<W(x), S2<W(x)}

e1 = e2 => W(x) W(X) E(5) S2 E(11) S1 { W(x)<S1, W(X)<S1, S2<S1,
W(X)<S2, W(X)<W(x) S2<W(x) }

The only valid ordering for these events given the constraints is:
W(X) S2 W(x) S1
which has a sequence point between the two writes.

(The bit I disagree with here is E(X=5,11)<W(x) => S2 < W(x). I think
this is stronger than the text of the standard requires)

Tim.
 
G

Guest

My own view is that the pseudo-code

register = x;
erase_x;
evaluate_new_x(old_x = register);
write_x;

Is allowed by the standard.

In the example in the subject line, the "evaluate_new_x" will itself
contain an erase_x, write_x pair.


"all side effects of previous evaluations shall be complete and no side
effects of subsequent evaluations shall have taken place". I'm arguing
that the outer assignment is neither previous nor subsequent to the
evaluation of the comma operator, it is concurrent with it.

There is no parallel execution in the abstract machine, is there? Or am
I misunderstanding you?
 
G

google

Harald said:
There is no parallel execution in the abstract machine, is there? Or am
I misunderstanding you?

Don't think there is parallel execution, no. But x=++x; according to
the same appendix has a strict ordering on writes but still has
undefined behaviour because there is no intervening sequence point
between the two writes.

There are 3 sequence points here, before the start of the expression,
the comma operator, and the end of the expression.
Call them S0, S1 and S2.

I'm saying one write must start after S0 and complete by S2, the other
write must start after S0 and complete by S1. For the outer write the
previous sequence point is S0 and the next sequence point is S2 even
though there must be a sequence point S1 between them.

What others are saying is that the sequence point S1 prohibits the
outer assignment starting before S1 because the write cannot complete
until after S1.

The standard is, IMO, ambiguous on this. for example, extracting from a
footnote:

*p++ = getchar();

it says that the actual increment of p "can occur at any time between
the previous sequence point and the next sequence point (the ;)".
However, if p++ is evaluated before the call to getchar then if
sequence points define a strict ordering then the next sequence point
is actually before the function call, not the ;

My view is that the ; is always the next sequence point for the p++
regardless of precisely when the getchar() call is made.

Tim.
 
J

Jordan Abel

Not claiming to be an expert but 6.5.2.2#10

"The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual call."

would suggest that the order of evaluation of the arguments is
undefined. Regarding the order of evaluation of a and b, the only thing
I can find that seems to apply would be 6.5.16#2

The fact that the standard says "there is a sequence point before the
actual call" and does NOT, for the = operator, say "there is a sequence
point before the actual assignment", is the distinction i'm trying to
point out. If it doesn't need to be said for the latter, why was it said
for the former?
"The left operand of a comma operator is evaluated as a void expression;
there is a sequence point after its evaluation. *Then* the right operand
is evaluated; the result has its type and value.94) If an attempt is
made to modify the result of a comma operator or to access it after the
next sequence point, the behavior is undefined." (emphasis mine)

While it's not very clear I would suggest that the use of "Then" would
imply that the right operand is evaluated after the left.

But that doesn't say ANYTHING about the assignment, which is _outside the
scope of the comma operator_ - just like the ordering of (a,b) with
respect to (c,d) is outside the scope of the two comma operators which are
in play there. Sequence points define a partial ordering. (any
"ordering" for which you can say "a is before b" but cannot say "a is
before (or not before) d" is partial.)
 
J

Jordan Abel

One of the more obvious things that I don't think the standard
states

If it doesn't state it, then I don't give a damn how "obvious" it is,
it's simply not a requirement of the standard. Especially since it
states it for SOME operators (notably, the function call operator) and
omits it for others.
 
G

Guest

Don't think there is parallel execution, no. But x=++x; according to
the same appendix has a strict ordering on writes but still has
undefined behaviour because there is no intervening sequence point
between the two writes.

That's UB even completely ignoring the as-if rule for precisely the
reason you state, so it is a significantly different example.

As for the rest of your message, good points.
 
T

Tim Woodall

Of course not. The order of evaluation of the operators
doesn't determine the order in which the side effects
take place. Evaluating an assignment operator causes
a store to take place, but the side effect of updating the
stored value may take place at any point before the
next sequence point (and after the assignment operator
has been evaluated).

Now we're getting somewhere.

"(and after the assignment operator has been evaluated)"

Where is this in the standard?

(a,b) + (c,d)

if an example implementation evaluates a then b then c then d then you
seem to be saying that the sequence point after c but before d means
that all the side effects of evaluating b will have completed.

This I disagree with.

It's hard to come up with a good example where this distinction matters.
Memory that requires an erase before write is the most realistic example
I can think of.

Now that I'm at home, it appears that that Formal method for determining
whether an expression has defined behaviour isn't in either C99 or C++
even as an informative appendix.

Tim.
 
O

Old Wolf

Tim said:
On 29 May 2006 06:32:10 -0700,


Now we're getting somewhere.

"(and after the assignment operator has been evaluated)"

Where is this in the standard?

It isn't. A side-effect is part of an expression evaluation.
It may occur before or after (or at the same time as) the
value of the expression is computed.

(The point I'm making else-thread is that the side-effect
"obviously" cannot occur before the right-hand operand
has been evaluated.)
(a,b) + (c,d)

if an example implementation evaluates a then b then c then d then you
seem to be saying that the sequence point after c but before d means
that all the side effects of evaluating b will have completed.

I don't see where he is saying that.
This I disagree with.

Naturally. a's side effects must complete before b is evaluated,
and c's side effects must complete before d is evaluated; there
is no other constraint.
 
O

Old Wolf

Richard said:
Old Wolf said:

You could subtract it from 0 and hold it as a temporary value:

tmp = 0 - 3
x = 0 + 5
y = 0 + 6
r = 0
r += x
r += y
r += tmp
Oh, but you can - see above, where tmp represents a complete
evaluation of the subtrahend and the subtraction.

In this example you have subtracted 3 from 0, and then added
the result to (5 + 6) later. In fact you have computed

(5 + 6) + (-3)

which was not the original expression.

So my question now becomes: what are you going to
add -3 to if you have not yet computed (5+6) ?

NB. This is difficult to discuss because of the as-if rule, and
it isn't even relevant to the original topic ( + behaves
differently to = ), so if you want to drop it then that's ok :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,139
Latest member
JamaalCald
Top