Topic: Preincrement, sequence points, etc


Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 1 Apr 2002 21:39:23 GMT
Raw View
On Sat, 30 Mar 2002 22:52:43 GMT, "Igor A. Goussarov"
<igusarov@gate.studio1001.akella.ru> wrote:

>    I'm trying to peek nits in the builtin prefix increment/decrement
> operators.

Note that all of these problems also exist for the assignment operators.
There was a long discussion of these problems some time ago and the
prefix operators were also mentioned in it.  The committee is aware of
the problems; however, there seems not to have been any issue raised.
Maybe a can of worms best left closed?

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: thp@cs.ucr.edu
Date: Mon, 1 Apr 2002 21:39:27 GMT
Raw View
Igor A. Goussarov <igusarov@gate.studio1001.akella.ru> wrote:
: Hello group,

:    I'm trying to peek nits in the builtin prefix increment/decrement
: operators. The first question I came across: is there a guarantee that

:      int      x;
:      assert( &++x == &x );   // Is it true?

:    Subclause 5.3.2p1 does not say explicitly that the result of prefix
: ++ is the same object as the argument.
:    The point here is that ++x claims to return a _new_ value of 'x' and
: it should be an lvalue. Thus, I can write *&++x and it should (?) be
: equivalent to ++x. Subclause 5p4 is not violated, because the
: composition of *& is not accessing the _prior_ value. I've put a
: question mark because I'm not sure. So here's the second question: does
: *&++x directly or indirectly violate some clause or otherwise lead to
: undefined behaviour?

I'm probably confused, but IIRC, where C would return an object's new
value (++x, --x, x=y, x+=3), it is the C++ policy to instead return a
reference to that object and to let lvalue-to-rvalue conversion
retrieve the object's new value.  That policy, however, conflicts with
the sequence-point rules.  Consider, for instance, the well-defined C
expression "3 * ++x".  If the result of "++x" is a reference to x,
then the lvalue-to-rvalue conversion on "++x" accesses  x  a second time
(without an intervening sequence point) for a purpose other than
determining the new value of x.  The resulting behavior is, therefore,
undefined!!  (If so, this situation warrants a defect report.)

Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 1 Apr 2002 23:45:27 GMT
Raw View
On Mon,  1 Apr 2002 21:39:27 GMT, thp@cs.ucr.edu wrote:

> I'm probably confused, but IIRC, where C would return an object's new
> value (++x, --x, x=y, x+=3), it is the C++ policy to instead return a
> reference to that object and to let lvalue-to-rvalue conversion
> retrieve the object's new value.  That policy, however, conflicts with
> the sequence-point rules.  Consider, for instance, the well-defined C
> expression "3 * ++x".  If the result of "++x" is a reference to x,
> then the lvalue-to-rvalue conversion on "++x" accesses  x  a second time
> (without an intervening sequence point) for a purpose other than
> determining the new value of x.  The resulting behavior is, therefore,
> undefined!!  (If so, this situation warrants a defect report.)

You missed an important word.  It is the "prior" value which may only be
accessed to determine the new value.  In your example, it is the new
value which is accessed either via the reference or magic allowed for
builtins.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Al Grant" <tnarga@arm.REVERSE-NAME.com>
Date: Tue, 2 Apr 2002 13:22:40 GMT
Raw View
"John Potter" <jpotter@falcon.lhup.edu> wrote in message
news:3ca7272b.9857402@news.earthlink.net...
> Note that all of these problems also exist for the assignment operators.
> There was a long discussion of these problems some time ago and the
> prefix operators were also mentioned in it.  The committee is aware of
> the problems; however, there seems not to have been any issue raised.

Does issue 222 not include this case?



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: thp@cs.ucr.edu
Date: Tue, 2 Apr 2002 15:28:09 GMT
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: On Mon,  1 Apr 2002 21:39:27 GMT, thp@cs.ucr.edu wrote:

:> I'm probably confused, but IIRC, where C would return an object's new
:> value (++x, --x, x=y, x+=3), it is the C++ policy to instead return a
:> reference to that object and to let lvalue-to-rvalue conversion
:> retrieve the object's new value.  That policy, however, conflicts with
:> the sequence-point rules.  Consider, for instance, the well-defined C
:> expression "3 * ++x".  If the result of "++x" is a reference to x,
:> then the lvalue-to-rvalue conversion on "++x" accesses  x  a second time
:> (without an intervening sequence point) for a purpose other than
:> determining the new value of x.  The resulting behavior is, therefore,
:> undefined!!  (If so, this situation warrants a defect report.)

