$ gcc --version gcc (GCC) 10.2.0 $ cat test.c #include <stdio.h> #include <string.h> #define RECLEN 128 struct R { int head; int code; char text[1]; }; const char* fun(int n, const struct R* p) { union { struct R r; char rec[RECLEN]; } x; const char* err = 0; memset(&x, 0, sizeof(x)); if (p) memcpy(&x.r, p, sizeof(*p)); else err = "Invalid argument"; if (!err) { static const char magic[] = "MAGIC"; const char* msg; if (memcmp(x.rec, magic, sizeof(magic)-1) == 0) msg = x.rec; else if (x.r.text[0]) msg = x.r.text; else msg = "Error w/code"; if (msg) printf("%s\n", msg); if (x.rec <= msg && msg < x.rec + sizeof(x)) err = "Error detected"; else err = msg; } else printf("%s\n", err); return err; } $ gcc -Wall -O2 -c test.c test.c: In function ‘fun’: test.c:45:12: warning: function may return address of local variable [-Wreturn-local-addr] 45 | return err; | ^~~ test.c:19:7: note: declared here 19 | } x; | ^ test.c:19:7: note: declared here Noted that does not matter whether "sizeof(x)" or "sizeof(x.rec)" is used at the end of the "if()" statement on line 39.
Note, the following condition in the if statement if (x.rec <= msg && msg < x.rec + sizeof(x)) Is undefined if msg is not in the range of x.rec[0]...x.rec[RECLEN] .
> undefined if msg is not in the range of x.rec[0]...x.rec[RECLEN] Indeed for the segmented data address space. But in most systems it's linear, and the warning is then architecture dependent, and is only expected on those, where the comparison cannot be safely made. Besides, the warning then should be about that fact, not the return address being of a local variable -- that's misleading, at best.
The warning behaves as designed. There's no way for it tell from the IL that the function cannot return the address of x: <bb 7> [local count: 375809640]: # msg_21 = PHI <"Error w/code"(6), msg_19(10)> if (&MEM <char[128]> [(void *)&x + 128B] > msg_21) goto <bb 9>; [50.00%] else goto <bb 8>; [50.00%] <bb 8> [local count: 187904820]: <bb 9> [local count: 1073741824]: # err_3 = PHI <msg_21(8), "Invalid argument"(4), "Error detected"(7), "Error w/code"(6)> x ={v} {CLOBBER}; return err_3; <<< -Wreturn-local-addr <bb 10> [local count: 375809641]: # msg_19 = PHI <&x.r.text(5), &x.rec(3)> <<< x is on stack __builtin_puts (msg_19); goto <bb 7>; [100.00%] } As Andrew explained, the pointer relational expression is undefined if msg points to the string literal (changing it to equality avoids the warning). In addition, and arguably more important, GCC's ability to extract useful information from pointer relationships is quite limited so it doesn't "see through" the expression. The underlying problem (and the limitation) can be reduced to the failure to fold and the false positive for the much simpler test case below. For a + n to be valid n must be zero or one, and so the function must return null. If GCC used that to fold the function the warning wouldn't trigger. With that, I think the report can be resolved as invalid (I'm sure the pointer range optimization has its own bug.) $ cat z.c && gcc -O2 -S -Wall -fdump-tree-isolate-paths=/dev/stdout z.c void* f (int n) { char a[1], *p = a + n; if (p <= a + 1) p = 0; return p; } ;; Function f (f, funcdef_no=0, decl_uid=1943, cgraph_uid=1, symbol_order=0) z.c: In function ‘f’: z.c:6:10: warning: function may return address of local variable [-Wreturn-local-addr] 6 | return p; | ^ z.c:3:8: note: declared here 3 | char a[1], *p = a + n; | ^ SSA replacement table N_i -> { O_1 ... O_j } means that N_i replaces O_1, ..., O_j p_7 -> { p_2 } .MEM_8 -> { .MEM_6 } Incremental SSA update started at block: 2 Number of blocks in CFG: 6 Number of blocks to update: 2 ( 33%) Removing basic block 3 void * f (int n) { char * p; char a[1]; sizetype _1; <bb 2> [local count: 1073741824]: _1 = (sizetype) n_3(D); p_4 = &a + _1; if (&MEM <char[1]> [(void *)&a + 1B] >= p_4) goto <bb 3>; [100.00%] else goto <bb 4>; [0.00%] <bb 3> [local count: 311385128]: # p_2 = PHI <0B(2)> a ={v} {CLOBBER}; return p_2; <bb 4> [count: 0]: # p_7 = PHI <p_4(2)> a ={v} {CLOBBER}; return 0B; }
The pointer limitation is discussed in more detail in pr91227.