Yes the reason is obvious, but it’s neither simple nor black and white. One huge problem is that this can cause serious performance regressions, and you have to change your code to opt out, e.g. add “[[indeterminate]]”. There are many, many cases in high performance computing where the intended & desired behavior is don’t touch my variables until I fill them.
This is changing C++ core principles, there’s a new designation for the state of a variable: erroneous. It’s also subtle and weird, because you can still have well-defined behavior even with erroneous state. It does seem like this might be an experiment though, I don’t think this is the end of the story. (It seems they’re already talking some redesign of this idea.)
- It's potentially a performance change in every single function, especially ones that have sizable fixed-size buffers
- If you have regressions you have to spray [[indeterminate]] everywhere, because there is no coarser way of suppressing it.
- While the language says unrecognized attributes are ignored, compilers frequently warn on unrecognized attributes. Clang, for instance, currently warns on [[indeterminate]].
- There is no defined macro name for backwards compatibility.
Which means that libraries are going have to all declare their own macros for [[indeterminate]] and pepper their code with it.The first is that you have a fixed buffer large enough for the maximum message size even though the typical ones aren't that big. You most often write 1% of the buffer and read it back, the other 99% is never accessed.
The second is that you always write the entire contents before reading it but the compiler may not be able to see that.
And the third is that you have a code path where that variable is simply not used.
You would then have the compiler emitting instructions to write zeros that are either overwritten before being read or are never read at all.
Moreover, zero initializing the data doesn't actually remove the bugs when that isn't the case. Consider the first case when you mess up. You have a fixed buffer used to store variable length messages. For the first message the buffer is now zeros instead of uninitialized, but for every subsequent message the remainder of the buffer still contains the remainder of the previous message and subjects you to information disclosure or data modification if you're reading back a different amount than was written in the associated call.
Now consider the second or third case. You unintentionally read from a variable before assigning to it. You get zeros instead of uninitialized memory, but if you weren't expecting zeros, well, the UID field is now 0.
char buffer[LARGE_SIZE];
if(maybe_fill_buffer(buffer, sizeof(buffer))) {
use_result(buffer);
}
The function maybe_fill_buffer() is an external library function that either fills the buffer and returns true or doesn't access it and returns false. Or maybe it unconditionally fills it, or unconditionally returns false without reading from it. The compiler can't see any of that though because it's in an external library. For all it knows that function is going to read from it instead of writing to it.Notice that if it could actually figure it out 99% of the time then it could also emit a warning the 1% of the time that it can't and encourage you to make an explicit choice, which would have been a better option if that was actually the rate.
Say you have some code that should not be reading the initial state and is buggy if it does. Without zero-init, valgrind and msan will give you an immediate and false positive message that your code is wrong-- or forget dynamic analysis: the compiler can often statically tell you that the code will use an uninitialized variable. Zero initialize it and you lose that signal.
Why a false positive? Are you one of those people that 'knows' that their code is correct and always blames the tool? What you describe sounds like a real error. Sort of. The error is not triggered by reading the value, it gets triggered when it is used in some conditional context and the undefinedness has an observable impact on the execution of the program.
This is a change from undefined/erroneous behaviour to something else - defined but maybe not what you wanted.
I agree that this can make error analysis more difficult.