Multiple Assignment Evaluation Debate

R

Robin Haigh

Ben Pfaff said:
Ben said:
(e-mail address removed) writes:

Any compiler that compiles p = p->next = q as storing into p
before fetching p->next is simply broken. Operands are
evaluated before the operator they're operands for, and
operators are evaluated before they produce a value--that's
just how C semantics work (in a C abstract machine of course).

Do you have any citations to back up your assertions?

"Evaluation of an expression may _produce_ side effects."

"[At sequence points,] all side effects of _previous_
evaluations shall be complete [...]."

"In the abstract machine, all expressions are evaluated
_as specified by the semantics_."
OK.

"[An operator] may specify an operation to be performed
(which _in turn_ may _yield a value_ [...], _produce_ a
side effect, or some combination thereof) [...]."

I don't see anything about ordering there.
"The syntax specifies the precedence of operators _in the
evaluation of_ an expression [...]."

Precedence != ordering.


Precedence puts unavoidable contraints on ordering, because it establishes
which subexpressions are the operands of other expressions.

"[Except ...], _the order of evaluation of subexpressions_
and _the order in which side effects take place_ are both
unspecified." (Not "order of A and B" but "order of A" and
"order of B".)

I don't think that typographical distinction is a good one to
hinge an argument on. "the order of" could be repeated just for
clarity of exposition. Even if the typography is significant
here, I don't think it gets you where you want to go.
Keeping these in mind, read through section 6.5, noting
which paragraphs are marked "Semantics". Expressions
are evaluated "as specified by the semantics".

OK. Let's look at section 6.15.6 "Assignment operators":

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

4 The order of evaluation of the operands is
unspecified. ...


In p = p->next = q there are two assignments, so 6.15.6 has to be applied to
each of them separately.

4 The order of evaluation of the operands is
unspecified. ...

Well each assignment has left and right operands, and in each case it makes
no difference whether we evaluate the left before the right or vice versa.
Nobody is claiming otherwise. It's a non-issue.

But nothing here contradicts the fact that one assignment must be evaluated
before the other, for the simple reason that the value of one is an operand
of the other. "Evaluated" here means fully evaluated according to the
semantics -- the abstract machine has no optimiser and no licence to
simplify, so just doing enough to ascertain the value doesn't qualify.


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

The wording is very specific. "The side effect of updating the stored
value" (of an object referenced by an lvalue) doesn't extend to computing
the lvalue in question. The evaluation of the left operand of an assignment
is not part of the side effect.

The timing of the side effect is only an issue for the outer assignment.
And the quote above only puts outer limits on when the side effect occurs.
It doesn't say that the side effect may occur at any time between the
sequence points -- of course that would be silly, because the new value has
to be computed before it can be stored, so the side effect cannot occur
before the evaluation of the expression which provides the new value.
Another constraint on ordering that is too obvious to be stated, but is not
overridden by any wording.

It seems pretty clear that both the order of evaluation of the
operands and the time at which the result is stored are both
rather unconstrained.


That's an interesting approach: two insignificant details are left
unspecified, so it must be saying "anything goes"
 
B

Ben Pfaff

You (Robin Haigh) believe that
p = p->next = q;
is well defined. Many other people share this opinion. But when
it was brought up in comp.std.c a long time ago, there were also
many people with the opposite opinion. If I recall correctly,
some of these folks were actually committee members. I'm not
going to waste a lot more of my time resuming the debate. It's
not productive. Go back and read the old thread.
 
R

Robin Haigh

Ben Pfaff said:
You (Robin Haigh) believe that
p = p->next = q;
is well defined. Many other people share this opinion. But when
it was brought up in comp.std.c a long time ago, there were also
many people with the opposite opinion. If I recall correctly,
some of these folks were actually committee members. I'm not
going to waste a lot more of my time resuming the debate. It's
not productive. Go back and read the old thread.


Well I wouldn't claim that the standard is crystal clear on the subject.

However, if this expression produces undefined behaviour, it can only be
because of 6.5/2. Trouble is, an enthusiastic reading of 6.5/2 can make all
sorts of things UB, including *p++. A line has to be drawn, and it might
seem important to know where it is.

