“Uninitialized use” warning in the g++ compiler

2019-04-24 23:32发布

I’m using g++ with warning level -Wall -Wextra and treating warnings as errors (-Werror).

Now I’m sometimes getting an error “variable may be used uninitialized in this function”.

By “sometimes” I mean that I have two independent compilation units that both include the same header file. One compilation unit compiles without error, the other gives the above error.

The relevant piece of code in the header files is as follows. Since the function is pretty long, I’ve only reproduced the relevant bit below.

The exact error is:

'cmpres' may be used uninitialized in this function

And I’ve marked the line with the error by * below.

for (; ;) {
    int cmpres; // *
    while (b <= c and (cmpres = cmp(b, pivot)) <= 0) {
        if (cmpres == 0)
            ::std::iter_swap(a++, b);
        ++b;
    }
    while (c >= b and (cmpres = cmp(c, pivot)) >= 0) {
        if (cmpres == 0)
            ::std::iter_swap(d--, c);
        --c;
    }
    if (b > c) break;
    ::std::iter_swap(b++, c--);
}

(cmp is a functor that takes two pointers x and y and returns –1, 0 or +1 if *x < *y, *x == *y or *x > *y respectively. The other variables are pointers into the same array.)

This piece of code is part of a larger function but the variable cmpres is used nowhere else. Hence I fail to understand why this warning is generated. Furthermore, the compiler obviously understands that cmpres will never be read uninitialized (or at least, it doesn’t always warn, see above).

Now I have two questions:

  1. Why the inconsistent behaviour? Is this warning generated by a heuristic? (This is plausible since emitting this warning requires a control flow analysis which is NP hard in the general case and cannot always be performed.)

  2. Why the warning? Is my code unsafe? I have come to appreciate this particular warning because it has saved me from very hard to detect bugs in other cases – so this is a valid warning, at least sometimes. Is it valid here?

4条回答
Explosion°爆炸
2楼-- · 2019-04-25 00:21

There was an interesting discussion on clang dev-mailing list related to those heuristics this week.

The bottom line is: it's actually quite difficult to diagnose unitialized values without getting exponential behavior...

Apparently (from the discussion), gcc uses a predicate base approach, but given your experience it seems that it is not always sufficient.

I suspect it's got something to do with the fact that the assignment is mixed within the condition (and after a short-circuiting operator at that...). Have you tried without ?

I think both the gcc and clang folks would be very interested by this example since it's relatively common practice in C or C++ and thus could benefit from some tuning.

查看更多
Evening l夕情丶
3楼-- · 2019-04-25 00:26

I would suggest that it's likely a heuristical error- that's what the "may" is for. I suspect that not many loop conditions look quite like that. That code is not unsafe because in all control paths, cmpres is assigned before use. However, I certainly wouldn't find it wrong to initialize it first.

You could, however, have some kind of variable shadowing going on here. That would be the only explanation I could think of for only one of the two translation units giving errors.

查看更多
Bombasti
4楼-- · 2019-04-25 00:31

The code is correct, but the compiler is failing to identify that the variable is never used without initialization.

查看更多
贪生不怕死
5楼-- · 2019-04-25 00:38

An algorithm that diagnoses uninitialized variables with no false negatives or positives must (as a subroutine) include an algorithm that solves the Halting Problem. Which means there is no such algorithm. It is impossible for a computer to get this right 100% of the time.

I don't know how GCC's uninitialized variable analysis works exactly, but I do know it's very sensitive to what early optimization passes have done to the code. So I'm not at all surprised you get false positives only sometimes. It does distinguish cases where it's certain from cases where it can't be certain --

int foo() { int a; return a; }

produces "warning: ‘a’ is used uninitialized in this function" (emphasis mine).

EDIT: I found a case where recent versions of GCC (4.3 and later) fail to diagnose an uninitialized variable:

int foo(int x)
{
    int a;
    return x ? a : 0;
}

Early optimizations notice that if x is nonzero, the function's behavior is undefined, so they assume x must be zero and replace the entire body of the function with "return 0;" This happens well before the pass that generates the used-uninitialized warnings, so there's no diagnostic. See GCC bug 18501 for gory details.

I bring this up partially to demonstrate that production-grade compilers can get uninitialized-variable diagnostics wrong both ways, and partially because it's a nice example of the point that undefined behavior can propagate backward in execution time. There's nothing undefined about testing x, but because code control-dependent on x has undefined behavior, a compiler is allowed to assume that the control dependency is never satisfied and discard the test.

查看更多
登录 后发表回答