Topic: Common compiler bug with static variable constructor


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 26 Jun 1994 21:47:19 GMT
Raw View
In article <9417419.27366@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>>Is this reflected in their working paper?
>>>Or is this a decision that they have made but not yet written down?
>>
>> The latter I think. The idea is that
>> "copy constructors without side effects can be freely inserted
>> or elided".
>
>Of course they can.  *Any* code without side effects can be optimized
>away or inserted at will.

 That statement is "apparently" correct.
(Dont you DARE optimise away my timing loops! :-))
Only "apparently". A "side-effect" differs from the "main-effect".
For example, a function may have no side effects -- but it
still can return a value. A "call" to that function like:

  f();

may have no side effects whereas

 int a = f();

does have an effect -- whether the effect is a side effect or
a main effect is another sort of problem. For example:

 void g() { int a = f(); }

Does a program containing:

 g();

have a side effect or not? One minute we're agreeing

 int a = f();

has a side effect and the next saying a program containing a call
to a function containing it doesnt.

>The question, surely, is when is the
>compiler allowed to optimize away copy constructors that *do* have
>side-effects.

 No, the question is whether the programmer is allowed
to rely on side effects in the contructor -- or whether
they're permitted at all.

>
>>Tom Plum is working on defining what 'side effects' means.
>
>Hmm, I don't quite understand.  A side effect is anything that
>can affect the program's observable input/output behaviour, right?
>Did you have some other definition in mind?

 Plum does. Consider:

 b->static_member();

There is a proposal that b must not be evaluated because it has no
side-effects. The point is that if 'b' is an invalid pointer,
loading it into a machine register may well have an interesting
side effect -- a core dump. This surely affects the programs
behaviour :-)
>
>>An alternative is that copy constructors be REQUIRED to be side
>>effect free; that is, a rule that says
>>
>> "copy constructors may be freely inserted or elided"
>>
>>means that you may not put side effects in functions or
>>your program has undefined behaviour.
>
>Is there any reason to make this undefined behaviour rather than
>implementation defined behaviour, as is done in the ARM, or at least
>unspecified behaviour?

 Yes. There is every reason to have lots of undefined
behaviour in the Standard.

>I don't think implementations should be allowed
>to dump core for a program that say counts the number of copy
>constructor calls and then prints that number out.

 Sure they should. That doesnt mean you'd buy
such a compiler. The Standard doesnt have to specify
everything. Its desirable it doesnt. The less it specifies
the more freedom implementors have.

>The standard
>should not necessarily specify which number will be printed out, but it
>should specify that *some* number will be printed out, rather than
>allowing completely arbitrary behaviour.

 When? At any time at all? Copy constructors might
be used by the compiler almost anywhere. Making it unspecified
makes conformance testing impossible -- right in the
middle of a conformance test numbers randomly start
appearing from "no where obvious" (actually from
copy constructors randomly inserted by the compiler).
Thats not on.

>> My contribution to the discussion is that I suggested
>>only PUBLIC copy constructors should be freely inserted
>>or elided, permitting non-public ones to have side effects.
>>I'm not sure.
>
>Hmm.  I don't think this abides by the "Principle of Least Astonishment".
>I could imagine that rule being quite confusing in practice.

 It seems sensible to me -- the idiom of private
copy constructors is used to inhibit copying. Imagine
your "Astonishment" if you got a linker error from a gratuitously
inserted copy constructor that had deliberately not been
defined and which you can determine by inspection
YOU have never invoked.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 27 Jun 1994 17:08:15 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>One minute we're agreeing
>
> int a = f();
>
>has a side effect and the next saying a program containing a call
>to a function containing it doesnt.

I don't think that this is contradictory.
A side-effect is anything that *may* affect the program's I/O behaviour.

Now the statement `int a = f()' contains a side-effect, since it *may*
affect the program's I/O behaviour (e.g. if the next statement is
`cout << a;'.)  But if f() doesn't have any side-effects, and
you embed `int a = f()' in a context where `a' is not used,
then it definitely doesn't affect the I/O behaviour, so the
statement doesn't have any side-effects.  There's no contradiction.
It's really quite sensible, IMHO.

