https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128312
--- Comment #2 from liu zhenyu <lzy20000419 at outlook dot com> --- gcc/tree-nrv.cc, pass_return_slot::execute (14.2 :362-389) sets [return slot optimization] on `d = f (t)` when dest_safe_for_nrv_p (14.2 :304-320) holds. That function only checks whether the call may clobber or use dest through escaped pointers (call_may_clobber_ref_p, ref_maybe_used_by_stmt_p). It does not check whether the call can throw internally while dest is live on the EH edge. With RSO, f's NRV object r is d itself, so the stores made before the throw are visible in the handler (optimized dump: `d = f (t_7(D)); [return slot optimization]` then `<L5>: _10 = d.a[0];`). The C++ front end already avoids this for MODIFY_EXPRs (gimplify.cc 14.2 :6131-6168, use_target only for INIT or non-addressable targets); the middle-end pass upgrades it without the EH check. Suggested fix: in dest_safe_for_nrv_p, return false when stmt_can_throw_internal (cfun, call) and dest is not an SSA name. A more precise fix would check whether dest is live in the landing pad. I tested the conservative version on 14.2 (fix-14.2.patch): the testcase passes at -O1/-O2/-O3/-Os. The same thing happens in C with setjmp/longjmp at -Os (lj1.c), but that case is less clear-cut, so I'm only mentioning it here.
