Bug 31960 - Premature hiding of symbols makes a copy constructor shooting at its own foot
Summary: Premature hiding of symbols makes a copy constructor shooting at its own foot
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.1.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 31961 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-05-16 18:04 UTC by Waldemar Rachwal
Modified: 2007-06-06 21:11 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Waldemar Rachwal 2007-05-16 18:04:33 UTC
When I create an object using copy constructor syntax, giving it other object of that type and (crucial) the same name as the object just being constructed... the compiler treats that "other" object as the object being constructed(!) Similar effects I get with the builtin data types.

Below is the short program demonstrating this (IMO) weird behavior with associated embedded Makefile and two sample outputs (#if 0--commented).

I couldn't find anything about this issue in the standard (draft), but thinking about it deeper since yesterday I couldn't discover any reasonable *application* of such a semantic assuming it was valid. Similar effects with builtin data types may be a dangerous trap for programmers. At least it's very unsafe.

Even if g++ gives at last warnings, they appear only when I've added (sic!) -O1, -O2... options which should have had nothing with the problem encountered (note that in all my tests I tend to use -Wall -Wextra -pedantic -ansi as a basis).

On the other hand, in oppose to semantics/behavior I observe with g++, I have the strong rationale for semantics I expect (in case the standard says nothing or leave it up to compiler implementators).

I get the problem with 4.1.1 on Solaris, with 3.4.4 on Cygwin as well, so I think it might be quite general problem (if not solved yet).

Regards,
WR.

code snippet:

// premature-hiding.cpp

// SUBJECT:
//   Premature hiding of symbols makes copy constructor shooting at its own foot.

// SYNOPSIS:
//   void fun (Seq& seq)  // <- (o)uter symbol
//   {
//       Seq seq(seq);    // symbols legend: (i)nner, (o)uter
//       //   ^i  ^o      // **EXPECTED**
//       //   ^i  ^i      // *G++ACTUAL** :(
//       ...
//   }

#include <iostream>

using namespace std;

class X {
public:
    X ();
    // In my original application I intentionally use non-const argument,
    // but here I put const variant, because:
    // 1) both give the same result, and
    // 2) const one looks more severe, as usually assiociated with "copying".
    explicit X (const X& rhs);
    ~X ();
private:
    int dummy;
};

inline X::X ()
{
    cout << "this ==> " << this << endl;
}

inline X::X (const X& rhs)
{
    cout << "&rhs ++> " << &rhs << endl;
    cout << "this oo> " << this << endl;
}

X::~X ()
{
    cout << "~obj ~~> " << this << endl;
}

int main ()
{
    // Weird behavior is observed already
    // when dealing with simple builtin types.

    cout << endl << "// int n = n + 1;" << endl;
    int n = n + 1;
    cout << "n ==> " << n << endl;

    cout << endl << "// double r = r + 1000.0;" << endl;
    double r = r + 1000.0;
    cout << "r ==> " << r << endl;

    //
    // auto object(s)
    //
    cout << endl << "// X obj;" << endl;
    {
        // Object 'obj' defined. It should be visible by code that follows.
        X obj;

        cout << "//   X obj(obj);" << endl;
        {
            // Object of the same name defined in nested scope
            // is expected to be properly "chained" with the parent object
            // of the outer scope.
            // Unfortunately it doesn't happen :( and GCC
            // allows an object being constructed to "copy" over itself.
            // It looks the symbol is introduced to symbol table
            // on '(' paren, but should be on ')'.
            // The latter (IMO proper) approach should only compile
            // if there is a symbol of that name already defined
            // somewhere earlier (not necessarily in nested {} as in my example)
            // and lexically visible.
            X obj(obj);         // <***** UNEXPECTED BEHAVIOR

            cout << "//     X obj(obj);" << endl;
            {
                // Trying the same on the next (3rd in turn) level...
                X obj(obj);     // <***** UNEXPECTED BEHAVIOR (as above)
            }
        }
    }

    //
    // heap object(s)
    //
    cout << endl << "// X *ptr = new X(*ptr);" << endl;
    X *ptr = new X(*ptr);
    delete ptr;

    return 0;
}

#if 0 // ##### Makefile ##################################################
CXX = g++
CXXFLAGS = -g -Wall -Wextra -pedantic -ansi $(EXTFLAGS)

program := premature-hiding

$(program): $(program).cpp Makefile
        $(CXX) -o $@ $(CXXFLAGS) $<

run: $(program)
        @./$(program)

clean:
        -rm $(program) $(program).exe *.o *.s *.ii
#endif

#if 0 // ##### examining flags ###########################################
make -B CXXFLAGS=       # clean compile, initial surprise
make -B                 # clean too(!!), big surprise
make -B EXTFLAGS='-O1'  # warnings, but compiles (why it has anything with -O1 ?!)
#endif

#if 0 // ##### g++ (GCC) 4.1.1 [sun4u] ###################################

// int n = n + 1;
n ==> 67481

// double r = r + 1000.0;
r ==> 1.96794e+307

// X obj;
this ==> 0xffbfefc8
//   X obj(obj);
&rhs ++> 0xffbfefc4     <****** UNEXPECTED
this oo> 0xffbfefc4
//     X obj(obj);
&rhs ++> 0xffbfefc0     <****** UNEXPECTED
this oo> 0xffbfefc0
~obj ~~> 0xffbfefc0
~obj ~~> 0xffbfefc4
~obj ~~> 0xffbfefc8

// X *ptr = new X(*ptr);
&rhs ++> 0
this oo> 0x214d8
~obj ~~> 0x214d8
#endif

#if 0 // ##### g++ (GCC) 3.4.4 [cygwin] ##################################

// int n = n + 1;
n ==> 1628302664

// double r = r + 1000.0;
r ==> 1000

// X obj;
this ==> 0x22ccb0
//   X obj(obj);
&rhs ++> 0x22cca0       <****** UNEXPECTED
this oo> 0x22cca0
//     X obj(obj);
&rhs ++> 0x22cc90       <****** UNEXPECTED
this oo> 0x22cc90
~obj ~~> 0x22cc90
~obj ~~> 0x22cca0
~obj ~~> 0x22ccb0

// X *ptr = new X(*ptr);
&rhs ++> 0x4
this oo> 0x6c0768
~obj ~~> 0x6c0768
#endif
Comment 1 Andrew Pinski 2007-05-16 19:47:55 UTC
*** Bug 31961 has been marked as a duplicate of this bug. ***
Comment 2 Wolfgang Bangerth 2007-05-17 15:55:23 UTC
Initializer syntax
   int i = i;
is a gcc extension. You may want to read the documentation of -Winit-self.

W.
Comment 3 Waldemar Rachwal 2007-06-06 09:44:08 UTC
[3 weeks went by, but i've not forgotten about my bug report ;]

as about the last comment (#2) I can't fully agree. -Winit-self works, but only in case of builtin scalar data types. For objects the compiler is almost quiet.

Please don't misunderstand me: I don't really want any options to signal a warning on self-initializers (more options, much headache). I really expect well defined, desirable semantics for such constructs because there are cases they are very helpful. Of course, if the current standard says nothing about this or leave up to implementors, and we want to have G++ semantics here defined, there is a room for some options in the meantime until the standard becomes definite.

Please note that scripting languages are well defined here. Below is a simple Perl code (strictness checks if vars are defined, while -w option add extra warning level).

Regards,
WR.

$ perl -Mstrict -e 'my $z = 1; my $z = $z + 1; print "$z\n"'
2
$ perl -w -Mstrict -e 'my $z = 1; my $z = $z + 1; print "$z\n"' 
"my" variable $z masks earlier declaration in same scope at -e line 1.
2
Comment 4 Andrew Pinski 2007-06-06 21:11:45 UTC
>  we want to have G++ semantics here defined

The semantics are defined, just you are invoking other undefined behavior with respect of doing stuff like:
b a;
a = a;

Which are what the semantics of C++ says the two are equaivant.