>>The question, surely, is when is the
>>compiler allowed to optimize away copy constructors that *do* have
>>side-effects.
>
> No, the question is whether the programmer is allowed
>to rely on side effects in the contructor

Well, the two questions are complementary.  If the programmer is
allowed to rely on side effects in the constructor, then the compiler
can't optimize them away, and if the compiler is allowed to optimize
them away, then the programmer certainly can't rely on them.

>-- or whether they're permitted at all.

Hmm.  I don't think this idea of *disallowing* side-effects in
copy-constructor/destructor pairs (in the sense that the behaviour
would be undefined if such a pair contained side-effects) is a good
one.  More below...

>>>Tom Plum is working on defining what 'side effects' means.
>>
>>Hmm, I don't quite understand.
[...]
> b->static_member();
>
>There is a proposal that b must not be evaluated because it has no
>side-effects.

I'm quite happy requiring that b must not be evaluated, but
I don't think that your reason "because it [b] has no side-effects"
makes sense.  I'd be a lot happier if you just said "because the value
of `b' is definitely not needed".

>The point is that if 'b' is an invalid pointer,
>loading it into a machine register may well have an interesting
>side effect -- a core dump.

Yes - the reason that you want to disallow evaluation of `b' is
because evaluating it *can* have side-effects, not because it can't.

>>>An alternative is that copy constructors be REQUIRED to be side
>>>effect free; that is, a rule that says
>>>
>>> "copy constructors may be freely inserted or elided"
>>>
>>>means that you may not put side effects in functions or
>>>your program has undefined behaviour.
>>
>>Is there any reason to make this undefined behaviour rather than
>>implementation defined behaviour, as is done in the ARM, or at least
>>unspecified behaviour?
>
> Yes. There is every reason to have lots of undefined
>behaviour in the Standard.

I'm well aware of the advantages of undefined behaviour
in general.  I just don't see how it would be useful to
compiler writers in this particular instance, and I *can*
see how it could be very useful to programmers if the
behaviour was unspecified rather than undefined.

>>I don't think implementations should be allowed
>>to dump core for a program that say counts the number of copy
>>constructor calls and then prints that number out.
>
> Sure they should. That doesnt mean you'd buy
>such a compiler. The Standard doesnt have to specify
>everything. Its desirable it doesnt. The less it specifies
>the more freedom implementors have.

Can you think of any optimizations that could be carried out
if the behaviour is undefined rather than unspecified?
I can't.  What exactly is it that the implementors are
going to use their extra freedom for in this particular case?

>>The standard
>>should not necessarily specify which number will be printed out, but it
>>should specify that *some* number will be printed out, rather than
>>allowing completely arbitrary behaviour.
>
> When? At any time at all? Copy constructors might
>be used by the compiler almost anywhere. Making it unspecified
>makes conformance testing impossible

No, it doesn't.

>-- right in the
>middle of a conformance test numbers randomly start
>appearing from "no where obvious" (actually from
>copy constructors randomly inserted by the compiler).
>Thats not on.

I was talking about a program with an increment of a global
variable in a copy-constructor, and an output statement in main().
For this program, the compiler might be able to insert
increments of that global variable where-ever and when-ever it
likes, but the test suite can still test conformance.
If the program outputs exactly one integer on one line, then the
implementation passes the test.  If the program dumps core,
or outputs either more than or less than one line, then the
implementation fails the test.

>>> My contribution to the discussion is that I suggested
>>>only PUBLIC copy constructors should be freely inserted
>>>or elided, permitting non-public ones to have side effects.
>>>I'm not sure.
>>
>>Hmm.  I don't think this abides by the "Principle of Least Astonishment".
>>I could imagine that rule being quite confusing in practice.
>
> It seems sensible to me -- the idiom of private
>copy constructors is used to inhibit copying. Imagine
>your "Astonishment" if you got a linker error from a gratuitously
>inserted copy constructor that had deliberately not been
>defined and which you can determine by inspection
>YOU have never invoked.

