Bug 20416 - Incorrect lifetime for temporary with static const reference
Summary: Incorrect lifetime for temporary with static const reference
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.4.3
: P2 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords: wrong-code
: 27216 29360 (view as bug list)
Depends on:
Blocks: 29843
  Show dependency treegraph
 
Reported: 2005-03-11 06:25 UTC by Chris Kohlhoff
Modified: 2014-07-13 04:56 UTC (History)
8 users (show)

See Also:
Host: i686-pc-linux-gnu
Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu
Known to work: 4.3.0
Known to fail: 3.4.5, 4.1.0, 4.1.1
Last reconfirmed: 2007-10-04 01:45:58


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Kohlhoff 2005-03-11 06:25:38 UTC
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()
--------------------------------------
Comment 1 Richard Sandiford 2005-11-21 16:09:00 UTC
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 ();
}
Comment 2 Richard Sandiford 2005-11-21 16:09:51 UTC
BTW, this is 12.2/5.
Comment 3 Andrew Pinski 2006-04-19 15:08:56 UTC
*** Bug 27216 has been marked as a duplicate of this bug. ***
Comment 4 Andrew Pinski 2006-10-05 20:38:25 UTC
*** Bug 29360 has been marked as a duplicate of this bug. ***
Comment 5 Jason Merrill 2007-10-04 17:58:23 UTC
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

Comment 6 Jason Merrill 2007-10-04 18:00:41 UTC
Fixed for 4.3.0.
Comment 7 shri314@yahoo.com 2014-07-12 02:22:05 UTC
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)
Comment 8 Jason Merrill 2014-07-13 04:56:25 UTC
(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.