Topic: i = i++ (almost)


Author: yarbroug@bga.com (Keith Yarbrough)
Date: 23 Oct 1994 01:32:10 GMT
Raw View
yaakov (yaakov@cc.gatech.edu) wrote:
: In article <NEWTNews.5910.781379262.craign@craign-slip.teleport.com>,
: Craig R. Nelson <craign@teleport.com> wrote:
: >
: >ok, folks, try this:
: >
: >  ++i++;
: >
: >take two asprin and read an ARM in the morning :-)
: >
: >Craig
: >
: I believe that ++i++ is not syntactically correct (so we don't even get into
: the question of what it does).  ++i++ would mean ++(i++) according to the
: precedence of the preincrement and postincrement operators, and
: ++(something) requires that (something) be an lvalue, but i++ is not an lvalue.

:                                Yaakov


Break out those aspirin.  Yaakov is confining himself to the narrow
world of C, not C++.  In C++, i++ can indeed be an lvalue.

class foo {
public:
   foo() { ival = 0; }
   foo & operator++() { ival++; return *this; }

private:
   int ival;
};

main()
{
   foo i;
   ++i++;
}

This app works fine.  The i.ival is set to 2 when all is done.

--
Regards,
--------------------------------------------------------------------
Keith A. Yarbrough                                  yarbroug@bga.com
--------------------------------------------------------------------




Author: seebs@solutions.solon.com (Peter Seebach)
Date: 23 Oct 1994 17:31:37 GMT
Raw View
In article<38ceeq$23q@giga.bga.com> yarbroug@bga.com (Keith Yarbrough) writes:
>yaakov (yaakov@cc.gatech.edu) wrote:
>Break out those aspirin.  Yaakov is confining himself to the narrow
>world of C, not C++.  In C++, i++ can indeed be an lvalue.

Yes, but in that case, I think comp.lang.c should be removed from the
newsgroups line.

Certainly, comp.std.c should be removed.