I agree that that would certainly be more astonishment.
But I was hoping that there was a solution which had less
astonishment than either of these.  In particular, I do
not think compilers should be allowed to insert calls
to copy-constructors whenever they like.  I think the
language standard should clearly specify

 (1) exactly when the basic language semantics would (or might)
     require construction of a temporary by a copy-constructor.
     (Such occaisions should be subject to normal semantic
     restrictions such access control, prevention from initializing
     non-const references with temporaries, etc.)

 (2) exactly when compilers are allowed to elide temporaries
     mention in (1)

 (3) exactly when compilers are allowed to insert additional
     temporaries not mentioned in (1) -- IF AT ALL.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 28 Jun 1994 15:11:53 GMT
Raw View
In article <9417903.20380@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>> b->static_member();
>>
>>There is a proposal that b must not be evaluated because it has no
>>side-effects.
>
>I'm quite happy requiring that b must not be evaluated, but
>I don't think that your reason "because it [b] has no side-effects"
>makes sense.  I'd be a lot happier if you just said "because the value
>of `b' is definitely not needed".
>
>>The point is that if 'b' is an invalid pointer,
>>loading it into a machine register may well have an interesting
>>side effect -- a core dump.
>
>Yes - the reason that you want to disallow evaluation of `b' is
>because evaluating it *can* have side-effects, not because it can't.

 Thats circular. Normally you optimise away things
WITH NO side effects and must leave IN things with them.
Now you argue the opposite -- to elide something BECAUSE
it might have a side effect!

 Consider that 'b' might be a class type with an
overloaded operator->() -- its definitely required to be
evaluated under the new rules. As if:

 b->static_mem()

meant

 b.operator->(), B::static_mem()

the new rules are that the LHS of

 b->mem

has to be evaluated. The proposal is "except if its useless
and has no side effects" -- and somehow thats not
supposed to count "undefined behaviour".

But then I understand your argument -- because I recently
argued the same as you for the case

 b;

or more particularly

 *b;

where b==NULL. <grin>


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@mundil.cs.mu.OZ.AU (Fergus Henderson)
Date: Wed, 29 Jun 1994 02:43:41 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>In article <9417903.20380@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>> b->static_member();
>>>
>>>There is a proposal that b must not be evaluated because it has no
>>>side-effects.
[...]
>>the reason that you want to disallow evaluation of `b' is
>>because evaluating it *can* have side-effects, not because it can't.
>
> Thats circular. Normally you optimise away things
>WITH NO side effects and must leave IN things with them.
>Now you argue the opposite -- to elide something BECAUSE
>it might have a side effect!

It's not an optimization.  It's how the base language is defined.
In the expression `b->static_member()', only the type of `b' matters.
The expression `b' is not evaluated.  Unlike optimization, which is
always implementation dependant, there are no ifs, buts, or maybes.
This corresponds with how `sizeof(b)' works.

Well - hang on, that's how the ARM defined it, but I notice that
the statement "the expression on the left of . or -> is not evaluated"
in ARM 9.4 is not present in the draft working paper.

> Consider that 'b' might be a class type with an
>overloaded operator->() -- its definitely required to be
>evaluated under the new rules.

I guess I should have read both the new rules and the proposal before
jumping into the thick of things ;-)
At least I've read the rules in the working paper now, although
I haven't seen the proposal.

>the new rules are that the LHS of
>
> b->mem
>
>has to be evaluated.

OK, I can see the advanages of this.  Sounds fine.

>The proposal is "except if its useless
>and has no side effects" -- and somehow thats not
>supposed to count "undefined behaviour".

