https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91358
Bug ID: 91358
Summary: Wrong code with dynamic allocation and optional like
class
Product: gcc
Version: 9.1.1
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: middle-end
Assignee: unassigned at gcc dot gnu.org
Reporter: antoshkka at gmail dot com
Target Milestone: ---
The issue is reproduced on GCCs from 5 to 9 with -O2 and -std=c++11. GCC-10
also generates wrong code with -O2 -std=c++11 -fno-allocation-dce.
Source code:
template<class T>
struct optional {
optional() : m_initialized(false) {}
~optional() {
if (m_initialized)
reinterpret_cast<T&>(m_storage).~T();
}
bool m_initialized;
alignas(T) unsigned char m_storage[sizeof(T)];
};
struct NoPtr1 {
void *ptr = nullptr;
~NoPtr1() {
if (ptr) {
__builtin_abort();
}
}
};
static void test(optional<NoPtr1> ) noexcept {
delete new unsigned;
}
void process(optional<NoPtr1> state) {
return test(state);
}
int main() {
process({});
}
The above code generates a conditional jump that depends on uninitialised
value. valgrind complains:
==13823== at 0x4007B2: ~NoPtr1 (main.cpp:18)
==13823== by 0x4007B2: ~optional (main.cpp:7)
==13823== by 0x4007B2: process(optional<NoPtr1>) (main.cpp:29)
==13823== by 0x40067F: main (main.cpp:33)
Running the example under GDB confirms that the destructor of NoPtr1 is called:
(gdb) break main.cpp:18
Breakpoint 1 at 0x400686: main.cpp:18. (2 locations)
(gdb) r
Breakpoint 1, NoPtr1::~NoPtr1 (this=<optimized out>, __in_chrg=<optimized out>)
at main.cpp:18
18 if (ptr) {
(gdb) bt
#0 NoPtr1::~NoPtr1 (this=<optimized out>, __in_chrg=<optimized out>) at
main.cpp:18
#1 optional<NoPtr1>::~optional (this=<optimized out>, __in_chrg=<optimized
out>) at main.cpp:7
#2 process (state=...) at main.cpp:29
#3 0x0000000000400680 in main () at main.cpp:33
(gdb) disassemble
Dump of assembler code for function process(optional<NoPtr1>):
0x0000000000400790 <+0>: push %rbp
0x0000000000400791 <+1>: push %rbx
0x0000000000400792 <+2>: sub $0x8,%rsp
0x0000000000400796 <+6>: mov 0x8(%rdi),%rbx
0x000000000040079a <+10>: movzbl (%rdi),%ebp
0x000000000040079d <+13>: mov $0x4,%edi
0x00000000004007a2 <+18>: callq 0x400600 <_Znwm@plt>
0x00000000004007a7 <+23>: mov %rax,%rdi
0x00000000004007aa <+26>: callq 0x4005f0 <_ZdlPv@plt>
=> 0x00000000004007af <+31>: test %rbx,%rbx
0x00000000004007b2 <+34>: je 0x4007b9 <process(optional<NoPtr1>)+41>
0x00000000004007b4 <+36>: test %bpl,%bpl
0x00000000004007b7 <+39>: jne 0x4007c0 <process(optional<NoPtr1>)+48>
0x00000000004007b9 <+41>: add $0x8,%rsp
0x00000000004007bd <+45>: pop %rbx
0x00000000004007be <+46>: pop %rbp
0x00000000004007bf <+47>: retq
0x00000000004007c0 <+48>: callq 0x4005e0 <abort@plt>