-seebs
--
Peter Seebach - seebs@solutions.solon.com  -- I need a job!  Hire me!
GAT(CS/M/P/SS) d-- H++ s+: !g p? !au a- w+++ v+++/* C++++ UB/V++++ P+ L b+++ !D
3+(NetBSD/Amiga) E- N+++ K !W--- M++/-- V- -po+ Y+ t 5++ jx R G'''' tv- B---
e++ u** h--- f+ r+++ !n x+++* --SeebS-- / The Laughing Prophet




Author: yaakov@cc.gatech.edu (yaakov)
Date: 17 Oct 1994 02:04:36 -0400
Raw View
In article <NEWTNews.5910.781379262.craign@craign-slip.teleport.com>,
Craig R. Nelson <craign@teleport.com> wrote:
>
>ok, folks, try this:
>
>  ++i++;
>
>take two asprin and read an ARM in the morning :-)
>
>Craig
>
I believe that ++i++ is not syntactically correct (so we don't even get into
the question of what it does).  ++i++ would mean ++(i++) according to the
precedence of the preincrement and postincrement operators, and
++(something) requires that (something) be an lvalue, but i++ is not an lvalue.

                               Yaakov

--

     -Yaakov Eisenberg (yaakov@cc.gatech.edu)




Author: xx657@FreeNet.Carleton.CA (Michael Whiten)
Date: Thu, 13 Oct 1994 23:57:28 GMT
Raw View
In a previous article, volpe@bart.crd.ge.com (Christopher R. Volpe) says:

> steve@lia.com (Stephen Williams) writes:
>> "Craig R. Nelson" <craign@teleport.com> writes:
>>
>>   ok, folks, try this:
>>
>>     ++i++;
>>
>>Ok, how 'bout this:
>>
>>The expression ++i is a l-value.
>
>In what language?
>

I think that shud be (++i)++.

The old 2.4.5 version of the gnu compiler gave an amusing result to this:

 int i = 3;

 ++(++i);
 cout << i << endl;

it output a '6'.  :-)

P.S. ++(++(++i))) yield a '12' I think.


cli





Author: xx657@FreeNet.Carleton.CA (Michael Whiten)
Date: Fri, 14 Oct 1994 00:03:23 GMT
Raw View
In a previous article, hoshi@sra.co.jp (Hoshi Takanori) says:

> steve@lia.com (Stephen Williams) writes:
>
>> Thus, the only possible binding is (++i)++.
>
>No.  My copy of ARM says that postfix ++ is preceded over prefix ++.
>So, ++i++ *must* be parsed ++(i++) in C++.  (I believe it's same in C.)

I thought that the reputed difference in the precedences of post- and
pre-fix ++/-- was a myth.  I always suspected it came from the K&R way
of explaining the semantic difference between the operators (i.e. that
the postfix increment/decrement happens sometime after the value of the
object is used but before the next sequence begins).

If there really *is* a difference in their precedences, then I simply ask
"why?"


cli





Author: steve@lia.com (Stephen Williams)
Date: Thu, 6 Oct 1994 22:09:55 GMT
Raw View
In article <NEWTNews.5910.781379262.craign@craign-slip.teleport.com> "Craig R. Nelson" <craign@teleport.com> writes:


   ok, folks, try this:

     ++i++;

Ok, how 'bout this:

The expression ++i is a l-value. Also, the ++ must be applied to the i
before it is used as an l-value. "x++," however, is an rvalue
independent of anything. Thus, the only possible binding is
(++i)++. This implies that (++i) is evaluated first and the resulting
lvalue used to evaluate (++i)++. So, ++i++; ::= i += 2;.

I believe this is true for C and C++.

N'est pas?

--

Stephen 2. Williams
 Work: steve@lia.com
 Home: steve@icarus.com

    Fight License Managers! Buy from
    vendors who use honor system!




Author: danpop@cernapo.cern.ch (Dan Pop)
Date: Fri, 7 Oct 1994 09:07:46 GMT
Raw View
In <372cbh$7m3@panix.com> berke@panix.com (Wayne Berke) writes:

>Both ++i and i++ are rvalue expressions, neither is an lvalue.  Since
>++, --, =, *=, or any of the other "assignment class" operators require
>an lvalue operand, neither (++i)++ nor ++(i++) nor ++i++ is legal C.
>It's worse(*) than undefined, it's a syntax error!

It's certainly an error, but not a _syntax_ error. A syntax error is a
piece of code that cannot be parsed according to the C rules. There is
nothing syntactically wrong with these expressions. They're errors
because they're constraint violations.

Dan
--
Dan Pop
CERN, CN Division
Email: danpop@cernapo.cern.ch
Mail:  CERN - PPE, Bat. 31 R-004, CH-1211 Geneve 23, Switzerland




Author: berke@panix.com (Wayne Berke)
Date: 6 Oct 1994 22:38:41 -0400
Raw View
In <STEVE.94Oct6150955@spokane.LIA.COM> steve@lia.com (Stephen Williams) writes:

>In article <NEWTNews.5910.781379262.craign@craign-slip.teleport.com> "Craig R. Nelson" <craign@teleport.com> writes:


>   ok, folks, try this:

>     ++i++;

>Ok, how 'bout this:

>The expression ++i is a l-value. Also, the ++ must be applied to the i
>before it is used as an l-value. "x++," however, is an rvalue
>independent of anything. Thus, the only possible binding is
>(++i)++. This implies that (++i) is evaluated first and the resulting
>lvalue used to evaluate (++i)++. So, ++i++; ::= i += 2;.

>I believe this is true for C and C++.

>N'est pas?

Well I was looking for a smiley but I guess you're serious.  :-)

Both ++i and i++ are rvalue expressions, neither is an lvalue.  Since
++, --, =, *=, or any of the other "assignment class" operators require
an lvalue operand, neither (++i)++ nor ++(i++) nor ++i++ is legal C.
It's worse(*) than undefined, it's a syntax error!

If someone tells me this is different in C++ I'll be extremely shocked.

(*)  Of course, maybe syntax errors are better since they're easier to detect.

>--

>Stephen 2. Williams
> Work: steve@lia.com
> Home: steve@icarus.com

>    Fight License Managers! Buy from
>    vendors who use honor system!
--
Wayne Berke
berke@panix.com




Author: kanze@us-es.sel.de (James Kanze US/ESC 60/3/164 #71425)
Date: 07 Oct 1994 16:01:50 GMT
Raw View
In article <372cbh$7m3@panix.com> berke@panix.com (Wayne Berke)
writes:

|> In <STEVE.94Oct6150955@spokane.LIA.COM> steve@lia.com (Stephen Williams) writes:

|> >The expression ++i is a l-value.
|> >I believe this is true for C and C++.

|> Both ++i and i++ are rvalue expressions, neither is an lvalue.

|> If someone tells me this is different in C++ I'll be extremely shocked.

Prepare to be shocked.  This is one of the differences between C and
C++.  In C++, ++i *is* an lvalue.  (See section 5.3.1 of the ARM.)

I'll admit that I was shocked, too, when I found out.

I'll have to ask Andrew Koenig to explain why.  When I first found
out, he explained the rules to me in email.  There is a coherent rule
(which I've forgotten) which was used to determine what is and what
isn't an l-value.  I'll admit that I wasn't convinced at the time,
although I was pleased that at least the rule was coherent.

Since then, I have noticed (again thanks to Andy) that there is a
difference between "writing" to an object, and "modifying" an object;
there are (sometimes) very good reasons for wanting the results of
"modifying" an object to be an l-value.  So today I accept that ++i is
an l-value without being bothered.  On the other hand, this particular
logic doesn't apply to assignment (also an l-value in C++).
--
James Kanze      Tel.: (+33) 88 14 49 00     email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                              -- Beratung in industrieller Datenverarbeitung






Author: ark@alice.att.com (Andrew Koenig)
Date: Fri, 7 Oct 1994 18:42:35 GMT
Raw View
In article <372cbh$7m3@panix.com> berke@panix.com (Wayne Berke) writes:

> Both ++i and i++ are rvalue expressions, neither is an lvalue.  Since
> ++, --, =, *=, or any of the other "assignment class" operators require
> an lvalue operand, neither (++i)++ nor ++(i++) nor ++i++ is legal C.
> It's worse(*) than undefined, it's a syntax error!

> If someone tells me this is different in C++ I'll be extremely shocked.

Prepare to be extremely shocked.

In C++, if i is an int, then ++i is an lvalue (but i++ is not).
More generally, built-in operators that return one of their operands
return an lvalue if the operand is an lvalue.  i++ does not return
an lvalue because its result is not its operand but a copy of
its operand.

The reason for this is so that people will not be surprised to find

 const int& j = ++i;

having a different meaning from

 ++i;
 const int& j = i;
--
    --Andrew Koenig
      ark@research.att.com




Author: pascual@peggy.tid.es (Pascual Juan)
Date: Mon, 10 Oct 1994 14:07:43 GMT
Raw View
In article <CxBFyz.GJ5@alice.att.com>, ark@alice.att.com (Andrew Koenig) writes:
|> In C++, if i is an int, then ++i is an lvalue (but i++ is not).
|> More generally, built-in operators that return one of their operands
|> return an lvalue if the operand is an lvalue.  i++ does not return
|> an lvalue because its result is not its operand but a copy of
|> its operand.

I agree, but I think the code you gave hasn't a different meaning:

|> The reason for this is so that people will not be surprised to find
|>
|>  const int& j = ++i;
|>
|> having a different meaning from
|>
|>  ++i;
|>  const int& j = i;
|> --
|>     --Andrew Koenig
|>       ark@research.att.com

I guess you mean:

     const int& j = i++;

having a different meaning from

     const int& j = i;
     i++;

Or more generally:

     int i = 5;
     const int& j = i++; // j is a reference of a temporary.
     const int& k = ++i; // k is a reference of the authentic i.
     bool b = (&(i)!=&(j)) && (&(i)==&(k)); // always true.

This works fine in C-Front 3.0.

-------------------------------------------------------------------------------
 ||||  ##     ####                    |         Pascual Juan         |    _V_
 ||||  ## _|_ ## ##   Telefonica I+D  | E-mail: pascual@gonzo.tid.es |  _(,|,)`
 @@@@  ##  |  ## ##  (Telefonica R&D) |    Phone: +34-1-337-47-04    | | ___ ')
 @@@@  ##     ####                    |    fax:   +34-1-337-42-22    | |_|`__/
-------------------------------------------------------------------------------




Author: volpe@bart.crd.ge.com (Christopher R. Volpe)
Date: Mon, 10 Oct 1994 13:40:53 GMT
Raw View
In article <STEVE.94Oct6150955@spokane.LIA.COM>, steve@lia.com (Stephen Williams) writes:
>In article <NEWTNews.5910.781379262.craign@craign-slip.teleport.com> "Craig R. Nelson" <craign@teleport.com> writes:
>
>
>   ok, folks, try this:
>
>     ++i++;
>
>Ok, how 'bout this:
>
>The expression ++i is a l-value.

In what language?

--

Chris Volpe    Phone: (518) 387-7766 (Dial Comm 8*833
GE Corporate R&D   Fax:   (518) 387-6560
PO Box 8, Schenectady, NY 12301  Email: volpecr@crd.ge.com





Author: hoshi@sra.co.jp (Hoshi Takanori)
Date: 11 Oct 1994 02:40:22 GMT
Raw View
In article <STEVE.94Oct6150955@spokane.LIA.COM> steve@lia.com (Stephen Williams) writes:

> Thus, the only possible binding is (++i)++.

No.  My copy of ARM says that postfix ++ is preceded over prefix ++.
So, ++i++ *must* be parsed ++(i++) in C++.  (I believe it's same in C.)

hoshi




Author: kanze@us-es.sel.de (James Kanze US/ESC 60/3/164 #71425)
Date: 11 Oct 1994 17:15:25 GMT
Raw View
In article <CxGM0B.H5D@crdnns.crd.ge.com> volpe@bart.crd.ge.com
(Christopher R. Volpe) writes:

|> In article <STEVE.94Oct6150955@spokane.LIA.COM>, steve@lia.com (Stephen Williams) writes:
|> >In article <NEWTNews.5910.781379262.craign@craign-slip.teleport.com> "Craig R. Nelson" <craign@teleport.com> writes:

|> >   ok, folks, try this:

|> >     ++i++;

|> >Ok, how 'bout this:

|> >The expression ++i is a l-value.

|> In what language?

C++.  Chris Volpe is right, though.  This thread is cross-posted to
both comp.std.c and comp.std.c++.  So we should maybe make an effort
to specify which language (we think) we are talking about.
--
James Kanze      Tel.: (+33) 88 14 49 00     email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                              -- Beratung in industrieller Datenverarbeitung






Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Sun, 2 Oct 94 01:16:17 GMT
Raw View
Kevin D. Quitt (kdq@trans.jpl.nasa.gov) wrote:
> Thus wrote rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
> >Right.  But nothing that I can find in the standard requires that the function
> >be evaluated before the assignment.  Consider
> > i = 0 & f(i++);
> >Is this defined?  Is the compiler permitted to notice that 0 & anything
> >is 0 and assign 0 to i before evaluating f()?

> No and No.  Functions calls can have side-effects, and unless the compiler
> can guarantee that no side effects result from f(), it can't ignore the
> call.  C does not shortcut &.  The side-effects issue are requires that the
> function be evaluated before the assignment: those side effects may change
> where things are assigned!

> >I can find
> >no argument that f() must be evaluated before assigning in
> > i = 0 & f(i++);

> I trust now you have one.

Yes, but not this one.  C[++] permits side-effects to be deferred until the
next sequence point.  Calling f may be a side-effect, so why can it not
be deferred?  Your argument applies just as well to

 i = 0 & i++;

and we know (I hope we know) that this is undefined.

Several people have tried to argue from the semantics.  I believe that such
arguments fail because of the permission given to the C compiler to defer
side-effects and the fact that the semantics of a & b are that the value
is determined if a is 0 without examining b.

The reason I now believe that f must be called before the assignment is that
6.3 (ISO C) seems to require that values be determined based on the syntax.
It's not entirely clear, but it seems to me that this requires that the values
of the operands of & (and most other operators) must be determined before
the operation is done.  Annex G supports this (but, of course, is not part
of the standard).

Note the careful wording.  The standard clearly does not require that the
operands be evaluated, which would imply realization of the side-effects,
before performing the operation.  However, I think it implies that the
evaluation be done except for side-effects, which must be realized  only if
sequence points internal to the operand require.  In

 i = 0 & f(i++);

the sequence point after evaluation of the function designator and the
arguments requires that the incrementation be done before the & and so
before the =.

--
Mike Rubenstein




Author: msb@sq.sq.com (Mark Brader)
Date: Mon, 3 Oct 94 21:12:03 GMT
Raw View
I quoted 6.3.6/3.3.6:
# The result of the binary * operator is the product of the operands.

and commented:
> > "The operands", plural.  Two values are required.  If you think this is
> > not sufficiently explicit, then you could file a DR; I think it is.

Lawrence Kirby (fred@genesis.demon.co.uk) replied:
> The result of applying an operator is simply a value. Even the wording
> above simply defines what the value *is* and implies no constraints about
> how it is calculated.

The operands are, I claim, also simply values.  (3.1.5/6.1.5 defines operand
as "an entity on which an operator acts", which doesn't really help, and
even this vague meaning seems not to be adhered to elsewhere in the standard.)

If the operands are values, then it is not possible to calculate the product
of two of them, whether the calculation is constrained or not, until you
have two of them.  Ergo, the standard is requiring the operands to be
evaluated first (provided we could tell the difference -- as always, the
"as if" rule applies).

> An implementation is required to produce
> the right value but is not constrained in how it comes up with it.

It is constrained by the "as if" rule, as defined in 5.1.2.3/2.1.2.3.
This states that we are to read the standard as defining "an abstract
machine in which issues of optimization are irrelevant", but that the
implementation is only actually constrained by...

> The only constraints for order of evaluation defined by the standard
> concern side effects and sequence points.

In the example statement "i = 0 * g();", these *are* sufficient
constraints to require g() to be called, and for this call to occur
before i is assigned to, if we can tell whether this happened.

The abstract machine requires two operand values before doing a
multiplication; this requires the evaluation of both operands; this
requires the value of g(); this requires a call to g(), which (under
the "if we can tell" presumption above) has side effects which are
constrained by the rule.

And the call also implies a sequence point, which is constrained by
the above reasoning to occur at an earlier time than the assignment.
For this reason I think Lawrence is also wrong when he writes, in a
separate article:

> The standard says the the side effect of
> updating i must happen after the previous and before the next sequence point.
> This means it can happen either before or after the call to g, (the sequence
> point defined by the () operator is not relevant here since the i is not
> one of its operands).

The i is not an operand of (), but the sequence point is constrained anyway.

Followups redirected to comp.std.c again, for consistency.  One of Lawrence's
two postings responded to here appeared only there.
--
Mark Brader             |  "'A matter of opinion'[?]  I have to say you are
msb@sq.com              |    right.  There['s] your opinion, which is wrong,
SoftQuad Inc., Toronto  |    and mine, which is right."  -- Gene Ward Smith

This article is in the public domain.




Author: msb@sq.sq.com (Mark Brader)
Date: Mon, 3 Oct 94 21:36:52 GMT
Raw View
> > > i = 0 & f(i++);
>
> Yes, but not this one.  C[++] permits side-effects to be deferred until the
> next sequence point.  Calling f may be a side-effect, so why can it not
> be deferred?  ...

I am answering from a C point of view only and, for consistency, directing
followups back to comp.std.c.

The answer is that the "next sequence point" is defined by the requirements
of the abstract machine semantics.  Since f() returns a value, it must
contain at least one expression, and therefore there must be at least one
sequence point during its execution.  The first of these to occur is the
"next sequence point" by which time f() must really be called.
--
Mark Brader                     "How can we believe that?"
msb@sq.com                      "Because this time it's true!"
SoftQuad Inc., Toronto                  -- Lynn & Jay: YES, PRIME MINISTER

This article is in the public domain.




Author: diamond@jrd.dec.com (Norman Diamond)
Date: 3 Oct 1994 08:56:42 GMT
Raw View
In article <1994Sep28.102433.9027@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>Having said that, let me add that I now believe that
> i = 0 & f(i++);
>is defined

Oh don't leave us now that you've obtained a loyal following!

>based on ISO C 6.3
> Except as indicated by the syntax (35) or otherwise specified later ...
> the order of evaluation of subexpressions and the order in which
> side-effects take place are both unspecified.
>The meaning of the first clause is not very clear,

I think it means in a case like   i = j & f(i++)   if it is impossible for
the translator to figure out the value of j or the result of the function
call, and if the generated object code (or other executor) observes that j
is non-zero, then it must call f before computing & and it must increment
i before computing & (or if j must be zero but the translator is too stupid
to figure that out, then it must fall back on similar bullet-proof code).

>but the only way I can make any sense of it is to assume that it means that
>the compiler may NOT consider the semantics of an operation in deciding
>whether to evaluate its operands (except, of course, for those operations
>like && where the standard requires it).

Two problems with this.  First, your quotation says that semantics may not
be considered to specify an ordering (except of course for && and other
places where specified later), because only syntax can do so.  So we must
interpret this as giving implementations more freedom, not less.  Second,
remember that we've never suggested omitting the evaluation of operands,
only deferring the evaluations until required by a sequence point.
(These two problems do not have to be recognized in this sequence :-)

>Annex G supports this, listing as unspecified behavior
> The order in which expressions are evaluated -- in any order conforming
> to the precedence rules even in the presence of parentheses.
>Of course the footnote and Annex are not part of the standard, so these
>cannot be considered as proof.

Annex G supports your original interpretation and mine, against your
current rash of insanity :-)  But of course it is not proof of anything.
--
 <<  If this were the company's opinion, I would not be allowed to post it.  >>
segmentation fault (california dumped)




Author: diamond@jrd.dec.com (Norman Diamond)
Date: 3 Oct 1994 08:45:14 GMT
Raw View
In article <CwurA2.7v4@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
>* (3.3) To the extent that syntax specifies precedence, syntax specifies
>order of side-effects.

It does not.  In the expression   i = j++   there is no requirement for
the side effect of incrementing j to occur before the side effect of
modifying i.  If there were, then even   i = i++   would be defined.
--
 <<  If this were the company's opinion, I would not be allowed to post it.  >>
segmentation fault (california dumped)




Author: "Craig R. Nelson" <craign@teleport.com>
Date: Wed, 05 Oct 94 10:46:29 PDT
Raw View
In article <CwurA2.7v4@microsoft.com>, <jimad@microsoft.com> writes:

>  i = (0 * (g()));
>
> Thus the compiler must generate code "as if:"
>
>  g();
>  i = 0;
>
> If g() has no side effects, the above code would work "as if:"
>
>  i = 0;
>  g();
>

ok, folks, try this:

  ++i++;

take two asprin and read an ARM in the morning :-)

Craig





Author: fred@genesis.demon.co.uk (Lawrence Kirby)
Date: Mon, 19 Sep 1994 13:44:32 +0000
Raw View
In article <1994Sep18.224527.16592@nlm.nih.gov>
           rubenst%occs.nlm.nih.gov "Michael M. Rubenstein" writes:

>::      If this is not defined, then equally certainly i = (i = 2), 3; is
>:: also not defined. The consensus on comp.std.c has been that this is well
>:: defined. I believe it *should* be well defined. A DR looks to be in order.
>:: Note that, in this case, the compiler optimization you talk of is even more
>:: probable.

I believe the standard does not define adequately what a sequence point is.
In 5.1.2.3 it says:

"At certain specified points in the execution sequence called sequence points
all side effects of previous evaluations shall be complete and no side effects
of subsequent evaluations shall have taken place."

(What precisely is an 'execution sequence'? I don't believe the standard
defines it.)

So here sequence points are defined in terms of the 'execution sequence'.
However sequence points are *used* in the standard in terms of operators
and other constructs which form part of a source expression tree.

The confusion arises because there may be many possible execution sequences
for a particular expression tree e.g. take:

  b = a + (1, a++, 1);



Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Wed, 28 Sep 94 10:24:33 GMT
Raw View
Jos Horsmeier (jos@and.nl) wrote:
> In article <1994Sep21.150901.2145@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

> | [ ... ]I can find
> |no argument that f() must be evaluated before assigning in
> |
> | i = 0 & f(i++);

> Unlike the `&&' operator, both operands are evaluated when the
> `&' operator is applied. Optimization is not allowed to short
> cut this expression to: `i= 0' or whatever. The relevant parts
> of the Standard reads:

> ISO 5.1.2.3 Program execution

> The semantic descriptions in the International Standard describe the
> behavior of an abstract machine in which issues of optimization are
> irrelevant. [ ... ]

> In the abstract machine, all expressions are evaluated as specified
> by the semantics. An actual implementation need not evaluate part of
> an expression if it can deduce that its value is not used and that
> no needed side effects are produced (including any caused by calling
> a function or accessing a volatile object.)

> [ end quote ]

The semantics of & include the fact  that if one operand is 0, the result is
0.  Furthermore, I would interpret the clause on not evaluating as giving the
compiler permission to defer side-effects until they are needed.  Side-effects
are not needed until the next sequence point which is the end of the
statement (the sequence point after evaluating the arguments and function
designator doesn't count unless f must be called, so you can't use it to
prove that f must be called).

One should note that your argument would apply just as well to

 i = 0 * i++;

I certainly hope that there aren't too many people who would argue that this
is defined.

Having said that, let me add that I now believe that

 i = 0 & f(i++);

is defined based on ISO C 6.3

 Except as indicated by the syntax (35) or otherwise specified later ...
 the order of evaluation of subexpressions and the order in which
 side-effects take place are both unspecified.

The meaning of the first clause is not very clear, but the only way I can
make any sense of it is to assume that it means that the compiler may NOT
consider the semantics of an operation in deciding whether to evaluate its
operands (except, of course, for those operations like && where the standard
requires it).

Footnote 35 discusses the precedence of operators.  I can't see how this is
apropos to the above statement unless the syntax specifies evaluation.

Annex G supports this, listing as unspecified behavior

 The order in which expressions are evaluated -- in any order conforming
 to the precedence rules even in the presence of parentheses.

Of course the footnote and Annex are not part of the standard, so these
cannot be considered as proof.

I'm not entirely happy with this argument.  The statement in 6.3 is just not
clear enough.  I'd be much happier if a statement like that in Annex G were
part of the standard -- that's the only thing I can find which (in my opinion)
unambiguously requires that the values of all operands be determined before
an operation is performed (with specified exceptions, of course).

--
Mike Rubenstein




Author: jimad@microsoft.com (Jim Adcock)
Date: Wed, 28 Sep 1994 18:27:36 GMT
Raw View
In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
|As a simpler example of the problem, I gave
|
| i = 0 * g().
|
|Again, there is absolutely no question of whether g() must be called.  The
|standard requires that.  The question is WHEN MUST g() BE CALLED?  A compiler
|clearly may compile this as if it were
|
| g();
| i = 0;
|
|Is it also permitted to compile it as if it were
|
| i = 0;
| g();
|
|Common sense says no, but I cannot find anything in the standard to prohibit
|this.

I think you are trying to make this too complicated.  The "as if" rule
says the compiler can generate whatever code it likes, as long as the
constraints of the language is maintained.  What then are the constraints?

* (B) Function call: after evaluation of the args is a sequence point,
so for example f(i++) the side effect of incrementing i has occured before
f is even called.

* (3.3) To the extent that syntax specifies precedence, syntax specifies
order of side-effects.  The syntax does specify a precedence of effectively:

 i = (0 * (g()));

Thus the compiler must generate code "as if:"

 g();
 i = 0;

If g() has no side effects, the above code would work "as if:"

 i = 0;
 g();

which then would also be an acceptable implementation.  If the body
of g is in file scope, for example, an optimizing compiler might well discover
that g() has no side effect, thus no effect, and generate code equivalent
to:

 i = 0;

which again is all "as if"

 g();
 i = 0;

looking at:

 i = f(i++);

side effect on i must have occured before f called, thus this is
eqivalent to:

 arg = i;
 i++;
 i = (f(arg));


note that (3.3 note 34) doesn't apply in these cases since there *is* a
sequence point after evaluation of all args to a function.





Author: rhialto@mbfys.kun.nl (Olaf Seibert)
Date: Tue, 27 Sep 1994 23:19:20 GMT
Raw View
In <35uqrv$2tj@blackice.winternet.com> seebs@solutions.solon.com (Peter Seebach) writes:
>In article <1994Sep22.181142.7821@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>>Ajoy's example is quite appropriate to this discussion.  I don't see how you
>>can argue that
>
>> i = (i = 2), 3;
>
>Huh.  Well, I think that would end up being
>(i = (i = 2)), 3; by precedence, and that's assigning i twice between
>sequence points, no?

But this is basically the same (by leaving out the sequence point of the
comma operator and all that follows it) as

    i = (i = 2);

or

    i = i = 2;

and this being undefined is somehow less than satisfactory I think.


--
___ Olaf 'Rhialto' Seibert    rhialto@mbfys.kun.nl     Ooey-Gooey-Fluffy-Barfie
\X/ I'm not weird, I'm differently percepted. D787B44DFC896063 4CBB95A5BD1DAA96




Author: fred@genesis.demon.co.uk (Lawrence Kirby)
Date: Sat, 1 Oct 1994 21:00:35 +0000
Raw View
In article <CwurA2.7v4@microsoft.com> jimad@microsoft.com "Jim Adcock" writes:

>* (3.3) To the extent that syntax specifies precedence, syntax specifies
>order of side-effects.  The syntax does specify a precedence of effectively:
>
>        i = (0 * (g()));
>
>Thus the compiler must generate code "as if:"
>
>        g();
>        i = 0;

Not according to the standard. The standard says the the side effect of
updating i must happen after the previous and before the next sequence point.
This means it can happen either before or after the call to g, (the sequence
point defined by the () operator is not relevant here since the i is not
one of its operands).

Therefore:

   i = 0;
   g();

is a perfectly valid order of execution even if g() has side effects and even
if it modifies i. If it does modify i then i is modified twice between
sequence points and the behaviour is undefined.

>If g() has no side effects, the above code would work "as if:"
>
>        i = 0;
>        g();

the as-if rule is not really relevant here.

>looking at:
>
>        i = f(i++);
>
>side effect on i must have occured before f called, thus this is
>eqivalent to:
>
>        arg = i;
>        i++;
>        i = (f(arg));

No, these are not equivalent. According to the standard the first results in
undefined behaviour while the second one does not (assuming f() does not
modify i).

--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------




Author: fred@genesis.demon.co.uk (Lawrence Kirby)
Date: Sat, 1 Oct 1994 21:11:01 +0000
Raw View
In article <Cwr4vH.Krw@cs.utwente.nl> kenter@cs.utwente.nl "Arjan Kenter" writes:

>> >> |As a simpler example of the problem, I gave
>> >> |
>> >> |     i = 0 * g().
>> >> |
  .
  .
>Sorry to drop in a little late, but doesn't the following sample
>function g() show it is *not* permitted?
>
>  int g (void)
>    {
>      i++;
>      return 1; /* Whatever */
>    }