One way to draw a line is to say that the old value (of the modified object)
can be used within the subexpression that computes the new value. This is
rational, workable, creates no difficulties or ambiguities, conflicts with
nothing in the standard, and finds support in the Rationale.

Many people have claimed that the line is somewhere else, but nobody seems
to be offering a workable detailed exposition of where they think it is.


The other approach (with p = p->next = q) is to argue that maybe 6.5/2
doesn't apply, but the outcome is still unspecified as a result of the
"reordering" licence given to the abstract machine.

Well it seems to me that 6.5/2 is intended to cover, and does cover, _at
least_ all the cases where the reordering licence creates an ambiguity, as
regards the value of an expression or as regards the final stored value of
any object (at the next sequence point). This doesn't leave any room for
outcomes to be unspecified as to these aspects. So the effect of the
reordering licence on any expression that isn't UB is invisible, unless
there are external consequences of side effects, such as file output or
asynchronous access to objects.
 
P

pete

Robin said:
Trouble is, an enthusiastic reading of 6.5/2 can make all
sorts of things UB, including *p++.

An enthusiastic reading of 6.5/2 should find the footnote:

70)
This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a = i;
 
R

Robin Haigh

pete said:
Robin said:
Trouble is, an enthusiastic reading of 6.5/2 can make all
sorts of things UB, including *p++.

An enthusiastic reading of 6.5/2 should find the footnote:

70)
This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a = i;



Can you see how this footnote helps? I can't
 
E

ena8t8si

Ben said:
Ben said:
(e-mail address removed) writes:

Any compiler that compiles p = p->next = q as storing into p
before fetching p->next is simply broken. Operands are
evaluated before the operator they're operands for, and
operators are evaluated before they produce a value--that's
just how C semantics work (in a C abstract machine of course).

Do you have any citations to back up your assertions?

"Evaluation of an expression may _produce_ side effects."

"[At sequence points,] all side effects of _previous_
evaluations shall be complete [...]."

"In the abstract machine, all expressions are evaluated
_as specified by the semantics_."
OK.

"[An operator] may specify an operation to be performed
(which _in turn_ may _yield a value_ [...], _produce_ a
side effect, or some combination thereof) [...]."

I don't see anything about ordering there.

Maybe in some imaginary world where words have no intrinsic
meaning. In _this_ world where the writing is a mixture of
informal and semi-formal English, the words used clearly say
something about ordering because of causality relationships.
"The syntax specifies the precedence of operators _in the
evaluation of_ an expression [...]."

Precedence != ordering.

Again, the word precedence implies something about
ordering because of the meaning of the word. The
root of precedence is precede.
"[Except ...], _the order of evaluation of subexpressions_
and _the order in which side effects take place_ are both
unspecified." (Not "order of A and B" but "order of A" and
"order of B".)

I don't think that typographical distinction is a good one to
hinge an argument on. "the order of" could be repeated just for
clarity of exposition. Even if the typography is significant
here, I don't think it gets you where you want to go.

Some people think that the standard says the ordering of any
events whatsoever between sequence points is completely
unconstrained. But that's not what it says. If you read
the defining paragraph for sequence points, you'll see that
sequence points are mentioned in relation to the relative
order of _side effects_. In the Semantics paragraphs there
never is a statement that side effects are unordered
relative to expression evaluations. On the contrary, it's
always side effects of _previous_ evaluations.

The standard takes pains to point out _in every case_ in the
various Semantics paragraphs each case where sequence points
come into play in determining (or not determing) ordering,
and it's always about side effects, nothing else. Since
trouble is taken to point out _exceptions_ to ordering
relationships, the clear implication is that other parts of
the standard use words with their normal sense of causality
and ordering relationships.

The sentence quoted above is just confirmation that the
standard intends to talk about (non-)ordering of side effects
relative to each other, and not relative to other events.
OK. Let's look at section 6.15.6 "Assignment operators":

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

4 The order of evaluation of the operands is
unspecified. ...