: You missed an important word.

Perhaps.

: It is the "prior" value which may only be accessed to determine the
: new value.

Agreed.

: In your example, it is the new value which is accessed either via the
: reference or magic allowed for builtins.

If I understand correctly, until the next sequence point the value of
x is in flux and any access to it yields undefined behavior.  What
guarantee do you find that an access to x (specifically the
lvalue-to-rvalue conversion) will find the x's "new value"?

Tom Payne

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Wed, 3 Apr 2002 00:31:38 GMT
Raw View
On Tue,  2 Apr 2002 15:28:09 GMT, thp@cs.ucr.edu wrote:

> If I understand correctly, until the next sequence point the value of
> x is in flux and any access to it yields undefined behavior.  What
> guarantee do you find that an access to x (specifically the
> lvalue-to-rvalue conversion) will find the x's "new value"?

As stated by Al Grant, I missed core issue 222.  See the discussion
there.  Much better than I could provide.

John

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Igor A. Goussarov" <igusarov@gate.studio1001.akella.ru>
Date: Sat, 30 Mar 2002 22:52:43 GMT
Raw View
Hello group,

   I'm trying to peek nits in the builtin prefix increment/decrement
operators. The first question I came across: is there a guarantee that

     int      x;
     assert( &++x == &x );   // Is it true?

   Subclause 5.3.2p1 does not say explicitly that the result of prefix
++ is the same object as the argument.
   The point here is that ++x claims to return a _new_ value of 'x' and
it should be an lvalue. Thus, I can write *&++x and it should (?) be
equivalent to ++x. Subclause 5p4 is not violated, because the
composition of *& is not accessing the _prior_ value. I've put a
question mark because I'm not sure. So here's the second question: does
*&++x directly or indirectly violate some clause or otherwise lead to
undefined behaviour?
   Now I'll try to answer my first question myself and the third
question is going to arise.

choice 1) Let's speculate how can &++x not equals to &x. This might be
the case only if the compiler has implemented the preincrement via some
transparent temporary, according to the following scenario:

    // Source
     int      x = 0;
     int      y = *&++x;

where the last line is compiled as

    // Machine code equivalent
     int      temp = x + 1;   // no seq.point here
     int      y = *&temp;     // no seq.point here
     x = temp;                // end of complete expression

   Here, 'temp' is an internal transparent temporary (remember, I'm
considering builtin types only). This scenario might allow the compiler
to make full use of sequence points and postpone the modification of 'x'
until the nearest sequence point. But this would lead to undefined
behaviour if I were to write

     int      x = 0;
     int*     p = &++x;
     int      y = *p;

   Then the last line would dereference the pointer to an internal
temporary which has already gone. Since the three lines above look
absolutely legal to me, I somehow doubt that the compiler is allowed to
act this way.
   Now, having persuaded myself (and probably you) that ++x cannot
return a reference to some internal temporary, I see no choice other
then

choice 2) &++x == &x. Then the returned value refers to the same memory
location as 'x'. Then, since ++x returns an lvalue which is the _new_
value of 'x', I can apply the composition of *& to it and obtain the
same _new_ value. But since the *& is accessing the new value via taking
its address and dereferencing the pointer, I conclude that the new value
should be stored in 'x' immediately (or at least before evaluating
operator&). In other words, in the presense of operator& the compiler
cannot take advantage of delayed writeback. Is this conclusion correct?

Igor

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Graeme Prentice <gp1@paradise.net.nz>
Date: Sun, 31 Mar 2002 00:08:53 CST
Raw View
On Sat, 30 Mar 2002 22:52:43 GMT, "Igor A. Goussarov"
<igusarov@gate.studio1001.akella.ru> wrote:


[I'm not a C++ guru, but I'm interested in your question, so here's my
comments]


>Hello group,
>
>   I'm trying to peek nits in the builtin prefix increment/decrement
>operators. The first question I came across: is there a guarantee that
>
>     int      x;
>     assert( &++x == &x );   // Is it true?
>
>   Subclause 5.3.2p1 does not say explicitly that the result of prefix
>++ is the same object as the argument.

