Topic: Comment on core issue 391 October 2003 note
Author: rani_sharoni@hotmail.com (Rani Sharoni)
Date: Mon, 22 Dec 2003 16:48:21 +0000 (UTC) Raw View
jpotter@falcon.lhup.edu (John Potter) wrote in message news:<mjg9uv0oq8r9067r7nc8dc8ltlfr9f1i6b@4ax.com>...
> On Sat, 20 Dec 2003 00:09:27 +0000 (UTC), rani_sharoni@hotmail.com
> ("Rani Sharoni") wrote:
>
> > Now consider the following modest modification of the above code:
> > extern "C" int printf(const char*, ...);
>
> > int main()
> > {
> > struct { bool b_; } const x = { true };
> > void f(bool&);
> > f(const_cast<bool&>(x.b_));
> >
> > if (!x.b_) printf("not aggressive!\n");
> > }
>
> > The front-ends gave up in this "extremely" complicated optimization case and
> > since the backend is obviously const blind then all the compilers generated
> > code that prints.
>
> I moved x outside of main and got a segfault.
>
> Your entire argument is based upon how compilers implement undefined
> behavior today. Because today's compilers have difficulty detecting
> modification of a const, the standard should not require generating
> a const. Does not hold water.
>
> There are good reasons for wanting a change. Also note that it is
> a proposal for a change not a defect report. There is no defect and
> any change would be in the next standard not the next TC.
I didn't talk about hardwired const but about "const temporary"
optimizations. The basic questions was whether allowing the freedom of
creating const temporary for future optimizations is in the scope of
the standard. I tried to illustrate my doubts, regarding this
optimizations freedom, using some simple examples that demonstrate how
conservative compilers can be when aliasing is involved (e.g.
reference) and how optimizers are const blind. To be frank, I don't
even know whether the original intention being the "const temporary"
was optimizations. It seems like "natural" decision when trying to
decide whether to const or not to const. The consequences are quite
annoying although no compiler is actually aware about the relevant
paragraph.
Here is even more fundamental problem of the "additional temporary"
feature.
Consider the following code:
extern "C" int printf(const char*, ...);
struct A
{
A() {}
A(A const&) { printf("A(A const&)\n"); }
A& ref() { return *this; }
} a;
void f(A const& x1)
{
if (&x1 == &a) printf("foo\n");
}
bool select() { return false; }
int main()
{
f( select() ? A() : a); // #1
f((select() ? A() : a).ref()); // #2
return 0;
}
In this case the additional temporary changes the meaning of the
program.
VC, Comeau and BCC produce code that prints "foo" but GCC spoiled my
arguments and called the copy constructor (i.e. print "A(A const&)"
and not "foo").
GCC seemed to be the only brave compiler that went ahead and actually
made the additional copy as allowed by the standard. I was very
surprised that such impressive complier seems to demolish DR #391.
After few hours of despair I added #2 and was relived to find out that
it's merely a bug in GCC since #2 (illegally) behaved like #1.
I must admit that the conditional operator section in the standard
really scares me since this section contains sentences that defeat my
humble intellectual abilities.
Anyway, here is the analysis of the above code. The most important
thing to note in this case is that rvalue and lvalue of the *same*
type are the operands of ?: and the result is rvalue which might yield
additional const temporary when the reference is bounded into it.
The interesting thing is how exactly the lvalue is transformed into an
rvalue. There are two distinct cases here:
1) The lvalue is of non-class type. The lvalue is converted into the
rvalue which result in new object
2) The lvalue is of class type. This case is interesting, according
to 5.16/3/2/1 the lvalue expression is changed into rvalue and no copy
is made. If the resulting rvalue produce additional temporary when the
reference is bounded into it then it will not refer to the original
lvalue which might change the meaning of the program.
GCC didn't produce the additional temporary during the reference
binding but earlier when it transformed the lvalue operand into rvalue
which violates 5.16/3/2/1. After the first violation the resulting
temporary was not const (no surprises) otherwise calling A::ref() was
ill-formed. Bug in a bug. What a relief!
The equivalent case when the rvalue is bound to the reference is more
controversial (DR #86). In case that the rvalue lifetime is extended
then, IMO, the rvalue that was produced from the lvalue should be
copied in order to be bounded to the reference lifetime. GCC and VC
that support this makes the additional copy as expected.
Thanks,
Rani
---
[ 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.jamesd.demon.co.uk/csc/faq.html ]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 22 Dec 2003 20:42:18 +0000 (UTC) Raw View
On Mon, 22 Dec 2003 16:48:21 +0000 (UTC), rani_sharoni@hotmail.com (Rani
Sharoni) wrote:
> Here is even more fundamental problem of the "additional temporary"
> feature.
> Consider the following code:
> extern "C" int printf(const char*, ...);
> struct A
> {
> A() {}
> A(A const&) { printf("A(A const&)\n"); }
> A& ref() { return *this; }
> } a;
> void f(A const& x1)
> {
> if (&x1 == &a) printf("foo\n");
> }
> bool select() { return false; }
> int main()
> {
> f( select() ? A() : a); // #1
> f((select() ? A() : a).ref()); // #2
>
> return 0;
> }
> In this case the additional temporary changes the meaning of the
> program.
As the old saying goes, any program which depends upon the existence
or non-existence of temporaries is broken.
> VC, Comeau and BCC produce code that prints "foo" but GCC spoiled my
> arguments and called the copy constructor (i.e. print "A(A const&)"
> and not "foo").
> GCC seemed to be the only brave compiler that went ahead and actually
> made the additional copy as allowed by the standard.
Are you sure where that additional temporary was created? I contend
that it constructed a temporary rvalue for the result of the conditional
expression which is an rvalue. In the true case, it default initializes
that temporary removing the copy from the default constructed temporary
as allowed. In the false case, it copy initializes that temporary from
the variable a. A simple bookkeeping exercise. There is always a
temporary to destroy when returning from the function or unwinding a
throw.
Do you have a reference stating that a temporary may not be created for
an lvalue to rvalue conversion?
I don't know what is going through other minds, but I see you talking
about temporaries and optimizations that were settled long ago. I do
see a problem with allowing binding a non-const rvalue or a const copy
to the const reference. This choice can change a well formed program
to undefined behavior. Not because of the temporary, but because of
the const.
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.jamesd.demon.co.uk/csc/faq.html ]
Author: rani_sharoni@hotmail.com (Rani Sharoni)
Date: Tue, 23 Dec 2003 07:15:50 +0000 (UTC) Raw View
jpotter@falcon.lhup.edu (John Potter) wrote in message news:<69ieuvsklnprvd96n2lvd85vqj5lhj2t4h@4ax.com>...
> On Mon, 22 Dec 2003 16:48:21 +0000 (UTC), rani_sharoni@hotmail.com (Rani
> Sharoni) wrote:
>
> > Here is even more fundamental problem of the "additional temporary"
> > feature.
> > Consider the following code:
>
> > extern "C" int printf(const char*, ...);
>
> > struct A
> > {
> > A() {}
> > A(A const&) { printf("A(A const&)\n"); }
> > A& ref() { return *this; }
> > } a;
>
> > void f(A const& x1)
> > {
> > if (&x1 == &a) printf("foo\n");
> > }
>
> > bool select() { return false; }
>
> > int main()
> > {
> > f( select() ? A() : a); // #1
> > f((select() ? A() : a).ref()); // #2
> >
> > return 0;
> > }
>
> > In this case the additional temporary changes the meaning of the
> > program.
>
> As the old saying goes, any program which depends upon the existence
> or non-existence of temporaries is broken.
AFAICT this is not the same case.
> > VC, Comeau and BCC produce code that prints "foo" but GCC spoiled my
> > arguments and called the copy constructor (i.e. print "A(A const&)"
> > and not "foo").
>
> > GCC seemed to be the only brave compiler that went ahead and actually
> > made the additional copy as allowed by the standard.
>
> Are you sure where that additional temporary was created?
#2 provides the evidence that it wasn't in the right place.
> Do you have a reference stating that a temporary may not be created for
> an lvalue to rvalue conversion?
You missed the most important point that I mentioned and therefore the
all argument that I tried to make. Maybe it indicates that I didn't
wrote my previous posting very well ;-)
The new "discovery" comes from 5.16/3/2:
-- If E2 is an rvalue, or if the conversion above cannot be done:
-if E1 and E2 have *class* type, and the underlying class types are
the *same* [...]
** If the conversion is applied, E1 is *changed* to an rvalue of type
T2 that still refers to the *original* source class object (or the
appropriate sub-object thereof). [Note: that is, *no copy* is made. ]
This "clearly" means that the lvalue in "cond ? rvalue : lvalue" is
changed to rvalue without copying (i.e. no conversion is involved). I
hope that this will encourage you to read my pervious post more
carefully.
Thanks,
Rani
---
[ 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.jamesd.demon.co.uk/csc/faq.html ]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: Wed, 24 Dec 2003 06:36:35 +0000 (UTC) Raw View
On Tue, 23 Dec 2003 07:15:50 +0000 (UTC), rani_sharoni@hotmail.com (Rani
Sharoni) wrote:
> The new "discovery" comes from 5.16/3/2:
5.16/3 starts: if the second and third operand have different types...
They don't and /3 is skipped completely.
We then get to /6 where an lvalue to rvalue conversion is performed.
I do find it amusing that a class rvalue can refer to the original
object and be used to modify the original object. I wonder if that
was the intent. It is not clear that any of that refers to the
evaluation of the expression and not just the parsing. There is no
conversion to an rvalue refering to the original object when the
other operand is selected. Modifiable rvalues are so much fun.
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.jamesd.demon.co.uk/csc/faq.html ]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: Thu, 18 Dec 2003 01:18:08 +0000 (UTC) Raw View
Although not addressed in the DR, the choice to construct a CONST copy
changes the behavior of a program.
X f ();
void g (X const& x) { const_cast<X&>(x); }
g(f());
If no copy or a non-const copy (which is not allowed), the program has
well defined behavior. If a const copy the program has undefined
behavior.
It is more than an optimization issue.
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.jamesd.demon.co.uk/csc/faq.html ]
Author: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Thu, 18 Dec 2003 17:49:40 +0000 (UTC) Raw View
John Potter wrote:
> Although not addressed in the DR, the choice to construct a CONST copy
> changes the behavior of a program.
>
> X f ();
> void g (X const& x) { const_cast<X&>(x); }
>
> g(f());
>
> If no copy or a non-const copy (which is not allowed), the program has
> well defined behavior. If a const copy the program has undefined
> behavior.
>
> It is more than an optimization issue.
This is simply a defective and draconic requirement that might have
"catastrophic" impact on the theoretical behavior the most obvious
well-behaved code:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1("x");
string str2("y");
cout << str1 + str2 << endl;
}
Is anyone willing to say that the above code might have undefined behavior
(beside of the actual printing)?
Lets all take closer look at possible implementation of std::srting. The
implantation might use array member for short strings which is fine
legitimate optimizations that obviously meant to have no effect on the well
behavior of the code. But is it so?
The standard "allows" the following sequence:
1) str1 + str2 is an rvalue
2) the second reference parameter of operator<< is initialized from this
rvalue
3) the implementation take an advantage of the freedom to copy the rvalue to
additional temporary which must be of type const std::string per
8.5.3/5/2/1/2
4) During the construction of the const string temporary the short string is
manually copied into the array member and the coping has *undefined
behavior* according 12.1/15. Oops
I hope that everyone will agree that this is unbearable requirement and the
experts who write such libraries or even the poor programmers can not be
blame to overlook such fundamental requirement.
The good news is that no implementation cares about this requirement because
they have no intention generating suicidal programs.
I think that the committee should revise the standard and probably remove
the "const temporary" requirement from 8.5.3/5/2/1/2 (even before
considering DR #391) and eliminate 12.1/15 altogether.
Rani
---
[ 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.jamesd.demon.co.uk/csc/faq.html ]
Author: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Thu, 18 Dec 2003 19:51:30 +0000 (UTC) Raw View
"Rani Sharoni" wrote:
> [...]
> 4) During the construction of the const string temporary the short
> string is manually copied into the array member and the coping has
> *undefined behavior* according 12.1/15. Oops
I'm sorry but I misread 12.1/15 due to premature enthusiasm. This above
statement and eventually the all example is wrong.
Sorry,
Rani
---
[ 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.jamesd.demon.co.uk/csc/faq.html ]
Author: rani_sharoni@hotmail.com ("Rani Sharoni")
Date: Sat, 20 Dec 2003 00:09:27 +0000 (UTC) Raw View
John Potter wrote:
> Although not addressed in the DR, the choice to construct a CONST copy
> changes the behavior of a program.
>
> X f ();
> void g (X const& x) { const_cast<X&>(x); }
>
> g(f());
>
> If no copy or a non-const copy (which is not allowed), the program has
> well defined behavior. If a const copy the program has undefined
> behavior.
>
> It is more than an optimization issue.
I hope that this post will help to rehabilitate my broken reliability ;-)
IMHO this is another case of "const optimizations" that probably no compiler
takes advantage of due to potential aliasing abuses.
Consider the following:
extern "C" int printf(const char*, ...);
bool const& b1 = true;
bool& b2 = const_cast<bool&>(b1); // undefined behavior
int main()
{
void f(bool&);
f(b2); // implemented in different translation unit
// set b2 = false
if (!b1) printf("not aggressive!\n");
}
Applying 8.5.3/5/2/1/2 in the above code results with b1 referring to const
temporary, aggressive complier is now free to eliminate the printf "dead"
code. The problem is that compilers are very conservative when aliasing is
involved and optimizers don't really care about consts.
I compiled the above code using VC, BCC, GCC and Comeau (VC BE) aggressive
optimizations mode and they all unsurprisingly generated code that prints. I
'll be happy to know about more compilers behavior but I suspect that they
all behave the same.
I suspect that even when "const optimizations" actually occurs then they are
preformed by the front-end since the back-end (i.e. the actual optimizer)
consumes intermediate code never heard about C++ const.
For Example:
int main()
{
bool const b3 = true;
void f(bool&);
f(const_cast<bool&>(b3));
// b3 is constant expression so
// if (!true) is the actual code
if (!b3) printf("not aggressive!\n");
}
All the compilers that I've tried, regardless of the optimization mode,
eliminated the printf code.
Now consider the following modest modification of the above code:
extern "C" int printf(const char*, ...);
int main()
{
struct { bool b_; } const x = { true };
void f(bool&);
f(const_cast<bool&>(x.b_));
if (!x.b_) printf("not aggressive!\n");
}
The front-ends gave up in this "extremely" complicated optimization case and
since the backend is obviously const blind then all the compilers generated
code that prints.
I'll not be surprised to know that even compilers writers are not aware of
this "const temporary" requirement, except for the one who wrote it ;-)
My conclusion is that the "const temporary" optimization freedom is out of
the scope of the C++ standard and when revising the standard (i.e. DR #391)
this requirement should be removed at least for class types because it might
change the behavior of well-behaved code without any clear benefit
especially since aliasing is involved.
Thanks,
Rani
---
[ 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.jamesd.demon.co.uk/csc/faq.html ]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 22 Dec 2003 04:56:09 +0000 (UTC) Raw View
On Sat, 20 Dec 2003 00:09:27 +0000 (UTC), rani_sharoni@hotmail.com
("Rani Sharoni") wrote:
> Now consider the following modest modification of the above code:
> extern "C" int printf(const char*, ...);
> int main()
> {
> struct { bool b_; } const x = { true };
> void f(bool&);
> f(const_cast<bool&>(x.b_));
>
> if (!x.b_) printf("not aggressive!\n");
> }
> The front-ends gave up in this "extremely" complicated optimization case and
> since the backend is obviously const blind then all the compilers generated
> code that prints.
I moved x outside of main and got a segfault.
Your entire argument is based upon how compilers implement undefined
behavior today. Because today's compilers have difficulty detecting
modification of a const, the standard should not require generating
a const. Does not hold water.
There are good reasons for wanting a change. Also note that it is
a proposal for a change not a defect report. There is no defect and
any change would be in the next standard not the next TC.
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.jamesd.demon.co.uk/csc/faq.html ]
Author: brok@rubikon.pl (Bronek Kozicki)
Date: Mon, 22 Dec 2003 04:56:20 +0000 (UTC) Raw View
On Sat, 20 Dec 2003 00:09:27 +0000 (UTC), "Rani Sharoni" wrote:
> bool const& b1 = true;
> bool& b2 = const_cast<bool&>(b1); // undefined behavior
I do not understand. You made some experiments involving undefined
behaviour, and want to compare its results to standard ? Where undefined
behaviour occurs, language rules do not apply any more.
> My conclusion is that the "const temporary" optimization freedom is out of
> the scope of the C++ standard and when revising the standard (i.e. DR #391)
I do not see this freedom in standard as it is right now (in clause 5.3
there is no word about temporary optimization, nor mention about 12.8).
Moreover, allowing this optimization would make result of this program
implementation-defined (from DF 434):
int main()
{
int * pi = NULL;
const int * const & rcpci = pi;
int i = 0;
pi = &i;
if (pi == rcpci)
std::cout << "bound to lvalue" << std::endl;
else
std::cout << "bound to temporary rvalue" << std::endl;
}
Actually I think that adding note to 5.3 explicitly disallowing such
optimization would be in place. If anybody agrees with me, I will
prepare DF.
It's possible that I misunderstood whole point of your post and DF 391,
in this case I apologise for off-topic.
B.
---
[ 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.jamesd.demon.co.uk/csc/faq.html ]