It seems pretty clear that both the order of evaluation of the
operands and the time at which the result is stored are both
rather unconstrained.

Only when read in isolation. When read in conjunction with
the other statements quoted above, since the evaluation
_produces_ the side effect, obviously the evaluation has to
come first.
 
E

ena8t8si

pete said:
It's possible for all evaluations to be completed
before any side effects take place.

Sure it is. What is _not_ possible is for any side
effect to precede the expression evaluation that
produces it. Did you miss the point of what I
was saying?
Hence we get OP's situation:
"A coworker and I have been debating the 'correct' expectation
of evaluation for the phrase a = b = c.
Two different versions of GCC ended up compiling this as
b = c; a = b
and the other ended up compiling it as
a = c; b = c. "

Yes, the situation _arose_ because the storing of a can
precede the storing of b. But the question the OP was
asking was about the semantics of the assignment operator.
When the side effects happen is part of those semantics,
but not the main point OP was asking about.
 
E

ena8t8si

pete said:
Micah said:
Richard G. Riley schrieb:

I'm coming around to thinking that 6.5 [#2] is relevant, and that
p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your intent.
You still are modifying p twice between sequence points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q = next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get the
value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation includes
all the actions in the Semantics paragraph, which includes
starting the side effect of storing the value.
 
V

Vladimir S. Oka

Micah Cowan wrote:

Richard G. Riley schrieb:

I'm coming around to thinking that 6.5 [#2] is relevant, and
that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your intent.
You still are modifying p twice between sequence points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q = next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get the
value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation includes
all the actions in the Semantics paragraph, which includes
starting the side effect of storing the value.

I think pete is right. The compiler _knows_ that the result of (p->q =
next) _will_be_ next before it even produces any code, and can use that
knowledge once it does get to producing some.
 
E

ena8t8si

Vladimir said:
(e-mail address removed) wrote:

Micah Cowan wrote:

Richard G. Riley schrieb:

I'm coming around to thinking that 6.5 [#2] is relevant, and
that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your intent.
You still are modifying p twice between sequence points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q = next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get the
value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation includes
all the actions in the Semantics paragraph, which includes
starting the side effect of storing the value.

I think pete is right. The compiler _knows_ that the result of (p->q =
next) _will_be_ next before it even produces any code, and can use that
knowledge once it does get to producing some.

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.
 
V

Vladimir S. Oka

pete wrote:
(e-mail address removed) wrote:

Micah Cowan wrote:

Richard G. Riley schrieb:

I'm coming around to thinking that 6.5 [#2] is relevant,
and that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your
intent. You still are modifying p twice between sequence
points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q = next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get the
value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation includes
all the actions in the Semantics paragraph, which includes
starting the side effect of storing the value.

I think pete is right. The compiler _knows_ that the result of (p->q
= next) _will_be_ next before it even produces any code, and can use
that knowledge once it does get to producing some.

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

But it still does not evaluate p->q in order to assign next to it. What
is evaluated is next, and its value is /assigned/ to p->q. Next, the
assignment is evaluated and assigned to p. The assignment evaluates to
next, but I don't think that compiler is obliged to do that in any
particular way.
 
C

Chris Torek

[on whether
p = p->next = next;
could have undefined behavior]

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

This is correct -- in order to claim to compile Standard C, a C
compiler must obey the standard -- but the question is whether
"everything else in the standard" actually *says* that

p = p->next = next;

has to have defined behavior. You and I may think it is bizarre
and wrong if the actual wording in the Standard fails to require
this to work the "obvious" and "correct" way. Unfortunately, those
who argue that both C89 and C99 fail to require this to work, do
in fact seem to have a leg to stand on, as the saying goes.

(The standard has a number of defects, and I consider this to be
one of them, albeit a minor one. Someone's -- I have forgotten
whose -- proposed annex to the C99 standard, with a formal
model for deciding sequence points, would have at least provided
a definitive answer. But that annex did not make it in.)

(There is a simple cure available to the C programmer, though: just
write:

p->next = next, p = next;

instead. We can avoid skating on the thin ice, instead of arguing
about just how thin it may be.)
 
