https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63518
Martin Sebor <msebor at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Last reconfirmed|2019-02-03 00:00:00 |2021-3-29 Known to fail| |10.2.0, 11.0, 4.8.4, 4.9.4, | |5.5.0, 6.4.0, 7.2.0, 8.3.0, | |9.1.0 --- Comment #4 from Martin Sebor <msebor at gcc dot gnu.org> --- This is a similar problem to (although not quite the same as) the one we have been discussing in pr60488. I have changed the test case to C to shorten the IL. Here's the C test case and the IL from a GCC instrumented to print it just as the pass runs. At each point of a variable's use, the warning looks for prior statements that might set its value. If it finds one, it doesn't trigger. In foo() the first such prior statement is the call to setTimeout(&t). In bar(), there is no such prior statement, and so there the warning does trigger. I don't see how to fix this except by running the warning in the front end or just before Gimplification when all the arguments to the wait() call statement are still in the IL. Once the code is Gimplified the arguments are already broken out of the call. This isn't an option today for all the reasons that have been discussed over the years but might perhaps be doable if/when the __builtin_warning() idea comes to fruition. With it in place, the warning could be scheduled to be issued early on, false positives weeded out by running the optimizers as usual, and then issued if the __builtin_warning() call was still left in the IL. This would have to be done under the control of the same predicate analysis as the one in tree-ssa-uninit.c today (which is why I'm factoring it out into a standalone reusable component). $ cat x.c && gcc -S -Wall x.c void wait (int, _Bool); void wait2 (_Bool, int); _Bool setTimeout (int*); void foo (void) { int t; wait (t, setTimeout (&t)); } void bar (void) { int t; wait2 (setTimeout (&t), t); } void foo () { int t; _Bool _1; int _2; int t.0_3; <bb 2> : # .MEM_5 = VDEF <.MEM_4(D)> _1 = setTimeout (&t); <<< address of t escapes first _2 = (int) _1; # VUSE <.MEM_5> t.0_3 = t; <<< missing warning # .MEM_6 = VDEF <.MEM_5> wait (t.0_3, _2); # .MEM_7 = VDEF <.MEM_6> t ={v} {CLOBBER}; # VUSE <.MEM_7> return; } void bar () { int t; int t.1_1; _Bool _2; int _3; <bb 2> : # VUSE <.MEM_4(D)> t.1_1 = t; <<< -Wuninitialized # .MEM_5 = VDEF <.MEM_4(D)> _2 = setTimeout (&t); <<< address of t escapes second _3 = (int) _2; # .MEM_6 = VDEF <.MEM_5> wait2 (_3, t.1_1); # .MEM_7 = VDEF <.MEM_6> t ={v} {CLOBBER}; # VUSE <.MEM_7> return; } _1 = setTimeout (&t); <<< address of t escapes _2 = (int) _1; t.0_3 = t; <<< missing warning wait (t.0_3, _2); t ={v} {CLOBBER}; return; } void bar () { int t; int t.1_1; _Bool _2; int _3; <bb 2> : t.1_1 = t; <<< -Wuninitialized _2 = setTimeout (&t); <<< address of t escapes _3 = (int) _2; wait2 (_3, t.1_1); t ={v} {CLOBBER}; return; } x.c: In function ‘bar’: x.c:15:3: warning: ‘t’ is used uninitialized [-Wuninitialized] 15 | wait2 (setTimeout (&t), t); | ^~~~~~~~~~~~~~~~~~~~~~~~~~ x.c:14:7: note: ‘t’ declared here 14 | int t; | ^