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>