[net.lang.c++] Bug in C++ version 1.0

solomon@crystal.UUCP (Marvin Solomon) (03/28/86)

Summary:  Under some circumstances, the destructor for a compiler-generated
temporary is called, even if though the temporary is never initialized.

Description:  The C++ compiler sometimes allocates automatic variables
for intermediate results in expressions.  The destructor for such a variable
is called "at the first opportunity, typically immediately after the
statement in which it was created." [The C++ Programming Language,
section 6.3.2, p. 174].  However, if the expression occurs inside
a conditional expression (... ? ... : ...), it may happen that the
temporary is only initialized in a branch of the conditional that
is not executed.  In such a case, the destructor is called on an
unitialized object.  The problem is that "after the statement" is not
"the first opportunity".

Repeat-by:
Compile and run the following program:
    extern void printf(...);
    int flag = 0;

    struct node {
        int x,y;
        node(int i, int j) { x=i; y=j; }
        ~node() { printf("delete %x(%d,%d)\n",this,x,y); }
    };

    node operator+(node a,node b) {
        return node(a.x+b.x, a.y+b.y);
    }

    node *copy(node& a) {
        return  new node(a.x,a.y);
    }

    main() {
        node a(1,1),b(2,2);
        node *c;
        { node junk(17,17); }
        
        c = (flag ? copy(a+b) : 0);
    }

The result is something like this:

    delete 7fffe5c4(17,17)
    delete 7fffe5c4(17,17)
    delete 7fffe5d8(1,1)
    delete 7fffe5d0(2,2)

The first line is generated by the destruction of 'junk'.
The third and fourth are are from 'a' and 'b'.
The second line is an attempt to delete a temporary that is supposed
to hold the result of 'a+b', but instead contains the left-over value
of 'junk' since it is in the same place on the stack.

Here is the C code generated by the last statement, edited for
readability:

{ struct node _auto__I2 ; 

    _auto_c = (
        flag
            ? copy (
                ( struct node * ) (
                    ( _auto__I2 = _plusFCnode__Cnode___(_auto_a, _auto_b )),
                    ( & _auto__I2 )
                )
            )
            : ( ( ( struct node * ) ( 0 ) ) )
    ) ; 

    { printf ( "delete %x(%d,%d)\n",
        ( ( struct node * ) ( & _auto__I2 ) ) ,
        ( ( struct node * ) ( & _auto__I2 ) ) -> _node_x ,
        ( ( struct node * ) ( & _auto__I2 ) ) -> _node_y ) ; 
    ; } 
    { printf ( "delete %x(%d,%d)\n",
        ( ( struct node * ) ( & _auto_a ) ) , 
        ( ( struct node * ) ( & _auto_a ) ) -> _node_x ,
        ( ( struct node * ) ( & _auto_a ) ) -> _node_y ) ; 
    ; } 
    { printf ( "delete %x(%d,%d)\n",
        ( ( struct node * ) ( & _auto_b ) ) , 
        ( ( struct node * ) ( & _auto_b ) ) -> _node_x ,
        ( ( struct node * ) ( & _auto_b ) ) -> _node_y ) ; 
    ; }
}
-- 
	Marvin Solomon
	Computer Sciences Department
	University of Wisconsin, Madison WI
	solomon@uwisc
	...{ihnp4,seismo,allegra}!uwvax!solomon