If you look at Table 74, requirements for a forward iterator, it says
that   &r == &++r    for a forward iterator,  similarly --r for
bidirectional iterator.  Since it was intended that built in pointer
type is a random access iterator, plus the fact that temporaries are
not lvalues,  I think you're right   - the intent is that the result
of prefix ++ is the same object as the argument.

In C89, ++r was not an lvalue.




>   The point here is that ++x claims to return a _new_ value of 'x' and
>it should be an lvalue. Thus, I can write *&++x and it should (?) be
>equivalent to ++x. Subclause 5p4 is not violated, because the
>composition of *& is not accessing the _prior_ value. I've put a
>question mark because I'm not sure.


Again I agree with you  - its not accessing the _prior_ value, its
accessing the post modified value so its legal.
Similarly --(++r) == r   and is legal and a requirement for a
bidirectional iterator  (table 75)


> So here's the second question: does
>*&++x directly or indirectly violate some clause or otherwise lead to
>undefined behaviour?

[snip]


>   Now, having persuaded myself (and probably you) that ++x cannot
>return a reference to some internal temporary, I see no choice other
>then
>
>choice 2) &++x == &x. Then the returned value refers to the same memory
>location as 'x'. Then, since ++x returns an lvalue which is the _new_
>value of 'x', I can apply the composition of *& to it and obtain the
>same _new_ value. But since the *& is accessing the new value via taking
>its address and dereferencing the pointer, I conclude that the new value
>should be stored in 'x' immediately (or at least before evaluating
>operator&). In other words, in the presense of operator& the compiler
>cannot take advantage of delayed writeback. Is this conclusion correct?

5.3.2 p1 says the result of ++x is an lvalue with a value which is the
new value.
Therefore *&++x should return the new value of x.


So, your question about delayed writeback is interesting   - is it
possible for the compiler to achieve the correct result for
expressions such as --(++r) and *&++x  with delayed writeback  (e.g.
delayed until the next sequence point).  I'm not a compiler writer,
but I suspect its theoretically possible to get the "right answer" but
delay the actual change to the memory location until the next sequence
point

- however, from a "user" point of view, it should make no difference
at what point the actual physical location changes as long as *&++x
produces the new value of x

But then, the standard says the result of ++x is an lvalue with a
value equal to the new value, so I think this means that there can be
no delayed writeback as you suggest.  If this is the case, I think a
comment to this effect should be added to the standard.


Graeme

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Graeme Prentice <gp1@paradise.net.nz>
Date: Sun, 31 Mar 2002 16:10:05 GMT
Raw View
On Sun, 31 Mar 2002 00:08:53 CST, Graeme Prentice
<gp1@paradise.net.nz> wrote:

>
>Again I agree with you  - its not accessing the _prior_ value, its
>accessing the post modified value so its legal.
>Similarly --(++r) == r   and is legal and a requirement for a
>bidirectional iterator  (table 75)


except that --(++r) for r being an lvalue of built in pointer type,
results in the object being modified twice without an intervening
sequence point (thereby resulting in undefined behaviour according to
section 5 paragraph 4 of the C++ standard     - but table 75 clearly
suggests it should work ???     - and section 24.1.4 begins ...  "a
class or built in type X satisifes ...  "    plus section 24.1
paragraph 2 says  ...  "This ensures that every template function that
takes iterators works as well with regular pointers."


so is --(++x) legal for x a built in pointer type or is the comment in
table 75 just "pseudo code" and really means ++x; --x; for a built in
pointer


>
>
>So, your question about delayed writeback is interesting   - is it
>possible for the compiler to achieve the correct result for
>expressions such as --(++r) and *&++x  with delayed writeback  (e.g.
>delayed until the next sequence point).  I'm not a compiler writer,
>but I suspect its theoretically possible to get the "right answer" but
>delay the actual change to the memory location until the next sequence
>point
>
>- however, from a "user" point of view, it should make no difference
>at what point the actual physical location changes as long as *&++x
>produces the new value of x

I have another comment about this.

The standard says that between two sequence points, the order in which
side effects take place is unspecified.  But what is the definition of
"side effect takes place" ?

Lets define that if  *&++x  returns the modified value of x then the
"side effect has taken place"  (whether this means the actual physical
memory has changed I'm not sure, but for all intents and purposes, it
has changed)

- therefore, since I think that *&++x is required to return the new
value of x, then the side effect must have taken place prior to the *
operator


Graeme

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]