https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120987
--- Comment #10 from Tom de Vries <vries at gcc dot gnu.org> --- (In reply to Tom de Vries from comment #4) > and then rethrown: > ... > if (file_exception.reason < 0) > throw_exception (std::move (file_exception)); > ... > > The gdb_exception class contains a message member: > ... > std::shared_ptr<std::string> message; > ... > and the std::move should leave file_exception.message in a "valid but > unspecified state" but gcc seems optimize that away. Let's look at this in a bit more detail, at "-O0 -g". We set a breakpoint at the line containing throw_exception, and run to it. At that point, message is: ... (gdb) p file_exception.message $2 = std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, s td::allocator<char> >> (use count 1, weak count 0) = {get() = 0x1371640} ... We step into throw_exception, until here: ... 188 else if (exception.reason == RETURN_ERROR) > 189 throw gdb_exception_error (std::move (exception)); ... Then step into gdb_exception_error constructor: ... 280 explicit gdb_exception_error (gdb_exception &&ex) noexcept > 281 : gdb_exception (std::move (ex)) ... Then into gdb_exception constructor: ... > 144 explicit gdb_exception (gdb_exception &&other) noexcept = default ... Then into shared_ptr constructor: ... 358 shared_ptr(shared_ptr&& __r) noexcept > 359 : __shared_ptr<_Tp>(std::move(__r)) { } ... Then into __shared_ptr constructor: ... │ 1532 __shared_ptr(__shared_ptr&& __r) noexcept │ │ > 1533 : _M_ptr(__r._M_ptr), _M_refcount() │ │ 1534 { │ │ 1535 _M_refcount._M_swap(__r._M_refcount); │ │ 1536 __r._M_ptr = nullptr; │ │ 1537 } │ ... When entering this function, file_exception.message still has the same value. After the swap, we have: ... (gdb) p file_exception.message $10 = std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> (empty) = {get() = 0x1371640} (gdb) ... and after the nullptr assignment we have: ... gdb) p file_exception.message $12 = std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> (empty) = {get() = 0x0} ... Now with "-O2 -flto=auto". We start out with: ... (gdb) p file_exception.message $2 = std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> (use count 1, weak count 0) = {get() = <optimized out>} ... We step into throw_exception, then gdb_exception_error constructor, then gdb_exception constructor, then shared_ptr constructor, then __shared_ptr constructor. The code is scattered by lto, so I stepi through it. Finally, we reach __cxa_throw. At that point, file_exception.message is still the same (though the optimized out part may have changed): ... (gdb) p file_exception.message $21 = std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> (use count 1, weak count 0) = { get() = <optimized out>} ... but the use count should have been 0 at that point. Looking at the first argument of __cxa_throw, the construction of the exception object went ok: ... (gdb) p /x $rdi $23 = 0xe145b0 (gdb) p ((gdb_exception *)0xe145b0)->message $24 = std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> (use count 1, weak count 0) = {get() = 0xe14620} (gdb) p *((gdb_exception *)0xe145b0)->message $25 = "No symbol table is loaded. Use the \"file\" command." ...