M

Micah Cowan

The value of p->q = next is next. But in order to get the
value, the assignment p->q = next must _have been
evaluated_,

Wrong. The assignment itself need not have taken place. If you really
/did/ understand sequence points, you would know this.

As far as C is concerned, an implementation is more than welcome to do

p = next;
p->q = next;

That's why sequence points are very important.
 
M

Michael Wojcik

You (Robin Haigh) believe that
p = p->next = q;
is well defined. Many other people share this opinion. But when
it was brought up in comp.std.c a long time ago, there were also
many people with the opposite opinion. If I recall correctly,
some of these folks were actually committee members.

It's my feeling that any question of the C standard which leads to
an extended argument between Lawrence Kirby and Douglas Gwyn[1] is
pretty much by definition an unclear matter.

I agree with Ben. It's not productive. Avoid multiple assignments
between sequence points where ordering could be significant, and not
only is the potential problem avoided, but the resulting code will
be simpler and clearer.


1. http://groups.google.com/group/comp...18c7f713378/eeab46d713d239a2#eeab46d713d239a2
 
E

ena8t8si

Vladimir said:
pete wrote:
(e-mail address removed) wrote:

Micah Cowan wrote:

Richard G. Riley schrieb:

I'm coming around to thinking that 6.5 [#2] is relevant,
and that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your
intent. You still are modifying p twice between sequence
points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q = next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get the
value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation includes
all the actions in the Semantics paragraph, which includes
starting the side effect of storing the value.

I think pete is right. The compiler _knows_ that the result of (p->q
= next) _will_be_ next before it even produces any code, and can use
that knowledge once it does get to producing some.

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

But it still does not evaluate p->q in order to assign next to it. What
is evaluated is next, and its value is /assigned/ to p->q. Next, the
assignment is evaluated and assigned to p. The assignment evaluates to
next, but I don't think that compiler is obliged to do that in any
particular way.

Sorry, the compiler _is_ obliged to do the evaluation as per the
Semantics description. The Semantics paragraphs in 6.5.16
specify _both_ that the store happens and what the value is.
(Yes, the side effect of the store may happen later.) Where in
the standard is there any statement that says an expression
(_any_ expression) may yield a value _before_ it has been
evaluated?
 
E

ena8t8si

Chris said:
[on whether
p = p->next = next;
could have undefined behavior]

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

This is correct -- in order to claim to compile Standard C, a C
compiler must obey the standard -- but the question is whether
"everything else in the standard" actually *says* that

p = p->next = next;

has to have defined behavior. You and I may think it is bizarre
and wrong if the actual wording in the Standard fails to require
this to work the "obvious" and "correct" way. Unfortunately, those
who argue that both C89 and C99 fail to require this to work, do
in fact seem to have a leg to stand on, as the saying goes.

If you mean there are more than a few people that believe that
p = p->next = next are undefined, I agree with you.

The problem is, none of their arguments are convincing. They all
boil down to the premise that an expression can yield a value
before it has been evaluated. But no one has cited any statement
from the standard that says this is allowed. Usually the arguments
are of the form "I can imagine a compiler doing this, so it must
be allowed." That's a bogus argument.

Let me put it as a question - are you aware of any statement
in the standard that says an expression may yield a value
before it's been evaluated?
(The standard has a number of defects, and I consider this to be
one of them, albeit a minor one. Someone's -- I have forgotten
whose -- proposed annex to the C99 standard, with a formal
model for deciding sequence points, would have at least provided
a definitive answer. But that annex did not make it in.)

I wonder how many people took the trouble to read _any_ of the
formal models--there are at least three or four generally
available--before considering their positions or framing their
arguments.
 
E

ena8t8si

Micah said:
Wrong. The assignment itself need not have taken place. If you really
/did/ understand sequence points, you would know this.

As far as C is concerned, an implementation is more than welcome to do

p = next;
p->q = next;

That's why sequence points are very important.

