Multiple Assignment Evaluation Debate

B

Ben Pfaff

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

That is always true.

The best that the rest of us can hope for is to answer a question
earlier than Chris, or to answer a question that he doesn't.
 
P

pete

Even if that ridiculous claim were true,

The ridiculous claim is true.
The topic comes up on this newsgroup from time to time,
usually in the form of "p = p->next = q"
You can look it up next time you're online.

http://groups.google.com/group/comp.lang.c/search?group=comp.lang.c&q="p+=+p->next+=+q"

http://groups.google.com/group/comp.lang.c/msg/1c7e2102fd256d79

"The p = p->next = q problem is also a real problem that came from
a program I was writing when I posted a question about it here
years ago." -- Ben Pfaff
it isn't responsive
to the OP's question. Does the intermediate assignment
influence what value is assigned to a? The answer to that
is YES.

No.
The value of the intermediate assignment
influences what value is assigned to a,
but that value is known prior to the side effect
of the actual assignment taking place.

If you have
unsigned char uc;
unsigned int ui;
ui = uc = -1;
that can be translated as either
uc = UCHAR_MAX;
ui = UCHAR_MAX;
or
ui = UCHAR_MAX;
uc = UCHAR_MAX;
The order in which the side effects take place doesn't matter.

OP's question is entirely an issue of
side effects not being ordered between sequence points.
 
E

ena8t8si

pete said:
The ridiculous claim is true.
The topic comes up on this newsgroup from time to time,
usually in the form of "p = p->next = q"
You can look it up next time you're online.

http://groups.google.com/group/comp.lang.c/search?group=comp.lang.c&q="p+=+p->next+=+q"

http://groups.google.com/group/comp.lang.c/msg/1c7e2102fd256d79

"The p = p->next = q problem is also a real problem that came from
a program I was writing when I posted a question about it here
years ago." -- Ben Pfaff

Excuse me while I stop laughing. 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). The same thing is true for
assignment: only the *side effect* of updating the object
being assigned might be delayed until the next sequence
point. And there's no point in allowing an "optimization"
which if it's allowed causes people to stop using the very
construct being optimized.

If it were permitted to evaluate operators before operands
when the resulting value is "known" then this code could get
undefined behavior -

unsigned u = 12345, *p = &u;
p += 1|(0&*p);

The idea that p = p->next = q is allowed to store into
p before evaluating p->next doesn't pass the laugh test.
No.
The value of the intermediate assignment
influences what value is assigned to a,
but that value is known prior to the side effect
of the actual assignment taking place.

The "intermediate assignment" I'm talking about is the text
"b =", not any run time operation.
If you have
unsigned char uc;
unsigned int ui;
ui = uc = -1;
that can be translated as either
uc = UCHAR_MAX;
ui = UCHAR_MAX;
or
ui = UCHAR_MAX;
uc = UCHAR_MAX;
The order in which the side effects take place doesn't matter.

OP's question is entirely an issue of
side effects not being ordered between sequence points.

If you think that then you didn't understand the question
the OP was trying to ask.
 
B

Ben Pfaff

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?
 
P

pete

Ben said:
Do you have any citations to back up your assertions?

I think he's confused about sequence points.
Some of the standard's function operator descriptions
seem to order the sequence of events between sequence points,
without proper respect to the concept of sequence points.

Here, we see the standard claim that the postfix increment
takes place after the result is obtained:
N869
6.5.2.4 Postfix increment and decrement operators
[#2] The result of the postfix ++ operator is the value of
the operand. After the result is obtained, the value of the
operand is incremented.

But, if we look at
N869
5.1.2.3 Program execution
[#16] EXAMPLE 7
"sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));
but the actual increment of p can occur at any time between
the previous sequence point and the next sequence point "

.... we see the standard deny any ordering of events
between sequence points.

In the p = p->next = q thread discussion, a lot of people quoted:
N869
6.5 Expressions
[#2] Furthermore, the prior value
shall be accessed only to determine the value to be
stored.60)
60)This paragraph renders undefined statement expressions
such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a = i;

.... but I don't think that's really what makes the case for
undefinedness,
because that would imply that there is something wrong with
p = p->next
and I don't think that there is anything wrong with
p = p->next

I think it's just simply that assignments aren't sequence points,
which is what the problem is with
p = p->next = q.
 
P

pete

In the p = p->next = q thread discussion, a lot of people quoted:
N869
6.5 Expressions
[#2] Furthermore, the prior value
shall be accessed only to determine the value to be
stored.60)
60)This paragraph renders undefined statement expressions
such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a = i;

... but I don't think that's really what makes the case for
undefinedness,
because that would imply that there is something wrong with
p = p->next
and I don't think that there is anything wrong with
p = p->next

