When a static const reference bound is bound to a temporary, the temporary is destroyed when the enclosing function exits. The lifetime of the temporary should (I believe) be the same as that of the reference. The program below illustrates the problem. The bug did not occur with 3.2.1, 3.2.3 or 3.3. It occurs with 3.3.1, 3.3.5, 3.4.1, 3.4.3 and a cvs checkout of gcc-4_0-branch. Command line and output: -------------------------------------- $ g++ -v -save-temps lifetime.cpp Reading specs from /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/specs Configured with: ../gcc-3.4.3/configure --exec-prefix=/usr/local/gcc-3.4.3 --prefix=/usr/local/gcc-3.4.3 --enable-threads --enable-languages=c,c++ Thread model: posix gcc version 3.4.3 /usr/local/gcc-3.4.3/libexec/gcc/i686-pc-linux-gnu/3.4.3/cc1plus -E -quiet -v -D_GNU_SOURCE lifetime.cpp -mtune=pentiumpro -o lifetime.ii ignoring nonexistent directory "/usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../i686-pc-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../include/c++/3.4.3 /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../include/c++/3.4.3/i686-pc-linux-gnu /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/../../../../include/c++/3.4.3/backward /usr/local/include /usr/local/gcc-3.4.3/include /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/include /usr/include End of search list. /usr/local/gcc-3.4.3/libexec/gcc/i686-pc-linux-gnu/3.4.3/cc1plus -fpreprocessed lifetime.ii -quiet -dumpbase lifetime.cpp -mtune=pentiumpro -auxbase lifetime -version -o lifetime.s GNU C++ version 3.4.3 (i686-pc-linux-gnu) compiled by GNU C version 3.4.3. GGC heuristics: --param ggc-min-expand=47 --param ggc-min-heapsize=32080 as -V -Qy -o lifetime.o lifetime.s GNU assembler version 2.11.93.0.2 (i386-redhat-linux) using BFD version 2.11.93.0.2 20020207 /usr/local/gcc-3.4.3/libexec/gcc/i686-pc-linux-gnu/3.4.3/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/crtbegin.o -L/usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3 -L/usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/../../.. lifetime.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/local/gcc-3.4.3/lib/gcc/i686-pc-linux-gnu/3.4.3/crtend.o /usr/lib/crtn.o -------------------------------------- Preprocessed output: -------------------------------------- # 1 "lifetime.cpp" # 1 "<built-in>" # 1 "<command line>" # 1 "lifetime.cpp" extern "C" int puts(const char*); class Foo { public: Foo() { puts("Foo()"); } Foo(const Foo&) { puts("Foo(const Foo&)"); } ~Foo() { puts("~Foo()"); } }; Foo get_foo() { return Foo(); } void func() { static const Foo& t = get_foo(); } int main() { puts("Before func call 1"); func(); puts("After func call 1"); puts("Before func call 2"); func(); puts("After func call 2"); return 0; } -------------------------------------- Program output: -------------------------------------- Before func call 1 Foo() ~Foo() After func call 1 Before func call 2 ~Foo() After func call 2 -------------------------------------- Expected program output (produced by intel C++ 7.0): -------------------------------------- Before func call 1 Foo() After func call 1 Before func call 2 After func call 2 ~Foo() --------------------------------------
Confirmed. Here's a dejagnu-style testcase: // PR c++/20416. We correctly constructed the temporary S in foo(), // but incorrectly destroyed it every time foo() was called. // { dg-do run } extern "C" void abort (void); namespace { int counter; } struct S { S() { counter++; } S(const S &) { counter++; } ~S() { counter--; } }; void foo (void) { static const S &s = S(); if (counter != 1) abort (); } int main () { if (counter != 0) abort (); foo (); foo (); }
BTW, this is 12.2/5.
*** Bug 27216 has been marked as a duplicate of this bug. ***
*** Bug 29360 has been marked as a duplicate of this bug. ***
Subject: Bug 20416 Author: jason Date: Thu Oct 4 17:58:07 2007 New Revision: 129020 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=129020 Log: PR c++/20416 * call.c (initialize_reference): Handle local static reference temps properly. Added: trunk/gcc/testsuite/g++.dg/init/ref15.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/call.c
Fixed for 4.3.0.
Issue exists in 4.1.2 as well. And it can cause heap corruption from seemingly mundane and regular pieces of code: <snip> #include <string> std::string::size_type foo() { const static std::string& bad = "r="; return bad.size(); } int main() { foo(); foo(); } </snip> valgrind shows "invalid free" ----------------------------- > ERROR SUMMARY: 5 errors from 3 contexts (suppressed: 18 from 9) > > 1 errors in context 1 of 3: > Invalid free() / delete / delete[] > at 0x40054B4: operator delete(void*) (vg_replace_malloc.c:346) > by 0x4095C9C: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.8) > by 0x804871B: foo() (basic_string.h:233) > by 0x804875A: main (A.cpp:13) > Address 0x410e028 is 0 bytes inside a block of size 15 free'd > at 0x40054B4: operator delete(void*) (vg_replace_malloc.c:346) > by 0x4095C9C: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.8) > by 0x804871B: foo() (basic_string.h:233)
(In reply to shri314@yahoo.com from comment #7) > Issue exists in 4.1.2 as well. GCC 4.1 is no longer supported upstream; if you're seeing this with the RHEL5 system compiler, please report it to Red Hat bugzilla. The patch seems like a fine candidate for backporting.