(Assuming the same i is being referenced in both cases). This simply
under the heading if an object eing modified twice between sequence points
and the behaviour is therefore undefined. Compare to:

j = (i*=2) + g();

which is undefined for the same reason (and which more clearly gives
different results depending on which operand to + is evaluated first).


>
>Of Michael's statement sequences, only ``g(); i = 0;'' preserves the semantics
> of
>the original ``i = 0 * g()''. The sequence ``i = 0; g();'' breaks it in the
> most

The semantics of ``i = 0 * g()'' are that it results in
undefined behaviour therefore ``g(); i = 0'' is not equivalent because it
does not preserve those semantics.

--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------




Author: kenter@cs.utwente.nl (Arjan Kenter)
Date: Mon, 26 Sep 1994 19:30:53 GMT
Raw View
In article <780423061snz@genesis.demon.co.uk>, fred@genesis.demon.co.uk (Lawrence Kirby) writes:
> In article <1994Sep23.152948.3496@nlm.nih.gov>
>            rubenst%occs.nlm.nih.gov "Michael M. Rubenstein" writes:
>
> (note I'm responding to comments from Mike and Jos here, so check quote
> levels carefully!)
>
> >Jos Horsmeier (jos@and.nl) wrote:
> >> In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov
> > (Michael M. Rubenstein) writes:
> >
> >> |As a simpler example of the problem, I gave
> >> |
> >> |     i = 0 * g().
> >> |
> >> |Again, there is absolutely no question of whether g() must be called.  The
> >> |standard requires that.  The question is WHEN MUST g() BE CALLED?  A
> > compiler
> >> |clearly may compile this as if it were
> >> |
> >> |     g();
> >> |     i = 0;
> >> |
> >> |Is it also permitted to compile it as if it were
> >> |
> >> |     i = 0;
> >> |     g();

Sorry to drop in a little late, but doesn't the following sample
function g() show it is *not* permitted?

  int g (void)
    {
      i++;
      return 1; /* Whatever */
    }

Of Michael's statement sequences, only ``g(); i = 0;'' preserves the semantics of
the original ``i = 0 * g()''. The sequence ``i = 0; g();'' breaks it in the most
obvious way, while the presence of sequence points does not make ``i = 0 * g()''
undefined with my function g(). I think this is what Lawrence was actually saying
in the rest of his article (which I cut :-) or was that in some other article :-( ,
but well, examples are there to clarify things, aren't they?

--
                                       ^^
ir. H.J.H.N. Kenter                   oo )         kenter@cs.utwente.nl
University of Twente                 =x=  \        tel. +31 53 893747
Tele-Informatics & Open Systems        |   \       tfx. +31 53 333815
P.O. Box 217   7500 AE Enschede       /|__  \
The Netherlands                      (____)_/

The best book on programming for the layman is "Alice in Wonderland";
but that's because it's the best book on anything for the layman.