I think it's just simply that assignments aren't sequence points,
which is what the problem is with
p = p->next = q.


I've been mulling this, and I've come to think that my
p = p->next
implication, is incorrect.
In the multiple assignment expression, "the prior value", (p),
is also accessed to determine which object
is the lvalue in the assignment from q
p->next = q

I'm coming around to thinking that 6.5 [#2] is relevant, and that
p = p->next = q
is undefined, and not just unspecified.
 
R

Richard G. Riley

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 .
 
M

Michael Mair

Richard said:
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; parentheses
do not introduce new sequence points.
If you want to be on the safe side, you can only do
p->next = q, p = p->next;
which IMO does not give you any advantage over
p->next = q;
p = p->next;

Cheers
Michael
 
R

Richard G. Riley

Richard said:
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; parentheses
do not introduce new sequence points.
If you want to be on the safe side, you can only do
p->next = q, p = p->next;
which IMO does not give you any advantage over
p->next = q;
p = p->next;

Cheers
Michael

Well, thats a shit load of legacy code I didnt properly break down
then. Mind you, I'm sure there was never a problem.

I'm truly astonished at this.

But looking at it anyway its not quite as "common" as

a=b=c=1;

Whats the problem with the above? Anything? (Assuming single thread
etc).
 
B

Ben Pfaff

Richard G. Riley said:
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 .

Why would you think it to be any different? Parentheses do not
insert a sequence point.
 
R

Robin Haigh

pete said:
Ben said:
Do you have any citations to back up your assertions?

I think he's confused about sequence points.
Some of the standard's function operator descriptions
seem to order the sequence of events between sequence points,
without proper respect to the concept of sequence points.

Here, we see the standard claim that the postfix increment
takes place after the result is obtained:
N869
6.5.2.4 Postfix increment and decrement operators
[#2] The result of the postfix ++ operator is the value of
the operand. After the result is obtained, the value of the
operand is incremented.

But, if we look at
N869
5.1.2.3 Program execution
[#16] EXAMPLE 7
"sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));
but the actual increment of p can occur at any time between
the previous sequence point and the next sequence point "

... we see the standard deny any ordering of events
between sequence points.


But the standard isn't just a collection of sayings to be quoted out of
context. You have to follow the narrative.

The first quote here is defining the semantics of the abstract machine.

The second quote occurs as part of a long discussion of the "as-if" rule,
i.e. the extent to which an implementation may or may not depart from the
abstract machine.

The second quote doesn't contradict the first. On the contrary, it
presupposes the first: if the semantics of the abstract machine were
unspecified, there wouldn't be much point in saying that the real machine
has more latitude.
 
M

Micah Cowan

Michael Mair said:
Richard said:
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.
 
M

Michael Mair

Micah said:
Richard said:
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.

You are right, thanks for the correction.

Cheers
Michael
 
E

ena8t8si

Ben said:
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_."

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

"The syntax specifies the precedence of operators _in the
evaluation of_ an expression [...]."

"[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".)

Keeping these in mind, read through section 6.5, noting
which paragraphs are marked "Semantics". Expressions
are evaluated "as specified by the semantics".
 
E

ena8t8si

pete said:
I think he's confused about sequence points.

I assure you I understand sequence points quite well.
Some of the standard's function operator descriptions
seem to order the sequence of events between sequence points,
without proper respect to the concept of sequence points.

Here, we see the standard claim that the postfix increment
takes place after the result is obtained:
N869
6.5.2.4 Postfix increment and decrement operators
[#2] The result of the postfix ++ operator is the value of
the operand. After the result is obtained, the value of the
operand is incremented.

But, if we look at
N869
5.1.2.3 Program execution
[#16] EXAMPLE 7
"sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));
but the actual increment of p can occur at any time between
the previous sequence point and the next sequence point "

... we see the standard deny any ordering of events
between sequence points.

You're not reading carefully enough. The order in which
_side effects_ take place is unspecified, but _some_
evaluation must precede _any_ side effect, because it is
evaluations that produce side effects. The "at any time"
in the comment above means relative to other side effects.
 
E

ena8t8si

Micah said:
Michael Mair said:
Richard said:
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,
and evaluating p->q = next is necessary to get the value
that's assigned to p.
 
P

pete

The order in which
_side effects_ take place is unspecified, but _some_
evaluation must precede _any_ side effect, because it is
evaluations that produce side effects.

It's possible for all evaluations to be completed
before any side effects take place.
The "at any time"
in the comment above means relative to other side effects.

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. "
 
P

pete

Micah said:
Michael Mair 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)
 
B

Ben Pfaff

Ben said:
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.
"[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. ...

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.
 

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