gcc version 4.7.0 20120201 (experimental) [trunk revision 183790] (GCC) Testcase: #include <string.h> int foo (char *nname, char *oname) { int cmp; while (nname != oname && (cmp = strcmp (nname, oname)) < 0) { break; } if (nname == oname || cmp == 0) { return 1; } else { return 0; } } $ ./xgcc -B. -O2 -Wall ~/init.c -c /home/ryan/init.c: In function 'foo': /home/ryan/init.c:12:29: warning: 'cmp' may be used uninitialized in this function [-Wmaybe-uninitialized] Since both && and || operators guarantee left-to-right evaluation, in the case where nname == oname, strcmp is never called but in the (name == oname || cmp == 0) expression, nname == oname and the second operand is not evaluated so cmp is never used uninitialized.
I haven't followed development for a while, but I don't think GCC uninit analysis is powerful enough to detect this or even simpler cases like PR36550 and PR20968.
Yeah, it's simply too confusing code for GCCs static analysis. Thus the "may be used uninitialized".
If we look at the VRP dump after ASSERT_EXPR insertion it's obvious what needs to happen here: ; basic block 2, loop depth 0, count 0, freq 10000, maybe hot ;; prev block 0, next block 7, flags: (NEW, REACHABLE, VISITED) ;; pred: ENTRY [100.0%] (FALLTHRU,EXECUTABLE) if (nname_6(D) != oname_7(D)) goto <bb 3>; [70.00%] else goto <bb 7>; [30.00%] ;; succ: 3 [70.0%] (TRUE_VALUE,EXECUTABLE) ;; 7 [30.0%] (FALSE_VALUE,EXECUTABLE) ;; basic block 7, loop depth 0, count 0, freq 3000, maybe hot ;; prev block 2, next block 3, flags: (NEW) ;; pred: 2 [30.0%] (FALSE_VALUE,EXECUTABLE) nname_12 = ASSERT_EXPR <nname_6(D), nname_6(D) == oname_7(D)>; oname_15 = ASSERT_EXPR <oname_7(D), oname_7(D) == nname_12>; goto <bb 4>; [100.00%] ;; succ: 4 [100.0%] (FALLTHRU) ;; basic block 3, loop depth 0, count 0, freq 7000, maybe hot ;; prev block 7, next block 4, flags: (NEW, REACHABLE, VISITED) ;; pred: 2 [70.0%] (TRUE_VALUE,EXECUTABLE) nname_11 = ASSERT_EXPR <nname_6(D), nname_6(D) != oname_7(D)>; oname_14 = ASSERT_EXPR <oname_7(D), oname_7(D) != nname_11>; _10 = __builtin_strcmp (nname_11, oname_14); oname_16 = ASSERT_EXPR <oname_14, oname_14 != 0B>; nname_13 = ASSERT_EXPR <nname_11, nname_11 != 0B>; ;; succ: 4 [100.0%] (FALLTHRU,EXECUTABLE) ;; basic block 4, loop depth 0, count 0, freq 10000, maybe hot ;; prev block 3, next block 5, flags: (NEW, REACHABLE, VISITED) ;; pred: 3 [100.0%] (FALLTHRU,EXECUTABLE) ;; 7 [100.0%] (FALLTHRU) # cmp_4 = PHI <_10(3), cmp_8(D)(7)> _1 = nname_6(D) == oname_7(D); _2 = cmp_4 == 0; _3 = _1 | _2; if (_3 != 0) goto <bb 6>; [46.00%] else goto <bb 5>; [54.00%] ;; succ: 6 [46.0%] (TRUE_VALUE,EXECUTABLE) ;; 5 [54.0%] (FALSE_VALUE,EXECUTABLE) If we traverse the edge 2->7, then we know that nname == oname (which is shown in the IL via the ASSERT_EXPRs in BB7). We can use that to simplify the computation of _3 in BB4 and ultimately thread 2->7->6 and eliminate the test of cmp_4 on the threaded path (it becomes dead). That eliminates the uninitialized use. This is fixed on the trunk, most likely due to the work for pr71437 which looks to exploit ASSERT_EXPRs more aggressively.