Ahh, the old "you don't know what you're talking about" defense.
A bold ploy. Of course your statement presupposes that _you_ do
know what you're talking about. The problem is, you haven't
given any supporting statements for either opinion. Or, for that
matter, your opinion about how p = p->next = q is evaluated.

Any other unsupported opinions you'd like to express?
 
V

Vladimir S. Oka

(e-mail address removed) opined:
Vladimir S. Oka wrote:
On Friday 17 March 2006 04:48, (e-mail address removed) opined
(in
<[email protected]>):


pete wrote:
(e-mail address removed) wrote:

Micah Cowan wrote:

Richard G. Riley schrieb:
On 2006-03-12, pete <[email protected]>
wrote:

I'm coming around to thinking that 6.5 [#2] is
relevant, and that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying
your intent. You still are modifying p twice
between sequence points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q
= next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get
the value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation
includes all the actions in the Semantics paragraph,
which includes starting the side effect of storing the
value.

I think pete is right. The compiler _knows_ that the
result of (p->q = next) _will_be_ next before it even
produces any code, and can use that knowledge once it does
get to producing some.

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

But it still does not evaluate p->q in order to assign next
to it. What is evaluated is next, and its value is /assigned/
to p->q. Next, the assignment is evaluated and assigned to p.
The assignment evaluates to next, but I don't think that
compiler is obliged to do that in any particular way.

Sorry, the compiler _is_ obliged to do the evaluation as per
the Semantics description. The Semantics paragraphs in 6.5.16
specify _both_ that the store happens and what the value is.
(Yes, the side effect of the store may happen later.) Where
in the standard is there any statement that says an expression
(_any_ expression) may yield a value _before_ it has been
evaluated?

Ok, now my head starts to spin a bit, but bear with me...

Is it right that the original problem statement was:

p = p->next = q;

Assuming it was: `p` is not modified twice, it is read once to
get `next`, and modified once to assign value to it in the
leftmost assignment. Assigning value to `next` does not modify
`p`, right?

Now, I re-read what 6.5.16 has to say, and yes, I know examples
are not normative, but to me Example 2 (6.5.16.1p5) seems to
support the following interpretation of the above statement:

1) `q` is evaluated
2) its value is assigned to `p->next`
3) that value becomes the value of the rightmost assignment
4) that value is assigned to `p`

I read that it happens in the order I give above. Also, `p` is
modified only once (4), and read only once (2).

I think this, now, expresses how I see what happens, and my
reading of the Standard. If it isn't what I said before, it's
completely my fault (assign 20% to English being my second
language).
 
E

ena8t8si

Vladimir said:
(e-mail address removed) opined:
Vladimir S. Oka wrote:
On Friday 17 March 2006 04:48, (e-mail address removed) opined
(in
<[email protected]>):


pete wrote:
(e-mail address removed) wrote:

Micah Cowan wrote:

Richard G. Riley schrieb:
On 2006-03-12, pete <[email protected]>
wrote:

I'm coming around to thinking that 6.5 [#2] is
relevant, and that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying
your intent. You still are modifying p twice
between sequence points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q
= next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get
the value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation
includes all the actions in the Semantics paragraph,
which includes starting the side effect of storing the
value.

I think pete is right. The compiler _knows_ that the
result of (p->q = next) _will_be_ next before it even
produces any code, and can use that knowledge once it does
get to producing some.

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

But it still does not evaluate p->q in order to assign next
to it. What is evaluated is next, and its value is /assigned/
to p->q. Next, the assignment is evaluated and assigned to p.
The assignment evaluates to next, but I don't think that
compiler is obliged to do that in any particular way.

Sorry, the compiler _is_ obliged to do the evaluation as per
the Semantics description. The Semantics paragraphs in 6.5.16
specify _both_ that the store happens and what the value is.
(Yes, the side effect of the store may happen later.) Where
in the standard is there any statement that says an expression
(_any_ expression) may yield a value _before_ it has been
evaluated?

Ok, now my head starts to spin a bit, but bear with me...

Is it right that the original problem statement was:

p = p->next = q;
Yes.

