Bug 60367 - Default argument object is not getting constructed
Summary: Default argument object is not getting constructed
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: 4.8.3
Assignee: Jason Merrill
URL:
Keywords: wrong-code
: 58501 59713 61751 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-02-28 17:30 UTC by rob.desbois
Modified: 2015-03-03 18:15 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2014-03-04 00:00:00


Attachments
Preprocessed, minimal testcase triggering unexpected behaviour (74.51 KB, text/plain)
2014-02-28 17:30 UTC, rob.desbois
Details

Note You need to log in before you can comment on or make changes to this bug.
Description rob.desbois 2014-02-28 17:30:06 UTC
Created attachment 32234 [details]
Preprocessed, minimal testcase triggering unexpected behaviour

The attached minimal testcase has the following function with default-constructed default argument:
    void do_something( foo f = {} )
    {
	std::cout << "default argument is at " << &f << std::endl;
    }

The constructor for foo outputs its address; I got the following output from a single run:
    constructed foo @ 0x7ffff10bdb7f
    default argument is at 0x7ffff10bdb60

It shows that only 1 foo was constructed, and not at the same address as that of the default argument. It's been a loooong week, but I can't see anything wrong with the code. In the real code on which this was based, a segfault was occurring when running the destructor of a foo that was move-constructed from the default argument, because the underlying memory was seemingly uninitialised.


build command & output
----------------------
[rob@localhost tests]$ g++ -v -save-temps -Wall -Wextra --std=c++0x -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations default-args-fail.cpp -o ./default-args-fail
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.2/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-isl=/builddir/build/BUILD/gcc-4.8.2-20131212/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.2-20131212/obj-x86_64-redhat-linux/cloog-install --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.2 20131212 (Red Hat 4.8.2-7) (GCC) 
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-Wextra' '-std=c++11' '-fno-strict-aliasing' '-fwrapv' '-fno-aggressive-loop-optimizations' '-o' './default-args-fail' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.2/cc1plus -E -quiet -v -D_GNU_SOURCE default-args-fail.cpp -mtune=generic -march=x86-64 -std=c++11 -Wall -Wextra -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations -fpch-preprocess -o default-args-fail.ii
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2
 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/x86_64-redhat-linux
 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/backward
 /usr/lib/gcc/x86_64-redhat-linux/4.8.2/include
 /usr/local/include
 /usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-Wextra' '-std=c++11' '-fno-strict-aliasing' '-fwrapv' '-fno-aggressive-loop-optimizations' '-o' './default-args-fail' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.2/cc1plus -fpreprocessed default-args-fail.ii -quiet -dumpbase default-args-fail.cpp -mtune=generic -march=x86-64 -auxbase default-args-fail -Wall -Wextra -std=c++11 -version -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations -o default-args-fail.s
GNU C++ (GCC) version 4.8.2 20131212 (Red Hat 4.8.2-7) (x86_64-redhat-linux)
        compiled by GNU C version 4.8.2 20131212 (Red Hat 4.8.2-7), GMP version 5.1.2, MPFR version 3.1.2, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
GNU C++ (GCC) version 4.8.2 20131212 (Red Hat 4.8.2-7) (x86_64-redhat-linux)
        compiled by GNU C version 4.8.2 20131212 (Red Hat 4.8.2-7), GMP version 5.1.2, MPFR version 3.1.2, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 36de953cba87bab1ad58280401e36d59
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-Wextra' '-std=c++11' '-fno-strict-aliasing' '-fwrapv' '-fno-aggressive-loop-optimizations' '-o' './default-args-fail' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 as -v --64 -o default-args-fail.o default-args-fail.s
GNU assembler version 2.23.2 (x86_64-redhat-linux) using BFD version version 2.23.2
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.2/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.2/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.2/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.2/:/usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-Wextra' '-std=c++11' '-fno-strict-aliasing' '-fwrapv' '-fno-aggressive-loop-optimizations' '-o' './default-args-fail' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.2/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o ./default-args-fail /usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.2/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../.. default-args-fail.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-redhat-linux/4.8.2/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.2/../../../../lib64/crtn.o
Comment 1 rob.desbois 2014-03-02 15:14:00 UTC
...having realised that this might look like I just don't grok move construction I expanded my test - adding copy & move constructors & assignment operators to foo and re-running the test still gives the same result, i.e. the address of the function argument is not the address of a constructed object:
    constructed foo @ 0x7fff80e0f25f
    default argument is at 0x7fff80e0f240

(I can attach the enhanced test on request)
Comment 2 Jonathan Wakely 2014-03-02 16:08:32 UTC
Possibly the same issue as Bug 59713
Comment 3 rob.desbois 2014-03-02 22:23:55 UTC
Adding a destructor didn't fix it for me - though it was destroyed for the same address as the constructed object.

    constructed foo @ 0x7fffa012e5ef
    default argument is at 0x7fffa012e5d0
    destructed foo @ 0x7fffa012e5ef

For what it's worth I tried putting a size_t member into the object being constructed, and using a member-initializer to set it to an identifiable bit pattern - even though the temporary was at an 'unconstructed' address, the size_t was repeatedly correct. The gremlins are messing with my head...
Comment 4 rob.desbois 2014-03-03 10:31:52 UTC
The problem only seems to occur when using the pattern "= {}" to default the parameter; "= foo{}" and "= foo()" don't seem to provoke the differing addresses.

