https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86899
--- Comment #3 from Jakub Jelinek <jakub at gcc dot gnu.org> --- block_may_fallthru on: try { return <retval> = 0; if (0) { return <retval> = 0; } } finally { A::~A (&a); } suggests that it may fall thru, because generally it looks at the last stmt, it is way too early for dce. Even for the if (0), it would need to prove there are no labels in the block and would need to decide to look at stmts before last too. In *.ehopt we have: try { A::A (&a); try { D.2328 = 0; goto <D.2331>; if (0 != 0) goto <D.2329>; else goto <D.2330>; <D.2329>: D.2328 = 0; // predicted unlikely by early return (on trees) predictor. goto <D.2331>; <D.2330>: } finally { A::~A (&a); } } finally { a = {CLOBBER}; } return; <D.2331>: return D.2328; and even here the if (0) stuff makes gimple_stmt_may_fallthru think it might fall thru, because it ends with a label (D.2330). Because of this the eh lowering decides to use a finally_tmp temporary to track where it should continue after the cleanup: finally_tmp.0 = 0; goto <D.2333>; <D.2330>: finally_tmp.0 = 1; <D.2333>: A::~A (&a); switch (finally_tmp.0) <default: <D.2336>, case 1: <D.2334>> Now, cfg construct with its cleanup makes <bb 2> : A::A (&a); <bb 3> : D.2328 = 0; finally_tmp.0 = 0; <bb 4> : A::~A (&a); if (finally_tmp.0 == 1) goto <bb 5>; [INV] else goto <bb 6>; [INV] <bb 5> : a = {CLOBBER}; return; <bb 6> : a = {CLOBBER}; <bb 7> : <L8>: return D.2328; <bb 8> : <L9>: a = {CLOBBER}; resx 1 out of this, which is unfortunately not good enough for the -Wreturn-type pass that follows - we aren't in SSA form, so there is no easy way during the cfg cleanup to figure out that finally_tmp.0 is ever stored only 0 and do further cfg cleanup.