Assuming it was: `p` is not modified twice, it is read once to
get `next`, and modified once to assign value to it in the
leftmost assignment. Assigning value to `next` does not modify
`p`, right?

Now, I re-read what 6.5.16 has to say, and yes, I know examples
are not normative, but to me Example 2 (6.5.16.1p5) seems to
support the following interpretation of the above statement:

1) `q` is evaluated
2) its value is assigned to `p->next`
3) that value becomes the value of the rightmost assignment
4) that value is assigned to `p`

I read that it happens in the order I give above. Also, `p` is
modified only once (4), and read only once (2).

Yes. Also the side effect of updating the value of p->next
can happen any time after (2), and the side effect of updating
the value of p can happen any time after (4).
 
V

Vladimir S. Oka

(e-mail address removed) opined:
(e-mail address removed) opined:
Vladimir S. Oka wrote:
Vladimir S. Oka wrote:
On Friday 17 March 2006 04:48, (e-mail address removed) opined
(in
<[email protected]>):


pete wrote:
(e-mail address removed) wrote:

Micah Cowan wrote:

Richard G. Riley schrieb:
On 2006-03-12, pete <[email protected]>
wrote:

I'm coming around to thinking that 6.5 [#2] is
relevant, and that p = p->next = q
is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying
your intent. You still are modifying p twice
between sequence points

Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.

Look again. Reading p is necessary to evaluate p->q
= next,

No, it isn't.
The value of (p->q = next) is (next)

The value of p->q = next is next. But in order to get
the value, the assignment p->q = next must _have been
evaluated_, which needed both operands. Evaluation
includes all the actions in the Semantics paragraph,
which includes starting the side effect of storing the
value.

I think pete is right. The compiler _knows_ that the
result of (p->q = next) _will_be_ next before it even
produces any code, and can use that knowledge once it does
get to producing some.

You've fallen into the trap of arguing based on what a
compiler might be capable of. Regardless of what the
compiler knows, it's still obliged to produce code that
behaves according to how the Semantics paragraphs and
everything else in the standard says it must.

But it still does not evaluate p->q in order to assign next
to it. What is evaluated is next, and its value is /assigned/
to p->q. Next, the assignment is evaluated and assigned to p.
The assignment evaluates to next, but I don't think that
compiler is obliged to do that in any particular way.

Sorry, the compiler _is_ obliged to do the evaluation as per
the Semantics description. The Semantics paragraphs in 6.5.16
specify _both_ that the store happens and what the value is.
(Yes, the side effect of the store may happen later.) Where
in the standard is there any statement that says an expression
(_any_ expression) may yield a value _before_ it has been
evaluated?

Ok, now my head starts to spin a bit, but bear with me...

Is it right that the original problem statement was:

p = p->next = q;
Yes.

Assuming it was: `p` is not modified twice, it is read once to
get `next`, and modified once to assign value to it in the
leftmost assignment. Assigning value to `next` does not modify
`p`, right?

Now, I re-read what 6.5.16 has to say, and yes, I know examples
are not normative, but to me Example 2 (6.5.16.1p5) seems to
support the following interpretation of the above statement:

1) `q` is evaluated
2) its value is assigned to `p->next`
3) that value becomes the value of the rightmost assignment
4) that value is assigned to `p`

I read that it happens in the order I give above. Also, `p` is
modified only once (4), and read only once (2).

Yes. Also the side effect of updating the value of p->next
can happen any time after (2), and the side effect of updating
the value of p can happen any time after (4).

I agree.

However, what is the side effect of updating `p->next`? It's just plain
storing a value into a variable. Again, `p` is not modified, AFAICT.
It's the struct member `next` that is, and `p` is just read to get its
address (`next`'s address).

Whether there's any side effect of updating `p` after (4) is, IMO,
irrelevant to the discussion (again, I can't see any side effect of
storing a value into variable).

I still think the operation in this case has to be equivalent to:

p->next = q;
p = p->next;

--
BR, Vladimir

A lad, at his first copulation,
Cried, "What a sensation! Inflation,
Gyration, elation
Throughout the duration,
I guess I'll give up masturbation."
 

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

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top