This one I'm a bit more dubious about. What's the advantage?
What does this give you the ordinary "as-if" rule doesn't?
Does the proposal say that if `b' does not have side effects
then it *must* not be evaluated, or just that it doesn't
have to be evaluated?

If it's the latter, then it just corresponds to the "as-if" rule,
so I don't see why it would be necessary.  If it's the former,
then it's a mis-use of the term "side-effects", IMHO.
Whether or not something has any effect on the I/O behavior of
the program (my definition of side-effects) is clearly undecideable,
and it would be obviouly foolish to require compilers to decide the
undecideable.

I guess this explains why you need a new definition of "side-effect"
for this proposal ;-)  I would suggest that you use a new term,
say "presumptive side-effect", if you want to use it to describe some
particular superset of side-effects which is decideable at compile
time.

In any case, this would complicate the language for little benefit,
IMHO.  What's the advantage of the proposal?

>But then I understand your argument -- because I recently
>argued the same as you for the case
>
> b;
>
>or more particularly
>
> *b;
>
>where b==NULL. <grin>

No, the argument in my previous post about the case of static members is
different to your argument about dereferencing a null pointer in an
expression-statement.  Your argument seems to be similar to the proposal
about evaluating objects for static member function calls.

I argued that dereferncing a null pointer in an expresion-statement
should be undefined behaviour, you argued that it should be defined to
do nothing.  Clearly in an expression statement, the expression must in
general be evaluated.  It's not like a static member call or a sizeof
expression where only the type is needed.  The only reason for this to
not be undefined would be if there was a clause "... except if the
expression does not have any *presumtive side-effects*, in which case
the expression *must not* be evaluated.".

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 1 Jul 1994 18:41:50 GMT
Raw View
In article <9418012.4194@mulga.cs.mu.OZ.AU> fjh@mundil.cs.mu.OZ.AU (Fergus Henderson) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>In article <9417903.20380@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>>> b->static_member();
>>>>
>>>>There is a proposal that b must not be evaluated because it has no
>>>>side-effects.
>[...]
>>>the reason that you want to disallow evaluation of `b' is
>>>because evaluating it *can* have side-effects, not because it can't.
>>
>It's not an optimization.  It's how the base language is defined.
>In the expression `b->static_member()', only the type of `b' matters.
>The expression `b' is not evaluated.  Unlike optimization, which is
>always implementation dependant, there are no ifs, buts, or maybes.
>This corresponds with how `sizeof(b)' works.

 Yes, but that was all changed at San Diego. In the case:

 f() -> static_member()

the call to f() is made in case there are side effects --
this is consistent with "not knowing" is "static_member"
is a static member or a non-static one.
>
>>The proposal is "except if its useless
>>and has no side effects" -- and somehow thats not
>>supposed to count "undefined behaviour".
>
>This one I'm a bit more dubious about.

 Me too.

>What's the advantage?
>What does this give you the ordinary "as-if" rule doesn't?

 Invalid pointers. If the pointer does not have to
be "evaluated" the call

 b->static_member()

is fine, if b must be evaluated the call is undefined.

>Does the proposal say that if `b' does not have side effects
>then it *must* not be evaluated, or just that it doesn't
>have to be evaluated?

 MUST not. Optional behaviour is pretty useless in
a Standard: the "lowest common denominator" must be
chosen where portability is desired, the other behaviour
is effectively a vendor extension, and the correct way
to allow vendor extensions is to specify "undefined behaviour"
exactly so the vendor is free to define it.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 23 Jun 1994 09:10:28 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>Is this reflected in their working paper?
>>Or is this a decision that they have made but not yet written down?
>
> The latter I think. The idea is that
> "copy constructors without side effects can be freely inserted
> or elided".

Of course they can.  *Any* code without side effects can be optimized
away or inserted at will.  The question, surely, is when is the
compiler allowed to optimize away copy constructors that *do* have
side-effects.

(Of course, in many situations the compiler will not know whether a copy
constructor has side-effects, and will have to assume that it does, so
allowing optimization of copy constructors that do have side-effects
will also mean that compilers are able to optimize the cases where they
don't have sufficient information.)

>Tom Plum is working on defining what 'side effects' means.

Hmm, I don't quite understand.  A side effect is anything that
can affect the program's observable input/output behaviour, right?
Did you have some other definition in mind?

>An alternative is that copy constructors be REQUIRED to be side
>effect free; that is, a rule that says
>
> "copy constructors may be freely inserted or elided"
>
>means that you may not put side effects in functions or
>your program has undefined behaviour.

Is there any reason to make this undefined behaviour rather than
implementation defined behaviour, as is done in the ARM, or at least
unspecified behaviour?  I don't think implementations should be allowed
to dump core for a program that say counts the number of copy
constructor calls and then prints that number out.  The standard
should not necessarily specify which number will be printed out, but it
should specify that *some* number will be printed out, rather than
allowing completely arbitrary behaviour.

> My contribution to the discussion is that I suggested
>only PUBLIC copy constructors should be freely inserted
>or elided, permitting non-public ones to have side effects.
>I'm not sure.

Hmm.  I don't think this abides by the "Principle of Least Astonishment".
I could imagine that rule being quite confusing in practice.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 20 Jun 1994 09:05:06 GMT
Raw View
jamshid@ses.com (Jamshid Afshar) writes:

>> void f() {
>>    static A a(A());
>> }
>
>Not only is that the simplest piece of code that breaks the most
>number of modern C++ compilers, but it also breaks the most number of
>comp.lang.c++ regulars ;-).

I may well be one of those "broken" by this example ;-)

>>G++ 2.5.5 has a different bug -- it does not ever create the temporary.
>>It never calls the copy constructor -- only the static `a' is created
>>and destroyed.  While it is legal to "elide" unnamed temporaries, the
>>constructor parameter `A()' must be created (and destroyed), and the
>>copy constructor must be called to construct `a'.
>
>I was apparently also wrong about eliding explicitly created
>temporaries.