I have confirmed that member data set in the default constructor seems to be correct in the temporary despite it being at an 'unconstructed' address. The address of the 'unconstructed' temporary is consistently the size of the nearest whole word to sizeof(foo) below the actually-constructed address, so it seems the address isn't just random but possibly correct in itself.

It seems that the copy/move constructor has been replaced by a memcpy(), thus losing the side-effects. I didn't state earlier but should confirm: this occurs with "-O0" turning off optimizations.

Behaviour with clang (3.3 final) is as expected.
Comment 5 rob.desbois 2014-03-03 11:20:22 UTC
The following is a side-by-side diff of the disassembly of the incorrect version vs. a correct version (defaulting the parameter with = foo{}). The object foo has a single member of type char initialized in default, move, and copy ctors.

 incorrect version                         | correct version
-------------------------------------------|-----------------------------------
push   %rbp                                   push   %rbp
mov    %rsp,%rbp                              mov    %rsp,%rbp
push   %rbx                                   push   %rbx
sub    $0x28,%rsp                          |  sub    $0x18,%rsp
lea    -0x11(%rbp),%rax                       lea    -0x11(%rbp),%rax
mov    %rax,%rdi                              mov    %rax,%rdi
callq  0x400bbe <_ZN3fooC2Ev>              |  callq  0x400bb8 <_ZN3fooC2Ev>
movzbl -0x11(%rbp),%eax                    |  lea    -0x11(%rbp),%rax
mov    %al,-0x30(%rbp)                     <   
lea    -0x30(%rbp),%rax                    <   
mov    %rax,%rdi                              mov    %rax,%rdi
callq  0x4009f0 <_Z6test_a3foo>            |  callq  0x400a50 <_Z6test_b3foo>
lea    -0x11(%rbp),%rax                       lea    -0x11(%rbp),%rax
mov    %rax,%rdi                              mov    %rax,%rdi
callq  0x400bfe <_ZN3fooD2Ev>              |  callq  0x400bf8 <_ZN3fooD2Ev>
mov    $0x0,%eax                              mov    $0x0,%eax
jmp    0x400b65 <main+85>                  |  jmp    0x400b5e <main+78>
mov    %rax,%rbx                              mov    %rax,%rbx
lea    -0x11(%rbp),%rax                       lea    -0x11(%rbp),%rax
mov    %rax,%rdi                              mov    %rax,%rdi
callq  0x400bfe <_ZN3fooD2Ev>              |  callq  0x400bf8 <_ZN3fooD2Ev>
mov    %rbx,%rax                              mov    %rbx,%rax
mov    %rax,%rdi                              mov    %rax,%rdi
callq  0x4008b0 <_Unwind_Resume@plt>          callq  0x4008b0 <_Unwind_Resume@plt>
add    $0x28,%rsp                          |  add    $0x18,%rsp
pop    %rbx                                   pop    %rbx
pop    %rbp                                   pop    %rbp
retq                                          retq



It does look like the incorrect version on the left could be bitwise-copying the default-constructed object.
As an aside - both versions have a single constructor call but TWO destructor calls...
Comment 6 Jonathan Wakely 2014-03-04 11:20:01 UTC
*** Bug 59713 has been marked as a duplicate of this bug. ***
Comment 7 Jonathan Wakely 2014-03-10 18:35:59 UTC
Jason, I don't think this is a regression, but it can have pretty bad results from seemingly harmless code (double free and other crashes caused by shallow copies instead of calling copy constructors)
Comment 8 Jason Merrill 2014-03-10 21:07:32 UTC
Author: jason
Date: Mon Mar 10 21:06:59 2014
New Revision: 208465

URL: http://gcc.gnu.org/viewcvs?rev=208465&root=gcc&view=rev
Log:
	PR c++/60367
	* call.c (convert_default_arg): Remove special handling for
	CONSTRUCTOR.

Added:
    trunk/gcc/testsuite/g++.dg/overload/defarg8.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
Comment 9 Volker Reichelt 2014-03-11 21:08:42 UTC
*** Bug 58501 has been marked as a duplicate of this bug. ***
Comment 10 Jason Merrill 2014-05-13 16:05:33 UTC
Author: jason
Date: Tue May 13 16:05:01 2014
New Revision: 210381

URL: http://gcc.gnu.org/viewcvs?rev=210381&root=gcc&view=rev
Log:
	PR c++/60367
	* call.c (convert_default_arg): Remove special handling for
	CONSTRUCTOR.

Added:
    branches/gcc-4_8-branch/gcc/testsuite/g++.dg/overload/defarg8.C
Modified:
    branches/gcc-4_8-branch/gcc/cp/ChangeLog
    branches/gcc-4_8-branch/gcc/cp/call.c
Comment 11 Jason Merrill 2014-05-13 16:06:30 UTC
Fixed for 4.8.3/4.9.0.
Comment 12 Jonathan Wakely 2014-07-08 17:33:15 UTC
*** Bug 61751 has been marked as a duplicate of this bug. ***
Comment 13 Václav Haisman 2015-03-03 18:13:02 UTC
This bug appears to be affecting 4.7.x series as well. Is there a chance to get this fixed for 4.7 as well?

http://stackoverflow.com/questions/28837142/stdmap-argument-with-default-empty-map-segfaults-in-gcc
Comment 14 Jonathan Wakely 2015-03-03 18:15:28 UTC
(In reply to Václav Zeman from comment #13)
> This bug appears to be affecting 4.7.x series as well. Is there a chance to
> get this fixed for 4.7 as well?

No, the 4.7 branch is closed and there will be no more 4.7.x releases.