Author: jos@and.nl (Jos Horsmeier)
Date: Tue, 27 Sep 1994 12:02:49 GMT
Raw View
In article <1994Sep23.152948.3496@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
|Jos Horsmeier (jos@and.nl) wrote:
|> In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
|
|> |As a simpler example of the problem, I gave
|> |
|> | i = 0 * g().
|> |
|> |Again, there is absolutely no question of whether g() must be called.  The
|> |standard requires that.  The question is WHEN MUST g() BE CALLED?  A compiler
|> |clearly may compile this as if it were
|> |
|> | g();
|> | i = 0;
|> |
|> |Is it also permitted to compile it as if it were
|> |
|> | i = 0;
|> | g();
|> |
|> |Common sense says no, but I cannot find anything in the standard to prohibit
|> |this.

|> Tricky ... Maybe the following fragments from the Standard apply here:

[ quotations deleted ... ]

|> There are a couple of sequence points in your example expression:
|
|> <s_1> i= 0*g(<s_2>);<s_3>
|
|> <s_1> is (obviously) a previous sequence point. <s_2> is a sequence
|> point after evaluation of the arguments of function g() (there aren't
|> any here, but that doesn't matter here,) and <s_3> terminates the
|> full expression. Evaluating function g() _after_ the assignment has
|> been performed, would lift the function call _behind_ sequence point
|> <s_3>, but function g() is part of the full expression terminated by
|> that sequence point. A compiler can't tell whether or not function
|> g() induces side effects (which must be completed somewhere before
|> sequence point <s_3>,) so my guess is, that the `previous sequence
|> point' described in section 6.3.16 (see above) has to be sequence
|> point <s_2>. And here's the tricky part: after evaluation of a
|> function's arguments, is it allowed to stick in other code before
|> the actual function call? Because that's the only possible location
|> left for the actual assignment to occur. But this would violate the
|> rule in Annex C -- there's just one sequence point there, not one
|> after evaluation of the arguments and one _before_ the function is
|> actually called ...

|But where does the standard say that g() must be evaluated before the
|assignment is done?  The sequence points in the evaluation of g() are
|only material when g() is called.
|
|Suppose we evaluate
|
| i = 0 * g();
|
|as
|
| i = 0;
| g();
|
|How does this violate the rules you have cited?  Certainly, this satisfies
|the semantics given for assignment -- that the value of the right hand side
|replace the value in the lvalue on the left.  Certainly at each sequence point
|all side-effects are complete.
|
|Based on some private discussions and some more work I've done on this, I'm
|now completely convinced that the standard INTENDS that this evaluation
|be illegal and that the standard INTENDS that operands be evaluated
|(except possibly for side effects not required by sequence points) before
|an operation is performed.

Like I wrote before: this is tricky. You've almost convinced me,
but I'm not dead yet ... ;-) I fully agree that the Standard is
very vague and cryptic about this topic, but I still sort of
refuse to agree that according to the Standard `i= 0*g();' would
be (semantically) equivalent to `i= 0; g();'

If we skip things like optimization here, ('cause there ruled out
by paragraph 5.1.2.3 Program execution:

`The semantic descriptions in the International Standard describe the
behavior of an abstract mahine in which issues of optimizations are
irrelevant.'

still then the last sentence of paragraph 6.3.16 Assignment operators:

`The order of evaluation of the operands is unspecified.'

tries to express what we all have in mind (or is part of our wishfull
thinking): the operands of the assignment expression `L= R;' are
evaluated in any order, but they _are_ evaluated.  The two operands of
a binary operator _have_ to be evaluated before the operator can be
applied. Since `g()' is part of one of the operands (and is an operand
in the multiplicative sub-expression itself), g() _has_ to be evaluated
before the assignment operator can be applied. There's no such thing as
`lazy evaluation' in the C programming language, i.e. if an operator is
applied, then it's operands are already evaluated. BTW, I'm not talking
about short wiring logical `and' or `or' operators here.

This very last sentence (read above) wouldn't make any sense at all if
those operands wouldn't have been evaluated before ...

But again, I fully agree that the Standard is quite inaccurate here ...

kind regards,

Jos aka jos@and.nl




Author: diamond@jrd.dec.com (Norman Diamond)
Date: 27 Sep 1994 05:33:31 GMT
Raw View
In article <Cwr4vH.Krw@cs.utwente.nl> kenter@cs.utwente.nl (Arjan Kenter) writes:
>>>>In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>>>>> As a simpler example of the problem, I gave
>>>>>      i = 0 * g().
>>>>> Again, there is absolutely no question of whether g() must be called.
>>>>> The standard requires that.  The question is WHEN MUST g() BE CALLED?
>>>>> A compiler clearly may compile this as if it were
>>>>>      g();
>>>>>      i = 0;
>>>>> Is it also permitted to compile it as if it were
>>>>>      i = 0;
>>>>>      g();

>Sorry to drop in a little late, but doesn't the following sample
>function g() show it is *not* permitted?
>  int g (void) {
>      i++;
>      return 1; /* Whatever */
>    }

It does not.

Had the standard contained language guaranteeing that the original statement
has a well defined effect, i.e. that there is maybe a mini sequence point of
some sort in the evaluation of each operator, then your example would show
that the latter form is not permitted.  If a technical corrigendum adds such
text to the standard, then again your example will show that.

However, as the standard is worded at present, your example depends on
unspecified behavior.  If evaluation occurs in a lucky order then the result
is defined, but if evaluation occurs in an unlucky order then the behavior
is undefined.[*]  The net effect is undefined behavior.[*]

When the standard does not guarantee some sort of non-undefined behavior
for a program, the program does not serve as an example that implementations
must be constrained by additional rules that are not stated by the standard.
It is merely an undefined program.

Of course, your example helps show that the standard doesn't say what the
committee intended to say, so it helps to show that the standard is
defective, and we can hope that a technical corrigendum will add what you want.

[* I think that the committee is already trying to make some change in
response to an earlier defect report, and it would have some effect on
your program.  Then your program will not be undefined, but the result can
still vary depending on the order of evaluation.  But I am not sure if the
committee will really even make that much of a change.]

>The best book on programming for the layman is "Alice in Wonderland";

That's only if you try to write strictly conforming programs.
Most of the time, it's not quite worth the effort.
--
 <<  If this were the company's opinion, I would not be allowed to post it.  >>
segmentation fault (california dumped)




Author: diamond@jrd.dec.com (Norman Diamond)
Date: 28 Sep 1994 01:39:38 GMT
Raw View
In article <CwsEsq.8oD@and.nl> jos@and.nl (Jos Horsmeier) writes:
>>>In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>>>> i = 0 * g().
>>>>Again, there is absolutely no question of whether g() must be called.  The
>>>>standard requires that.  The question is WHEN MUST g() BE CALLED?

>still then the last sentence of paragraph 6.3.16 Assignment operators:
>`The order of evaluation of the operands is unspecified.'

Bingo.

>the operands of the assignment expression `L= R;' are
>evaluated in any order, but they _are_ evaluated.

Everyone agrees on that.  Mr. Rubenstein even stated that before your
previous reply to it.

>The two operands of a binary operator _have_ to be evaluated before the
>operator can be applied.

This is true "most" of the time.
However, there is at least one instance where it is not true.
Mathematically in some cases it is not necessary, and the standard does not
state (as it could have done by fiat) an ordering requirement in those cases.

>There's no such thing as `lazy evaluation' in the C programming language,
>i.e. if an operator is applied, then it's operands are already evaluated.
>BTW, I'm not talking about short wiring logical `and' or `or' operators here.

I.e. you meant, "there's no such thing as 'lazy evaluation' except for the
&& and || and ?: operators, and except where there is no visible difference
(the as-if rule)."  OK, everyone agrees.  The call to g() cannot be omitted.
But previously, you tried to prohibit unfortunate orders as well as omissions,
and that is where we disagree.
--
 <<  If this were the company's opinion, I would not be allowed to post it.  >>
segmentation fault (california dumped)




Author: u9253513@uow.edu.au (Magao)
Date: 23 Sep 1994 16:10:58 +1000
Raw View
rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

>Clive D.W. Feather (clive@sco.com) wrote:

>Ajoy's example is quite appropriate to this discussion.  I don't see how you
>can argue that

> i = (i = 2), 3;

>is undefined yet argue that

> i = f(i++);

>is defined.

>The comma operator is a sequence point just as valid as the one before calling
>a function.  If the right side of the assignment must be evaluated before
>the assignment, then in both cases the sequence point comes between the
>two changes to i and the code is defined.

   Yes, the comma operator *is* a sequence point.

   i = (i = 2), 3;

should (as far as I know) be parsed as

   (i = (i = 2)), 3;

which means there is no sequence point between the two assignments to i. If
the following had been done it should be defined ...

   i = ((i = 2), 3);

   It's a problem of precedence ...

                                         _/_/_/_/
   _/_|  _/_|       _/_|     _/_/_/        _/_|     _/_/_/
  _/ _| _/ _|     _/  _|   _/            _/  _|   _/    _/
 _/  _|_/  _|   _/_/_/_|  _/   _/_/_/  _/_/_/_|  _/    _/
_/   _|    _| _/      _|  _/_/_/_/   _/      _|  _/_/_/

      Tim Delaney u9253513@wraith.cs.uow.edu.au




Author: jos@and.nl (Jos Horsmeier)
Date: Fri, 23 Sep 1994 10:27:33 GMT
Raw View
In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

|As a simpler example of the problem, I gave
|
| i = 0 * g().
|
|Again, there is absolutely no question of whether g() must be called.  The
|standard requires that.  The question is WHEN MUST g() BE CALLED?  A compiler
|clearly may compile this as if it were
|
| g();
| i = 0;
|
|Is it also permitted to compile it as if it were
|
| i = 0;
| g();
|
|Common sense says no, but I cannot find anything in the standard to prohibit
|this.

Tricky ... Maybe the following fragments from the Standard apply here:

5.1.2.3 Program execution
-------------------------

The semantic descriptions in this International Standard describe the
behavior of an abstract machine in which issues of program optimization
ar irrelevant.

At certain specified points in the exectution sequence called sequence
pointes, all side effects of previous calculations shall be complete
and no side effects of subsequent evaluations shall have taken place.

6.3.16 Assignment operators
---------------------------

Semantics

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

Annex C (informative) Sequence points
-------------------------------------

The following are the sequence points described in 5.1.2.3.

- The call to a function, after the arguments have been evaluated (6.3.2.2).

- The end of a full expression.

[ end quotes ]

There are a couple of sequence points in your example expression:

<s_1> i= 0*g(<s_2>);<s_3>

<s_1> is (obviously) a previous sequence point. <s_2> is a sequence
point after evaluation of the arguments of function g() (there aren't
any here, but that doesn't matter here,) and <s_3> terminates the
full expression. Evaluating function g() _after_ the assignment has
been performed, would lift the function call _behind_ sequence point
<s_3>, but function g() is part of the full expression terminated by
that sequence point. A compiler can't tell whether or not function
g() induces side effects (which must be completed somewhere before
sequence point <s_3>,) so my guess is, that the `previous sequence
point' described in section 6.3.16 (see above) has to be sequence
point <s_2>. And here's the tricky part: after evaluation of a
function's arguments, is it allowed to stick in other code before
the actual function call? Because that's the only possible location
left for the actual assignment to occur. But this would violate the
rule in Annex C -- there's just one sequence point there, not one
after evaluation of the arguments and one _before_ the function is
actually called ...

Hm, I hope this made some sense to you, 'cause I feel like a lawyer
by now ... ;-)

kind regards,