Well, you managed to convince me.
My first impression was that you were wrong, but when I went looking
in the ARM and the ANSI/ISO working paper for evidence of this,
I couldn't find any.  In fact the evidence seems to support your
original view.

>Consider:
>
> class B {
> public:
>    B(int);
>    B(const B&);
>    ~B();
> };
>
> B b(B(1));
>
>A compiler may construct the temporary B(1), use it to copy-construct
>`b', then destroy the temporary.  Alternatively, the compiler may
>optimize away the temporary and execute the line exactly as if you had
>written:
>
> B b(1);

Do you have any citations from the ANSI/ISO working paper which
confirm this?

>The compiler must still check that you have access to the copy
>constructor, though.

And that the copy constructor takes a _const_ reference, otherwise
initializing it with a temporary would be illegal, right?

>I originally thought this optimization was not
>allowed because the ARM seems to only allow eliding compiler-generated
>temporaries or function parameters and return values.  But, I've been
>told that ANSI/ISO has decided that even:
>
> B b(B(B(B(B(B(B(B(B(B(1))))))))));
>
>may be optimized into one call to the constructor B::B(1).

Is this reflected in their working paper?
Or is this a decision that they have made but not yet written down?

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: jamshid@ses.com (Jamshid Afshar)
Date: Tue, 21 Jun 1994 19:49:46 GMT
Raw View
In article <2u2o7q$eu1@f111.iassf.easams.com.au>,
Rohan LENARD <rjl@f111.iassf.easams.com.au> wrote:
>>In article <CrGIpD.L6C@ses.com>, Jamshid Afshar <jamshid@ses.com> wrote:
>>> void f() {
>>>    static A a(Tmp(1));
>>>    cout << "f() called" << endl;
>>> }
>>
>>This relies on the order of statics and globals being destructed and hence
>>it is undefined what should be defined.
>
>I forgot to add that the committee have been looking at this problem.  A
>proposal has been presented to add a directive like -
>require A;
>to force order of initialisation.

No, my example has nothing to do with static variable initialization
order (as discussed in ARM 3.4).  "static" here refers to lifetime, not
necessarily scope.  A static variable local to a function has a
completely different lifetime than a static variable declared in file
scope.  In fact, a function's static variable might not have any life at
all if the function is never called.  Does your proposal cover the very
different cases of local static variables (statics within a function) and
non-local static variables (which include file-scope statics, globals,
and static data members)?

I don't think the ARM or WP define what happens in the following
situations related to local static variables:

1. // does g()'s b get destroyed before or after f()'s a, or is it
 // implementation-dependent or undefined?

 A::A() { g(); }

 void f() {
    static A a;  // causes construction of g()'s b
 }

 void g() {
    static B b;
 }

2. // is it guaranteed that f()'s a is destroyed before or after
 // h()'s a, or is the order implementation-dependent or undefined?

 A& h() {
    static A a;
    return a;
 }

 void f() {
    static A a = h();
 }

But even if your proposal does discuss these situations, they have
nothing to do with the original example.  There is nothing undefined
about the order of construction and destruction of f()'s `a' or the
temporary, except maybe the timing of the destruction of the temporary.
According to the ARM the temporary can be destroyed anytime immediately
when the A constructor returns up until `a' itself is destroyed.  I
believe the WP requiers it to be destroyed at the end of the statement
(in this case, when the A constructor returns).

 void f() {
    static A a(Tmp(1));
    cout << "f() called" << endl;
 }

Any C++ compiler which does not handle code like this (eg, it doesn't
destroy the temporary or it destroys it each time the function is called)
has a bug.

Jamshid Afshar
jamshid@ses.com




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Jun 1994 21:44:08 GMT
Raw View
In article <9417119.23108@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>>temporaries or function parameters and return values.  But, I've been
>>told that ANSI/ISO has decided that even:
>>
>> B b(B(B(B(B(B(B(B(B(B(1))))))))));
>>
>>may be optimized into one call to the constructor B::B(1).
>
>Is this reflected in their working paper?
>Or is this a decision that they have made but not yet written down?

 The latter I think. The idea is that
 "copy constructors without side effects can be freely inserted
 or elided".

Tom Plum is working on defining what 'side effects' means.
An alternative is that copy constructors be REQUIRED to be side
effect free; that is, a rule that says

 "copy constructors may be freely inserted or elided"

means that you may not put side effects in functions or
your program has undefined behaviour.

 My contribution to the discussion is that I suggested
only PUBLIC copy constructors should be freely inserted
or elided, permitting non-public ones to have side effects.
I'm not sure.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 20 Jun 1994 09:48:09 +1000
Raw View
In article <CrGIpD.L6C@ses.com>, Jamshid Afshar <jamshid@ses.com> wrote:
>Redirected to comp.std.c++.
>
[ ..snip.. ]
>/* test program; should output:
> Tmp constructor called
> A(Tmp) constructor called
> Tmp destructor called
> f() called
> f() called
> f() called
> f() called
> f() called
> A destructor called
>*/

This relies on the order of statics and globals being destructed and hence
it is undefined what should be defined.

I just tested gcc and it does all except 'A destructor called', even though
the destructor is called (you can test this with the debugger).


Regards,
 Rohan

--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: rjl@f111.iassf.easams.com.au (Rohan LENARD)
Date: 20 Jun 1994 10:35:06 +1000
Raw View
In article <2u2lfp$d8b@f111.iassf.easams.com.au>,
Rohan LENARD <rjl@f111.iassf.easams.com.au> wrote:
>In article <CrGIpD.L6C@ses.com>, Jamshid Afshar <jamshid@ses.com> wrote:
>>Redirected to comp.std.c++.
>>
[ ..snip.. ]
>
>This relies on the order of statics and globals being destructed and hence
>it is undefined what should be defined.
>

I forgot to add that the committee have been looking at this problem.  A
proposal has been presented to add a directive like -

require A;

to force order of initialisation.

Regards,
 Rohan
--
------------------------------------------------------------------------
rjl@iassf.easams.com.au |
Rohan Lenard            |                .sig on holiday
+61-2-367-4555          |




Author: jamshid@ses.com (Jamshid Afshar)
Date: Wed, 15 Jun 1994 21:08:01 GMT
Raw View
Redirected to comp.std.c++.

In article <Cr46z2.9D1@ses.com>, Jamshid Afshar <jamshid@ses.com> wrote:
>[...] I nominate the
>following construct as the simplest piece of code that break the most
>number of modern compilers.
>
> void f() {
>    static A a(A());
> }

Not only is that the simplest piece of code that breaks the most
number of modern C++ compilers, but it also breaks the most number of
comp.lang.c++ regulars ;-).  As Anthony Scian (ANTHONY@watcom.on.ca)
and John Spicer (jhs@edg.com) pointed out in email, and John Tam
(johntam@vnet.ibm.com) posted in a followup, the above code is NOT
correct C++.  Watcom C++, IBM C++ and the latest EDG compiler
correctly give an error about the above code because the declaration:

 static A a(A());

must be interpreted as a function declaration named `a' returning an A
and taking one `A(*)()' parameter.  You can't declare static functions
inside another function.  Cfront, g++, Symantec, and BC++ fail to
diagnose the error.  I don't know about Microsoft C++ or DEC C++.

If you're wondering how "A()" can be interpreted as a function pointer
type, remember that function pointer types are treated similarily to
array types when used as parameters.

 void f( int g() );

is treated exactly like the more commonly used:

 void f( int (*g)() );

This is somewhat analogous to the declaration of the array parameter:

 void h( int a[10] );

being treated exactly like:

 void h( int* a );

Anyway, WP 8.2 says "the resolution is that any construct that could
possibly be a type-id in its syntactic context shall be considered a
type-id".  A type-id is "syntactically a declaration of an object or
function of that type that omits the nae of the object or function.
For example:

 int  // int i
 int *  // int *pi
 int (*)[3] // int (*p3i)[3]

WP 8.2 also says that a declaration can be explicitly disambiguated
between a type declaration with a redundant set of parenthesis and an
object declaration by using nonfunction-style casts or an = to
indicate initialization:

 struct S { S(int); };
 void f( double a ) {
    S x( int(a) ); // function declaration
    S y( (int)a ); // object declaration
    S z = int(a); // object declaration
 }

Applying this to the original example, we have to write:

 void f() {
    static A a = A();
    cout << "f() called" << endl;
 }

But please note the intended bug report from the original post is
still valid.  Many modern compilers incorrectly handle the destruction
of temporaries used to initialize a static variable.  Imagine A had a
constructor taking a Tmp object:

 void f() {
    static A a( Tmp(1) );
 }

BC++ 4.0, Cfront 3.0.2, Symantec C++ 6 and Watcom C++ v10 do not
correctly handle this code. These compilers either forget to destroy
the temporary or they try to destroy the temporary every time the
function is called even though the temporary is only constructed once.
Btw, Anthony from Watcom told me this bug in v10 is a slip up because
v9.5 does correctly handle it.  I believe IBM C++ and the latest EDG
also get this right.  Anyone know about DEC C++, MS C++, or Symantec
C++ v7?  A test program is appended.

>G++ 2.5.5 has a different bug -- it does not ever create the temporary.
>It never calls the copy constructor -- only the static `a' is created
>and destroyed.  While it is legal to "elide" unnamed temporaries, the
>constructor parameter `A()' must be created (and destroyed), and the
>copy constructor must be called to construct `a'.

