Created attachment 41458 [details] Reproducer I stumbled on this while building the binutils-gdb repo with -fsanitize=address with gcc 7.1.0 and reproduced it with gcc master from today. It doesn't happen with my distro compiler (5.4.0-6ubuntu1~16.04.4). gcc reports that the control can reach the end of the function, but I think it's not the case. This is the smallest reproducer I managed to find. Note that the problem disappears if you enable optimizations (-O > 0) or remove -fsanitize=address. Compile the attached file with: $ /opt/gcc/git/bin/gcc -Wreturn-type -Werror -O0 -fsanitize=address -c test.c test.c: In function 'foo': test.c:23:1: error: control reaches end of non-void function [-Werror=return-type] } ^ cc1: all warnings being treated as errors
Forgot to mention, the initial problem I stumbled on was this function: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=bfd/mach-o-i386.c;h=b2f02415c3ae019224b25184539237d530c5bc7e;hb=HEAD#l114
Looks like it is caused by try { ... } finally { ASAN_MARK (POISON, &n, 4); } which eventually gets to <bb 5> [0.00%]: ASAN_MARK (POISON, &n, 4); switch (finally_tmp.2) <default: <L8> [0.00%], case 1: <L5> [0.00%]> <L5> [0.00%]: <bb 7> [0.00%]: return; <L8> [0.00%]: return D.2131; thus it lacks a return. That's also visible in .original: { int n; int n; bar (&n); switch (i) { case 1:; switch (i) { default:; return 0; } goto <D.2128>; default:; return 0; } <D.2128>:; } but I guess that "dead" break; after the inner switch was previously CFG cleaned-up.
Hm, is the GIMPLE instrumentation w/ ASAN_MARK properly done or is it issues in tree-cfg pass? I'm bit confused here.
So there's explanation what happens: 1) w/o -fsanitize=address: decide_copy_try_finally returns true and so that we copy BB that contains finally statement: foo () { int n; int D.1806; bar (&n); i.0_1 = i; switch (i.0_1) <default: <D.1804>, case 1: <D.1801>> <D.1801>: i.1_2 = i; switch (i.1_2) <default: <D.1802>> <D.1802>: D.1806 = 0; goto <D.1809>; goto <D.1803>; <D.1804>: D.1806 = 0; goto <D.1809>; <D.1803>: n = {CLOBBER}; goto <D.1808>; <D.1809>: n = {CLOBBER}; goto <D.1807>; <D.1808>: return; <D.1807>: return D.1806; } then CFG pass removes the dead BBs. All works fine. 2) w/ -fsanitize=address the decide_copy_try_finally returns false and thus we create switch to dispatch from the finally statement: foo () { int finally_tmp.2; int n; int D.2128; ASAN_MARK (UNPOISON, &n, 4); bar (&n); i.0_1 = i; switch (i.0_1) <default: <D.2126>, case 1: <D.2123>> <D.2123>: i.1_2 = i; switch (i.1_2) <default: <D.2124>> <D.2124>: D.2128 = 0; finally_tmp.2 = 0; goto <D.2131>; goto <D.2125>; <D.2126>: D.2128 = 0; finally_tmp.2 = 0; goto <D.2131>; <D.2125>: finally_tmp.2 = 1; <D.2131>: ASAN_MARK (POISON, &n, 4); switch (finally_tmp.2) <default: <D.2134>, case 1: <D.2132>> <D.2132>: goto <D.2133>; <D.2134>: goto <D.2129>; <D.2133>: return; <D.2129>: return D.2128; } Then CFG removes: Removing basic block 7 ;; basic block 7, loop depth 0 ;; pred: 5 finally_tmp.2 = 1; ;; succ: 8 now finally_tmp.2 can have only one value, but the switch statement, as well as the problematic return BB are not removed.
Can be also simulated with ObjC: $ cat /tmp/objc.m volatile int i; int foo (void) { @try { switch (i) { case 1: switch (i) { default: return 0; } break; default: return 0; } } @finally { i = 2; } } $ gcc /tmp/objc.m -fobjc-exceptions -c -Wall /tmp/objc.m: In function ‘foo’: /tmp/objc.m:23:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ But as the function decide_copy_try_finally depends on optimization level: $ gcc /tmp/objc.m -fobjc-exceptions -c -Wall -O1 [nothing]