Jos aka jos@and.nl





Author: seebs@solutions.solon.com (Peter Seebach)
Date: 23 Sep 1994 15:05:35 GMT
Raw View
In article <1994Sep22.181142.7821@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>Ajoy's example is quite appropriate to this discussion.  I don't see how you
>can argue that

> i = (i = 2), 3;

Huh.  Well, I think that would end up being
(i = (i = 2)), 3; by precedence, and that's assigning i twice between
sequence points, no?

>is undefined yet argue that

> i = f(i++);

>is defined.

There's a sequence point before the call to f, and the call to f is part of
evaluating the value assigned to i, so i will be assigned at least one
function body after the increment.

>Mike Rubenstein

In the more general case, I think function calls are almost always going to
have side-effects.  Maybe the standard should require that function calls be
considered to have side effects regardless, just to nail this one down?

-s
--
Peter Seebach - seebs@solutions.solon.com  -- I need a job!  Hire me!
GAT(CS/M/P/SS) d-- H++ s+: !g p? !au a- w+++ v+++/* C++++ UB/V++++ P+ L b+++ !D
3+(NetBSD/Amiga) E- N+++ K !W--- M++/-- V- -po+ Y+ t 5++ jx R G'''' tv- B---
e++ u** h--- f+ r+++ !n x+++* --SeebS-- / The Laughing Prophet




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Fri, 23 Sep 94 14:19:18 GMT
Raw View
Magao (u9253513@uow.edu.au) wrote:
> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

> >Clive D.W. Feather (clive@sco.com) wrote:

> >Ajoy's example is quite appropriate to this discussion.  I don't see how you
> >can argue that

> > i = (i = 2), 3;

> >is undefined yet argue that

> > i = f(i++);

> >is defined.

> >The comma operator is a sequence point just as valid as the one before calling
> >a function.  If the right side of the assignment must be evaluated before
> >the assignment, then in both cases the sequence point comes between the
> >two changes to i and the code is defined.

>    Yes, the comma operator *is* a sequence point.

>    i = (i = 2), 3;

> should (as far as I know) be parsed as

>    (i = (i = 2)), 3;

> which means there is no sequence point between the two assignments to i. If
> the following had been done it should be defined ...

>    i = ((i = 2), 3);

>    It's a problem of precedence ...

Whoops.  Right of course.  I got too tied up in the idea behind this
discussion and missed this.

 i = (i = 2), 3;

is clearly undefined since the comma operator does not come between the
two changes to i.

--
Mike Rubenstein




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Fri, 23 Sep 94 15:29:48 GMT
Raw View
Jos Horsmeier (jos@and.nl) wrote:
> In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

> |As a simpler example of the problem, I gave
> |
> | i = 0 * g().
> |
> |Again, there is absolutely no question of whether g() must be called.  The
> |standard requires that.  The question is WHEN MUST g() BE CALLED?  A compiler
> |clearly may compile this as if it were
> |
> | g();
> | i = 0;
> |
> |Is it also permitted to compile it as if it were
> |
> | i = 0;
> | g();
> |
> |Common sense says no, but I cannot find anything in the standard to prohibit
> |this.

> Tricky ... Maybe the following fragments from the Standard apply here:

> 5.1.2.3 Program execution
> -------------------------

> The semantic descriptions in this International Standard describe the
> behavior of an abstract machine in which issues of program optimization
> ar irrelevant.

> At certain specified points in the exectution sequence called sequence
> pointes, all side effects of previous calculations shall be complete
> and no side effects of subsequent evaluations shall have taken place.

> 6.3.16 Assignment operators
> ---------------------------

> Semantics

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

> Annex C (informative) Sequence points
> -------------------------------------

> The following are the sequence points described in 5.1.2.3.

> - The call to a function, after the arguments have been evaluated (6.3.2.2).

> - The end of a full expression.

> [ end quotes ]

> There are a couple of sequence points in your example expression:

> <s_1> i= 0*g(<s_2>);<s_3>

> <s_1> is (obviously) a previous sequence point. <s_2> is a sequence
> point after evaluation of the arguments of function g() (there aren't
> any here, but that doesn't matter here,) and <s_3> terminates the
> full expression. Evaluating function g() _after_ the assignment has
> been performed, would lift the function call _behind_ sequence point
> <s_3>, but function g() is part of the full expression terminated by
> that sequence point. A compiler can't tell whether or not function
> g() induces side effects (which must be completed somewhere before
> sequence point <s_3>,) so my guess is, that the `previous sequence
> point' described in section 6.3.16 (see above) has to be sequence
> point <s_2>. And here's the tricky part: after evaluation of a
> function's arguments, is it allowed to stick in other code before
> the actual function call? Because that's the only possible location
> left for the actual assignment to occur. But this would violate the
> rule in Annex C -- there's just one sequence point there, not one
> after evaluation of the arguments and one _before_ the function is
> actually called ...

> Hm, I hope this made some sense to you, 'cause I feel like a lawyer
> by now ... ;-)

But where does the standard say that g() must be evaluated before the
assignment is done?  The sequence points in the evaluation of g() are
only material when g() is called.

Suppose we evaluate

 i = 0 * g();

as

 i = 0;
 g();

How does this violate the rules you have cited?  Certainly, this satisfies
the semantics given for assignment -- that the value of the right hand side
replace the value in the lvalue on the left.  Certainly at each sequence point
all side-effects are complete.

Based on some private discussions and some more work I've done on this, I'm
now completely convinced that the standard INTENDS that this evaluation
be illegal and that the standard INTENDS that operands be evaluated
(except possibly for side effects not required by sequence points) before
an operation is performed.

I come to this conclusion because if we assume otherwise, the language is
unusable.  Expressions like

 i = a * i;

become undefined if a is 0.  That is, of course, ridiculous.

But I still can't find this in the standard.

You, and several others, seem to be hanging your hats on the implication that
the right hand side of an assignment must be evaluated before the value
is stored.  But this is clearly not true.  The semantics of evaluating i++
include incrementing i.  Still

 i = i++;

is not defined because the standard clearly permits the side-effects to be
deferred.  We know that side-effects of the right hand side need not
have taken place at the time of the assignment.

But, according to 5.1.2.3 calling a function which performs side-effects is
itself a side-effect.  If the value of the function is not immediately needed,
why can calling the function not be deferred?  Why can some side-effects
be deferred and others not?

Is it because there are sequence points internal to the process of evaluating
the function?  Perhaps, but I can't find that in the standard.  If we move
evaluation of the function we move all of those sequence points also.

--
Mike Rubenstein




Author: barrett@lucy.ee.und.ac.za (Alan Barrett)
Date: 23 Sep 1994 18:44:07 +0200
Raw View
[ Followup-To: comp.std.c ]

In article <1994Sep23.152948.3496@nlm.nih.gov>,
rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
> You, and several others, seem to be hanging your hats on the
> implication that the right hand side of an assignment must be
> evaluated before the value is stored.  But this is clearly not true.

I argue that it *is* true.

Evaluating an expression produces a value, and may also produce side
effects.  The value of the expression becomes available when the
expression has been evaluated.  The side effects take place at some time
between the relevant previous and next sequence points.

The semantics of the assignment operator in the abstract machine require
that the modifiable lvalue on the left hand side must be evaluated,
and that the rvalue on the right hand side must be evaluated.  The
assignment operator produces a resulting value (the same as the value
from the right hand operand, possibly after type conversion), and has a
side-effect (storing the resulting value into the object denoted by the
left hand operand).

It is clear that the value from the right hand side cannot be stored
anywhere before the value from the right hand side is available.  This
follows from the everyday meaning of the terms, even if it not spelled
out in the Standard.  Thus, the right hand side of an assignment must
be evaluated before the value is stored.  (Any side effects that result
from the evaluation of the right hand side may of course be deferred
until after the value is stored, or may be performed concurrently with
the storing of the value, subject to the rules about sequence points.)

[I forget who said this:]
> > The semantics of evaluating i++ include incrementing i.  Still
> >
> >  i = i++;
> >
> > is not defined because the standard clearly permits the side-effects
> > to be deferred.  We know that side-effects of the right hand side
> > need not have taken place at the time of the assignment.

> But, according to 5.1.2.3 calling a function which performs
> side-effects is itself a side-effect.

My copy of the Standard does not say that.  The words "including any
[side effects] caused by calling a function [...]" suggest that calling
a function might *cause* side effects (presumably resulting from
whatever happens inside the function) and that such side effects must
be considered, but does not suggest that the act of making the call is
*itself* a side effect.

> If the value of the function is not immediately needed,
> why can calling the function not be deferred?

*If* the value is not immediately needed, then the call can be deferred.
I argue that, in your example, the value of the function *is* needed
before the assignment.

In your example "i = 0 * g(i++)", the assignment operator cannot store
the result into the variable "i" before the value of "0 * g(i++)" is
available.  That result is not available before the multiplication
has been performed.  The abstract machine is not permitted to do any
optimisation, so it cannot perform the multiplication before the result
from "g(i++)" is available.  That result is not available before the
function g() returns.  The function cannot return before it has been
called.  The function cannot be called before the result of "i++" is
available.

There is a sequence point between the evaluation of "i++" and the
call of function g().  This is before the other attempt to modify the
variable "i", so there is no violation of the rule about multiple
modifications between sequence points.

> Why can some side-effects be deferred and others not?

All side-effects can be deferred, but only until the next sequence point.

--apb (Alan Barrett)




Author: hellwig@rahul.net (Oliver Hellwig)
Date: Fri, 23 Sep 1994 16:58:13 GMT
Raw View
In article <35pfb2$ovq@euas20.eua.ericsson.se> eedbeh@eed.eua.ericsson.se writes:
>In article 17779@nlm.nih.gov, rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>> Eric Laney (cis) (elaney@zeppoacomp.usf.edu) wrote:
>
>> > In article <1994Sep18.170758.12030@nlm.nih.gov>, rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:
>
>> > |> Is
>> > |>
>> > |>  i = f(i++);
>> > |>
>> > |> defined?
>
>(...)
>
>> > Now, the way I understand it, you could write the above line as follows with no difference in the resulting compiled code:
>
>> > i = f(i);
>> > i++;
>
>> No.  The incrementation of i must be done before the next sequence point.
>> It may be done immediately or deferred, but here there is a sequence point
>> just before f is called.  i must be incremented before f is called.  If i
>> is a global and f uses it, f must use the incremented value.
>
>Sounds good, but what does my stupid compiler do? (gcc 2.6.x?)
>The following code prints out 42, that means i is not incremented before f is called!

Yes, technically speaking, i is incremented before the function call.
However, the value of i is pushed on the stack before it is incremented.
This is how it would look in a typical assembly language (details
will vary with each architecture):

push   i
inc    i
call   f

So, the value 42 is passed to function f, but i is incremented to 43
before function f is actually called.

>
>
>#include <stdio.h>
>
>int i = 42;
>
>void f (int i)
>{
>   printf ("%d\n", i);
>}
>
>void main (void)
>{
>   f (i++);
>}
>
--
Oliver Hellwig
ohellwig@scu.edu
Santa Clara University, CA




Author: fred@genesis.demon.co.uk (Lawrence Kirby)
Date: Sat, 24 Sep 1994 16:11:01 +0000
Raw View
In article <1994Sep23.152948.3496@nlm.nih.gov>
           rubenst%occs.nlm.nih.gov "Michael M. Rubenstein" writes:

(note I'm responding to comments from Mike and Jos here, so check quote
levels carefully!)

>Jos Horsmeier (jos@and.nl) wrote:
>> In article <1994Sep22.170913.5623@nlm.nih.gov> rubenst%occs.nlm.nih.gov
> (Michael M. Rubenstein) writes:
>
>> |As a simpler example of the problem, I gave
>> |
>> |     i = 0 * g().
>> |
>> |Again, there is absolutely no question of whether g() must be called.  The
>> |standard requires that.  The question is WHEN MUST g() BE CALLED?  A
> compiler
>> |clearly may compile this as if it were
>> |
>> |     g();
>> |     i = 0;
>> |
>> |Is it also permitted to compile it as if it were
>> |
>> |     i = 0;
>> |     g();

Indeed, this is essentially the point I argued a little while back in the
thread.

>> |Common sense says no, but I cannot find anything in the standard to prohibit
>> |this.

6.3.16 Assignment operators

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

clearly permits the 2nd ordering since the only difference is when the
side effect of the assignment happens. It is constrained by the sequence
points at the end of the expression and the end of the previous expression
but it is not constrained by the sequence point defined by the function call
(because it is no part of the function call). This last part is perhaps
what needs to be expanded so consider:

 i + (0, i++, 1)

Clearly in this example the 2 i's are not separated by sequence points
(the expression is undefined because it depends on whether i or i++ is
evaluated first). Sequence points only constrain the arguments to the
operators that define them.

>> There are a couple of sequence points in your example expression:
>
>> <s_1> i= 0*g(<s_2>);<s_3>
>
>> <s_1> is (obviously) a previous sequence point. <s_2> is a sequence
>> point after evaluation of the arguments of function g() (there aren't
>> any here, but that doesn't matter here,) and <s_3> terminates the
>> full expression. Evaluating function g() _after_ the assignment has
>> been performed, would lift the function call _behind_ sequence point
>> <s_3>,

No it doesn't. You have presented one possible order of evaluation and
are trying to make others conform unnecessarily to it. The relative
positions of sequence points are not fixed, for example in

   (a && b) + (c && d)

either the left or right operands of + may be evaluated first. This is
another example which shows that sequence points only constrain the (direct)
operands of the operators that define them.

In your example the only effect of <s_2> is that any side effects in
expression g (i.e. the function identifier which in this case has none)
are complete before the function is called. If there were any arguments
side effects in those would have to be completed also.

>> but function g() is part of the full expression terminated by
>> that sequence point. A compiler can't tell whether or not function
>> g() induces side effects (which must be completed somewhere before
>> sequence point <s_3>,) so my guess is, that the `previous sequence
>> point' described in section 6.3.16 (see above) has to be sequence
>> point <s_2>.

No, <s_2> does not constrain the assignment in this expression since the
assignment is not part of an operand of the function operator.
  .
  .

 < back to the original expression>

>Based on some private discussions and some more work I've done on this, I'm
>now completely convinced that the standard INTENDS that this evaluation
>be illegal

Agreed.

>and that the standard INTENDS that operands be evaluated
>(except possibly for side effects not required by sequence points) before
>an operation is performed.

What is a side effect not required by a sequence point? I believe the
intention of the standard is to make the value of an expression independent
of the evaluation order and to this end it makes rules to ensure that
expressions which do depend on evaluation order are undefined, and it
creates the concept of sequence points for those operators which do
require a precise evaluation order.

>I come to this conclusion because if we assume otherwise, the language is
>unusable.  Expressions like
>
>        i = a * i;
>
>become undefined if a is 0.  That is, of course, ridiculous.

No, this is covered by 6.3 Expressions

"Furthermore, the prior value shall be accessed only to determine the
value to be stored."

This implies that any instance of i on the RHS of an assignment must
obtain the value that i had before the side effect of the assignment
changes it. However there is no equivalent statement which ever allows
a variable to be modified twice between sequence points.

>You, and several others, seem to be hanging your hats on the implication that
>the right hand side of an assignment must be evaluated before the value
>is stored.  But this is clearly not true.  The semantics of evaluating i++
>include incrementing i.  Still
>
>        i = i++;

I agree that you can't show from the standard that the RHS must be fully
evaluated before the result is stored in the LHS. However I don't think
you can prove anything either way by considering side effects in the
RHS. It would be entirely possible to create a rule which states that the
RHS must be fully evaluated barring side effects before the value is
assigned to the LHS.

>But, according to 5.1.2.3 calling a function which performs side-effects is
>itself a side-effect.  If the value of the function is not immediately needed,
>why can calling the function not be deferred?  Why can some side-effects
>be deferred and others not?

It is probably simplest to consider standard library functions here for which
the standard defines no sequence points. I agree that the function call
can happen anywhere between enclosing sequence points. Here the sequence
point defined by the () operator moves with the function call, it doesn't
constrain its position.

--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Wed, 21 Sep 94 01:33:04 GMT
Raw View
Philip Staite (pstaite@zool.rchland.ibm.com) wrote:
> What if you replace:

>     i = f( i++ );

> by the equivalent function-call syntax?

>     i.operator =( f( i.operator ++( 0 ) ) );

> assuming the operator ++(int) is post increment and operator ++(void) is pre increment.  Looks well defined to me.

I don't see it.  In fact, it seems like a better argument for it not being
defined.  By that reasoning

 i = 0 + i++;

would be well defined since it is equivalent to

 i.operator=(0.operator+(i.operator++(0)));

After all, why should it be well defined for f, but not for 0.operator+?
Unless the standards committee has gone off the deep end, this will not be
defined.

The problem is that there is no sequence point after evaluating the arguments
and before calling an operator on a basic type (except, of course, for
operators which are defined as having a sequence point).  If there is no
sequence point before int::operator= is "called", then f need not be
completely evaluated; if f has not yet been called, its arguments might
not be completely evaluated.

Note: I'm aware, as I'm sure is Philip, that you can't use this notation
for basic types -- our arguments are conceptual.


--
Mike Rubenstein




Author: clive@sco.com (Clive D.W. Feather)
Date: Thu, 22 Sep 1994 12:28:09 GMT
Raw View
In article <1994Sep21.123223.27279@nlm.nih.gov>,
Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
> Does assignment require that the evaluation of the right hand side be
> complete?  Obviously not, else i = i++ would be defined.  Does it require
> that the right hand side be complete except for side effects?  I believe
> that it should, but I cannot find it in the standard.

I believe that the only sensible reading of the Standard is that the
right hand side must be evaluated to the point where its value can be
determined. This obviously excludes side effects.

> Let's consider
> i = 0 & ((i = 2), 3);
> or
> i = ((i = 2), 3) & 0;
> Is this defined?  Is the compiler permitted to recognize that
> that 0 & A == 0 == A & 0 and assign 0 to i before it starts evaluation of
> the other operand of &?

Both those expressions are equivalent to "i = 0;" in their effects. If
the & is not short-circuited, then the assignment of 2 occurs before a
sequence point and the assignment of 0 after it. If it is short circuited
(which I don't believe is permitted), then why evaluate the other
operand at all ?

> This is not trivial. Consider
>  i = 2;
>  i = 0 * g(i);
> Is the compiler permitted to store 0 in i before evaluating g(i)?  I don't
> believe that it should be, but where does the standard say that?

I don't believe so. Firstly, the Standard explicitly states that evaluation
of the operands of && || and ?: does not always happen, and *doesn't* say it
for &, so I don't think short circuiting is allowed. Secondly, for many
operators (and & in particular), the usual arithmetic conversions apply
*to both operands*. It appears ridiculous for:

    i = 0 & f ();

to be allowed not to call f, but for:

    i = 0L & f ();

to be required to call it.

--
Clive D.W. Feather     | Santa Cruz Operation    | If you lie to the compiler,
clive@sco.com          | Croxley Centre          | it will get its revenge.
Phone: +44 1923 813541 | Hatters Lane, Watford   |   - Henry Spencer
Fax:   +44 1923 813811 | WD1 8YN, United Kingdom | <= NOTE: NEW PHONE NUMBERS




Author: jaf3@ritz.cec.wustl.edu (John Andrew Fingerhut)
Date: 22 Sep 1994 12:26:34 -0500
Raw View
In article <1994Sep21.220038.22066@sq.sq.com>,
Mark Brader <msb@sq.sq.com> wrote:
>> > i = 0 & f(i++);
>> >Is this defined?  Is the compiler permitted to notice that 0 & anything
>> >is 0 and assign 0 to i before evaluating f()?
>>
>> No and No.
>
>*Yes* and no.  It's defined for the same reason that i = f(i++) is.
>Followups are directed to comp.std.c for the same reason as before.
>--
>Mark Brader, msb@sq.com, SoftQuad Inc., Toronto
>#define MSB(type)       (~(((unsigned type)-1)>>1))
>
>This article is in the public domain.

Based on your previous posting, there shouldn't even *be* a comp.std.c++!
This discussion definately belongs in comp.std.c++ because it effects the
upcomming standard for C++.  Be reasonable when you change the follow-ups!
People who are following this discussion may only be reading one of the
four news groups that it is cross-posted to.  I, for example, don't read
comp.std.c and I am very interested in this discussion because it does
apply to C++

Stephen Gevers
sg3235@shelob.sbc.com




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Thu, 22 Sep 94 17:09:13 GMT
Raw View
Clive D.W. Feather (clive@sco.com) wrote:
> In article <1994Sep21.123223.27279@nlm.nih.gov>,
> Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
> > Does assignment require that the evaluation of the right hand side be
> > complete?  Obviously not, else i = i++ would be defined.  Does it require
> > that the right hand side be complete except for side effects?  I believe
> > that it should, but I cannot find it in the standard.

> I believe that the only sensible reading of the Standard is that the
> right hand side must be evaluated to the point where its value can be
> determined. This obviously excludes side effects.

> > Let's consider
> > i = 0 & ((i = 2), 3);
> > or
> > i = ((i = 2), 3) & 0;
> > Is this defined?  Is the compiler permitted to recognize that
> > that 0 & A == 0 == A & 0 and assign 0 to i before it starts evaluation of
> > the other operand of &?

> Both those expressions are equivalent to "i = 0;" in their effects. If
> the & is not short-circuited, then the assignment of 2 occurs before a
> sequence point and the assignment of 0 after it. If it is short circuited
> (which I don't believe is permitted), then why evaluate the other
> operand at all ?

> > This is not trivial. Consider
> >  i = 2;
> >  i = 0 * g(i);
> > Is the compiler permitted to store 0 in i before evaluating g(i)?  I don't
> > believe that it should be, but where does the standard say that?

> I don't believe so. Firstly, the Standard explicitly states that evaluation
> of the operands of && || and ?: does not always happen, and *doesn't* say it
> for &, so I don't think short circuiting is allowed. Secondly, for many
> operators (and & in particular), the usual arithmetic conversions apply
> *to both operands*. It appears ridiculous for:

>     i = 0 & f ();

> to be allowed not to call f, but for:

>     i = 0L & f ();

> to be required to call it.

Please reread my post.  I did not suggest that the compiler could
omit the call of g().

The question is not whether f() must be called.  The standard is very
clear that it must be called if it is an operand of & (subject, of course,
to the condition the compiler can omit it if it knows this will make no
difference).

Note that making the other operand a long has no effect on this since
the conversion of the result is clearly not a necessary side-effect.

However, few if any compilers will elide the call to f().

The question is must f() be called before the 0 is stored in i?

A lot of people seem to be looking at the wrong question.  Let me
clarify a bit.

In the expression

 i = f(i++);

If this is defined, there is no question that f() must be called and that
i must be incremented before f is called.  The standard does not permit
defering side-effects of argument evaluation past the point at which the
function is called.  The question I pose is whether f() must be called before
its value is assigned to i BY THE ASSIGNMENT OPERATOR.

It seems obvious that f() must be called before its value is used, but
is this necessarily true?

As a simpler example of the problem, I gave

 i = 0 * g().

Again, there is absolutely no question of whether g() must be called.  The
standard requires that.  The question is WHEN MUST g() BE CALLED?  A compiler
clearly may compile this as if it were

 g();
 i = 0;

Is it also permitted to compile it as if it were

 i = 0;
 g();

Common sense says no, but I cannot find anything in the standard to prohibit
this.

--
Mike Rubenstein




Author: newcombe@aa.csc.peachnet.edu (Dan Newcombe)
Date: Thu, 22 Sep 1994 16:13:12 UNDEFINED
Raw View
In article <35pfb2$ovq@euas20.eua.ericsson.se> eedbeh@eed.eua.ericsson.se (Stefan Behrens) writes:
>Sounds good, but what does my stupid compiler do? (gcc 2.6.x?)
>The following code prints out 42, that means i is not incremented before f is called!
>#include <stdio.h>
>int i = 42;
>void f (int i)
>{
>   printf ("%d\n", i);
>}
>void main (void)
>{
>   f (i++);
>}

main is an int.
Anyway...  i is given to f and then incremented.  So f is handed the old value
of i.  To get it to print 43, it'd be ++i - since that would do the increment
and then hand off the value, not vice versa.






--
Dan Newcombe                    newcombe@aa.csc.peachnet.edu
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
"And the man in the mirror has sad eyes."       -Marillion




Author: danpop@cernapo.cern.ch (Dan Pop)
Date: Thu, 22 Sep 1994 22:36:30 GMT
Raw View
In <1994Sep22.181142.7821@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

>Ajoy's example is quite appropriate to this discussion.  I don't see how you
>can argue that
>
> i = (i = 2), 3;
>
>is undefined yet argue that
>
> i = f(i++);
>
>is defined.
>
>The comma operator is a sequence point just as valid as the one before calling
>a function.  If the right side of the assignment must be evaluated before
>the assignment, then in both cases the sequence point comes between the
>two changes to i and the code is defined.

Nope. In the first case you have two assignments _before_ the comma.
Since both assignments are to the same variable, you have a clear case
of undefined behaviour.
>
>On the other hand, if the compiler is permitted to notice that the RHS
>of Ajoy's example is always 3 and assign 3 to i before evaluating the
>right hand side, then Ajoy's example is undefined.

The compiler could assign 3 to i only as a result of undefined behaviour.
But most compilers will assign 2 to i, even if they do this twice
between consecutive sequence points :-)

Dan
--
Dan Pop
CERN, CN Division
Email: danpop@cernapo.cern.ch
Mail:  CERN - PPE, Bat. 31 R-004, CH-1211 Geneve 23, Switzerland




Author: jos@and.nl (Jos Horsmeier)
Date: Thu, 22 Sep 1994 14:24:33 GMT
Raw View
In article <1994Sep21.150901.2145@nlm.nih.gov> rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

| [ ... ]I can find
|no argument that f() must be evaluated before assigning in
|
| i = 0 & f(i++);

Unlike the `&&' operator, both operands are evaluated when the
`&' operator is applied. Optimization is not allowed to short
cut this expression to: `i= 0' or whatever. The relevant parts
of the Standard reads:

ISO 5.1.2.3 Program execution

The semantic descriptions in the International Standard describe the
behavior of an abstract machine in which issues of optimization are
irrelevant. [ ... ]

In the abstract machine, all expressions are evaluated as specified
by the semantics. An actual implementation need not evaluate part of
an expression if it can deduce that its value is not used and that
no needed side effects are produced (including any caused by calling
a function or accessing a volatile object.)

[ end quote ]

kind regards,

Jos aka jos@and.nl




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Thu, 22 Sep 94 18:11:42 GMT
Raw View
Clive D.W. Feather (clive@sco.com) wrote:
> In article <1994Sep18.224527.16592@nlm.nih.gov>,
> Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
> > This came from some private discussions with Ajoy Krishnan T
> > (ajoyk%hss@lando.hns.com).  Ajoy has been having trouble getting postings out.
> > In a private message he said:
> >::  If this is not defined, then equally certainly i = (i = 2), 3; is
> >:: also not defined. The consensus on comp.std.c has been that this is well
> >:: defined. I believe it *should* be well defined.

> I have never seen discussion on that expression, so I don't believe his
> "consensus". I am certain that the expression is undefined, as it
> assigns twice to i between sequence points.

> Note that, on parallel hardware, even assigning the same value twice
> might cause hardware faults.

Ajoy's example is quite appropriate to this discussion.  I don't see how you
can argue that

 i = (i = 2), 3;

is undefined yet argue that

 i = f(i++);

is defined.

The comma operator is a sequence point just as valid as the one before calling
a function.  If the right side of the assignment must be evaluated before
the assignment, then in both cases the sequence point comes between the
two changes to i and the code is defined.

On the other hand, if the compiler is permitted to notice that the RHS
of Ajoy's example is always 3 and assign 3 to i before evaluating the
right hand side, then Ajoy's example is undefined.

But if one takes the view that the compiler may determine the value of
the RHS without evaluating it, why would that not also apply to the
function call?  Certainly there are situations (rare, to be sure, and
not very useful to exploit) in which the compiler can determine
the value a function will return before calling the function.

--
Mike Rubenstein




Author: raspar@ccmail.monsanto.com
Date: Tue, 20 Sep 1994 16:17:25 GMT
Raw View
In article <CwD6r5.9o3@scone.london.sco.com>, <clive@sco.com> writes:

> In article <1994Sep18.224527.16592@nlm.nih.gov>,
> Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
> > This came from some private discussions with Ajoy Krishnan T
> > (ajoyk%hss@lando.hns.com).  Ajoy has been having trouble getting postings
out.
> > In a private message he said:
> >::  If this is not defined, then equally certainly i = (i = 2), 3; is
> >:: also not defined. The consensus on comp.std.c has been that this is well
> >:: defined. I believe it *should* be well defined.
>
> I have never seen discussion on that expression, so I don't believe his
> "consensus". I am certain that the expression is undefined, as it
> assigns twice to i between sequence points.
>
> Note that, on parallel hardware, even assigning the same value twice
> might cause hardware faults.

"between sequence points"?  Isn't the comma operator the sequence point,i.e.:
i=2;
i=3;

Rodney




Author: jason@cygnus.com (Jason Merrill)
Date: Wed, 21 Sep 1994 02:16:49 GMT
Raw View
>>>>> Eric Laney (cis) <elaney@zeppoacomp.usf.edu> writes:

> In article <1994Sep18.170758.12030@nlm.nih.gov>, rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

> |> Is
> |>
> |>  i = f(i++);
> |>
> |> defined?

> To make sure I understand the question correctly, let me say this:

> i is a defined integer
> f is a defined function which accepts a single integer parameter and returns an integer.

> Now, the way I understand it, you could write the above line as follows with no difference in the resulting compiled code:

> i = f(i);
> i++;

Nope.  It can be rewritten as

int postincrement (int& i) { int save = i; i = i+1; return save; }

...

i = f(postincrement(i));

The increment takes place before the call to f, but is not reflected in the
value that is passed to f.  Does that make sense?

Jason




Author: pstaite@zool.rchland.ibm.com (Philip Staite)
Date: 19 Sep 1994 19:26:44 GMT
Raw View
What if you replace:

    i = f( i++ );

by the equivalent function-call syntax?

    i.operator =( f( i.operator ++( 0 ) ) );

assuming the operator ++(int) is post increment and operator ++(void) is pre increment.  Looks well defined to me.

--

Phil Staite, (507) 253-2529
internet: pstaite@vnet.ibm.com  internal: pstaite@rchland




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Wed, 21 Sep 94 01:21:01 GMT
Raw View
Eric Laney (cis) (elaney@zeppoacomp.usf.edu) wrote:

> In article <1994Sep18.170758.12030@nlm.nih.gov>, rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

> |> Is
> |>
> |>  i = f(i++);
> |>
> |> defined?

> To make sure I understand the question correctly, let me say this:

> i is a defined integer
> f is a defined function which accepts a single integer parameter and returns an integer.

> Now, the way I understand it, you could write the above line as follows with no difference in the resulting compiled code:

> i = f(i);
> i++;

No.  The incrementation of i must be done before the next sequence point.
It may be done immediately or deferred, but here there is a sequence point
just before f is called.  i must be incremented before f is called.  If i
is a global and f uses it, f must use the incremented value.

> The postincrement by definition is executed after the current line of source is complete.

Where did you get that definition?  It is not correct.

> To contrast, if we wrote the line of code like this:

> i = f(++i);

> It would be the same as writing this:

> i++;
> i = f(i);

Here the effect is the same, but it's not that simple.  For example

 i++;
 i = i + 2;

is well defined.  i will have 3 added to its value.  However,

 i = ++i + 2;

is different; it gives undefined behavior.

My question applies equally to

 i = f(i++);

and

 i = f(++i);

I just chose one out of the many possible pieces of code to show this problem.
The question is whether there is a sequence point between the evaluation of
i++ and the assignment.

--
Mike Rubenstein




Author: elaney@zeppoacomp.usf.edu (Eric Laney (cis))
Date: 21 Sep 1994 00:53:04 GMT
Raw View
In article <1994Sep18.170758.12030@nlm.nih.gov>, rubenst%occs.nlm.nih.gov (Michael M. Rubenstein) writes:

|> Is
|>
|>  i = f(i++);
|>
|> defined?

To make sure I understand the question correctly, let me say this:

i is a defined integer
f is a defined function which accepts a single integer parameter and returns an integer.

Now, the way I understand it, you could write the above line as follows with no difference in the resulting compiled code:

i = f(i);
i++;

The postincrement by definition is executed after the current line of source is complete.  To contrast, if we wrote the line of code like this:

i = f(++i);

It would be the same as writing this:

i++;
i = f(i);

Or am I blowing smoke most foul??  =B)

Eric A. Laney




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Sun, 18 Sep 94 22:45:27 GMT
Raw View
I wrote:
> Well, its back.  But this time with a twist.

> Is

>  i = f(i++);

> defined?

> There is a sequence point before the call to f() and after the
> evaluation of i++.  But does it come between the evaluation of i++ and
> the assignment?

> Recently, I told someone it is undefined because I cannot find anything in
> the standard that says that f() must actually be called before doing
> the assignment.  Theoretically, it seems to me that if the compiler could
> determine by some magic what f() returns, it could do the assignment before
> completing evaluation of the arguments.

> However, even while making it I felt that this argument is a little
> outrageous.

This came from some private discussions with Ajoy Krishnan T
(ajoyk%hss@lando.hns.com).  Ajoy has been having trouble getting postings out.
In a private message he said:

::  If this is not defined, then equally certainly i = (i = 2), 3; is
:: also not defined. The consensus on comp.std.c has been that this is well
:: defined. I believe it *should* be well defined. A DR looks to be in order.
:: Note that, in this case, the compiler optimization you talk of is even more
:: probable.
::
::  As per the abstract machine semantics, an assignment does require
:: that its RHS be evaluated before the actual assignment (anything else is
:: an optimization). If the RHS has no sequence points, then the actual
:: assignment can occur in parallel with RHS evaluation. If the RHS has a sequence
:: point, then the actual assignment can occur only in parallel with that
:: part of the RHS after the last sequence point in the RHS side.
::
::  I would actually have posted the above para to comp.std.c in
:: response to your question; since my newsfeed access looks blocked, I'm
:: sending it by e-mail - feel free to quote it in any postings.
::

I do not agree with his position (except that a DR may be in order) and
have not seen previous discussion of the case he says should be well defined.
On the other hand, I don't see his explanation as being any more outrageous
than mine. :-)




Author: darcy@druid.com (D'Arcy J.M. Cain)
Date: Mon, 19 Sep 1994 00:22:44 GMT
Raw View
Michael M. Rubenstein (rubenst%occs.nlm.nih.gov) wrote:
: Well, its back.  But this time with a twist.

Really?  Looks the same to me.

: Is
:  i = f(i++);
: defined?

No.

: There is a sequence point before the call to f() and after the
: evaluation of i++.  But does it come between the evaluation of i++ and
: the assignment?

The question is when does i get incremented.  This is a different question
than what is the value of i++.  Here are three possibilities using the
assembler of some unnamed processor.


mov acc, i
inc acc
push acc
call f
mov i, acc
pop acc
inc i

  - or -

inc i
push i
call f
mov i, acc
pop acc

  - or -

mov r1, i
inc r1
push i
call f
mov i, acc
pop r1
mov i, r1

: Recently, I told someone it is undefined because I cannot find anything in
: the standard that says that f() must actually be called before doing
: the assignment.  Theoretically, it seems to me that if the compiler could
: determine by some magic what f() returns, it could do the assignment before
: completing evaluation of the arguments.

Note the last example above in effect performs this "magic" in that it
does the increment right away but waits till the very end before it
actually stores the result.  In the meantime i is assigned the result
of the call and then this result is overwritten.

--
D'Arcy J.M. Cain (darcy@druid.com)  |
Planix, Inc.                        |   Democracy is three wolves and a
Toronto, Ontario, Canada            |   sheep voting on what's for dinner.
+1 416 424 2871  (DoD#0082) (eNTP)  |




Author: msb@sq.sq.com (Mark Brader)
Date: Mon, 19 Sep 94 04:05:35 GMT
Raw View
> Is
>  i = f(i++);
> defined?

Yes, assuming of course that there is nothing else wrong with the program.

> Recently, I told someone it is undefined because I cannot find anything in
> the standard ...

The article was posted to four newsgroups, yet specifically states that
it's about the requirements of  "the standard".  Since the C++ standard
doesn't exist yet, the C standard must be meant.  It follows that
comp.std.c must have been the correct newsgroup.  I have directed followups
there and will respond from a C standard point of view.  When starting a
crossposted thread, please make sure that it is evident from the article's
content why (and also *that*) it is crossposted, and that the thread
actually is relevant to all the newsgroups used.  Don't use the C++ groups
to find people interested in C, and vice versa.  Please.

> ... that says that f() must actually be called before doing the assignment.

The value of a function call expression such as f(i++) was described in
ANSI section 3.3.2.2 as "specified in 3.6.6.4" (which is about return).
3.3.2.2 becomes 6.3.2.2 in the ISO standard, and with the recent amendments,
now reads in part:

 The value of the function call is determined by the return
 statement that executes within the called function, as
 specified in 6.6.6.4.

Thus it is quite explicit that the function call cannot take on a value
until the return statement has executed.  (That was reasonably explicit in
the unamended version too, if you followed the cross-reference, but now
it's nicely in one place.)  The expression in the return statement is a
full expression and so there is a sequence point after its evaluation.
So there are not just one but at least two sequence points between the
i++ and the simple assignment to i.  One suffices.

> Theoretically, it seems to me that if the compiler could
> determine by some magic what f() returns, it could do the assignment before
> completing evaluation of the arguments.
>
> However, even while making it I felt that this argument is a little
> outrageous.

Actually, it's an example of the "as if" rule (section 2.1.2.3 / 5.1.2.3).
Roughly speaking, this says that the compiler is allowed to do such an
optimization if it can be sure that a strictly conforming program wouldn't
be able to tell it happened.  Since the cited statement does not violate
strict conformance, a compiler that does implement the assignment before
the call (or which skips the call altogether) must produce the same result
as if it was done after the call and return.  Thus the line remains well
defined.
--
Mark Brader, msb@sq.com             "Logic is logic.  That's all I say."
SoftQuad Inc., Toronto                      -- Oliver Wendell Holmes

This article is in the public domain.




Author: heilig@cs.UND.NoDak.Edu (Zach Heilig)
Date: 19 Sep 1994 02:12:20 -0500
Raw View
In article <CwCp1w.JL2@druid.com>, D'Arcy J.M. Cain <darcy@druid.com> wrote:
>:  i = f(i++);

[...]

>inc i
>push i
>call f
>mov i, acc
>pop acc

note that this would be incorrect, and actually means:
     i = f(++i);

it would be correct if the 'inc i' and 'push i' were swapped, so the
slight mistake doesn't take the teeth out of the argument :-)
--
 Zach Heilig (heilig@aero.und.nodak.edu)




Author: clive@sco.com (Clive D.W. Feather)
Date: Mon, 19 Sep 1994 06:45:03 GMT
Raw View
In article <1994Sep18.224527.16592@nlm.nih.gov>,
Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
> This came from some private discussions with Ajoy Krishnan T
> (ajoyk%hss@lando.hns.com).  Ajoy has been having trouble getting postings out.
> In a private message he said:
>::  If this is not defined, then equally certainly i = (i = 2), 3; is
>:: also not defined. The consensus on comp.std.c has been that this is well
>:: defined. I believe it *should* be well defined.

I have never seen discussion on that expression, so I don't believe his
"consensus". I am certain that the expression is undefined, as it
assigns twice to i between sequence points.

Note that, on parallel hardware, even assigning the same value twice
might cause hardware faults.

--
Clive D.W. Feather     | Santa Cruz Operation    | If you lie to the compiler,
clive@sco.com          | Croxley Centre          | it will get its revenge.
Phone: +44 1923 813541 | Hatters Lane, Watford   |   - Henry Spencer
Fax:   +44 1923 813811 | WD1 8YN, United Kingdom | <= NOTE: NEW PHONE NUMBERS




Author: karrels@mcs.anl.gov (Edward L. Karrels)
Date: Mon, 19 Sep 1994 17:02:20 GMT
Raw View
In article <CwD6r5.9o3@scone.london.sco.com> clive@sco.com (Clive D.W. Feather) writes:

   In article <1994Sep18.224527.16592@nlm.nih.gov>,
   Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
   > In a private message he said:
   >::  If this is not defined, then equally certainly i = (i = 2), 3; is
   >:: also not defined. The consensus on comp.std.c has been that this is well
   >:: defined. I believe it *should* be well defined.

   I have never seen discussion on that expression, so I don't believe his
   "consensus". I am certain that the expression is undefined, as it
   assigns twice to i between sequence points.

Re-read your C manual.  There is a sequence point at the end
of the first operand of the comma operator.

Ed
--
Ed Karrels
karrels@mcs.anl.gov

What's the height of optimism?  A trombonist with a beeper.




Author: kdq@trans.jpl.nasa.gov (Kevin D. Quitt)
Date: 19 Sep 1994 11:58:15 -0700
Raw View
Thus wrote darcy@druid.com (D'Arcy J.M. Cain)
>Michael M. Rubenstein (rubenst%occs.nlm.nih.gov) wrote:
>: Well, its back.  But this time with a twist.
>Really?  Looks the same to me.

You need new glasses.


>: Is
>:  i = f(i++);
>: defined?
>
>No.

Oops!


>: There is a sequence point before the call to f() and after the
>: evaluation of i++.  But does it come between the evaluation of i++ and
>: the assignment?
>The question is when does i get incremented.  This is a different question
>than what is the value of i++.

There is no question.  All side effects *must* be complete before the call
to a function (or any other sequence point).  Therefore the increment of i
*will* take place before the call.


> Here are three possibilities using the
>assembler of some unnamed processor.
>
>mov acc, i
>inc acc
>push acc
>call f
>mov i, acc
>pop acc
>inc i

Which is incorrect in two ways: it's f(++i) instead of f(i++), and the
last two instructions invalidate the compiler.

>inc i
>push i
>call f
>mov i, acc
>pop acc

Which is still ++i instead of i++, but gets the assignment right.


>mov r1, i
>inc r1
>push i
>call f
>mov i, acc
>pop r1
>mov i, r1

Which is finally correct about i++ being pushed, but violates the
sequence point contstraint about side-effects.


>: Recently, I told someone it is undefined because I cannot find anything in
>: the standard that says that f() must actually be called before doing
>: the assignment.

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

Since the _value_ needs to be converted before the assignment, it's safe
to say you need to have the value first, therefore the function must be
evaluated before the assignment takes place.


>  Theoretically, it seems to me that if the compiler could
>: determine by some magic what f() returns, it could do the assignment before
>: completing evaluation of the arguments.

If it also guaranteed that all side effects from the function also took
place before the assignment, then it would have executed the function
one way or another.  The function ends with a sequence point.


>Note the last example above in effect performs this "magic"

Magic indeed.




--
#include <standard.disclaimer>
 _
Kevin D Quitt  USA 91351-4454  96.37% of all statistics are made up




Author: diamond@jrd.dec.com (Norman Diamond)
Date: 20 Sep 1994 00:08:27 GMT
Raw View
In article <KARRELS.94Sep19120220@ptera.mcs.anl.gov>, karrels@mcs.anl.gov (Edward L. Karrels) writes:
>In article <CwD6r5.9o3@scone.london.sco.com> clive@sco.com (Clive D.W. Feather) writes:
>>In article <1994Sep18.224527.16592@nlm.nih.gov>,
>>Michael M. Rubenstein <rubenst%occs.nlm.nih.gov> wrote:
>>>In a private message he said:
>>>> If this is not defined, then equally certainly i = (i = 2), 3; is
>>>>also not defined. The consensus on comp.std.c has been that this is well
>>>>defined. I believe it *should* be well defined.

>>I have never seen discussion on that expression, so I don't believe his
>>"consensus". I am certain that the expression is undefined, as it
>>assigns twice to i between sequence points.

>Re-read your C manual.  There is a sequence point at the end
>of the first operand of the comma operator.

Re-read your C manual.  Assignment has a higher precedence than the comma
operator, and both assignments are demanded before the sequence point.

>What's the height of optimism?  A trombonist with a beeper.

Really?  Not a usenetter who tells someone else to read a C manual when he
hasn't done so himself?
--
 <<  If this were the company's opinion, I would not be allowed to post it.  >>
segmentation fault (california dumped)




Author: rubenst%occs.nlm.nih.gov (Michael M. Rubenstein)
Date: Sun, 18 Sep 94 17:07:58 GMT
Raw View
Well, its back.  But this time with a twist.

Is

 i = f(i++);

defined?

There is a sequence point before the call to f() and after the
evaluation of i++.  But does it come between the evaluation of i++ and
the assignment?

Recently, I told someone it is undefined because I cannot find anything in
the standard that says that f() must actually be called before doing
the assignment.  Theoretically, it seems to me that if the compiler could
determine by some magic what f() returns, it could do the assignment before
completing evaluation of the arguments.

However, even while making it I felt that this argument is a little
outrageous.

--
Mike Rubenstein