I was apparently also wrong about eliding explicitly created
temporaries.  Consider:

 class B {
 public:
    B(int);
    B(const B&);
    ~B();
 };

 B b(B(1));

A compiler may construct the temporary B(1), use it to copy-construct
`b', then destroy the temporary.  Alternatively, the compiler may
optimize away the temporary and execute the line exactly as if you had
written:

 B b(1);

The compiler must still check that you have access to the copy
constructor, though.  I originally thought this optimization was not
allowed because the ARM seems to only allow eliding compiler-generated
temporaries or function parameters and return values.  But, I've been
told that ANSI/ISO has decided that even:

 B b(B(B(B(B(B(B(B(B(B(1))))))))));

may be optimized into one call to the constructor B::B(1).

Jamshid Afshar
jamshid@ses.com

/* test program; should output:
 Tmp constructor called
 A(Tmp) constructor called
 Tmp destructor called
 f() called
 f() called
 f() called
 f() called
 f() called
 A destructor called
*/

#include <iostream.h>

struct Tmp {
   Tmp(int) { cout << "Tmp constructor called" << endl; }
   ~Tmp() { cout << "Tmp destructor called" << endl; }
};

struct A {
   A() { cout << "A constructor called" << endl; }
   A( const A& ) { cout << "A copy constructor called" << endl; }
   A(Tmp) { cout << "A(Tmp) constructor called" << endl; }
   ~A() { cout << "A destructor called" << endl; }
};

void f() {
   static A a(Tmp(1));
   cout << "f() called" << endl;
}

int main() {
   f();
   f();
   f();
   f();
   f();
   return 0;
}