The following is nonsensical yet compiles cleanly with g++ -Wall -Wextra -Werror -Winit-self
(I tested GCC 4.7.2 and 4.9.0):
#include <iostream> #include <string> int main() { for (int ii = 0; ii < 1; ++ii) { const std::string& str = str; // !! std::cout << str << std::endl; } }
The line marked !!
results in undefined behavior, yet is not diagnosed by GCC. However, commenting out the for
line makes GCC complain:
error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]
I would like to know: why is GCC so easily fooled here? When the code is not in a loop, GCC knows that it is wrong. But put the same code in a simple loop and GCC doesn't understand anymore. This bothers me because we rely quite a lot on the compiler to notify us when we make silly mistakes in C++, yet it fails for a seemingly trivial case.
Bonus trivia:
std::string
to int
and turn on optimization, GCC will diagnose the error even with the loop.-O3
, GCC literally calls the ostream insert function with a null pointer for the string argument. If you thought you were safe from null references if you didn't do any unsafe casting, think again.I have filed a GCC bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 - I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
Undefined behavior exists mainly to give the compiler freedom to optimize. One thing it allows the compiler to do, for example, is to operate under the assumption that certain things can't happen (without having to first prove that they can't happen, which would often be very difficult or impossible).
So, in C/C++ programming, undefined behavior means when the program fails to compile, or it may execute incorrectly, either crashes or generates incorrect results, or when it may fortuitously do exactly what the programmer intended.
In computer programming, undefined behaviour is defined as 'the result of compiling computer code which is not prescribed by the specs of the programming language in which it is written'.
Why doesn't this code: a[i] = i++; work? The subexpression i++ causes a side effect--it modifies i' s value--which leads to undefined behavior since i is also referenced elsewhere in the same expression.
I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
Unlike Clang, GCC doesn't have logic to detect self-initialized references, so getting a warning here relies on the code for detecting use of uninitialized variables, which is quite temperamental and unreliable (see Better Uninitialized Warnings for discussion).
With an int
the compiler can figure out that you write an uninitialized int
to the stream, but with a std::string
there are apparently too many layers of abstraction between an expression of type std::string
and getting the const char*
it contains, and GCC fails to detect the problem.
e.g. GCC does give a warning for a simpler example with less code between the declaration and use of the variable, as long as you enable some optimization:
extern "C" int printf(const char*, ...); struct string { string() : data(99) { } int data; void print() const { printf("%d\n", data); } }; int main() { for (int ii = 0; ii < 1; ++ii) { const string& str = str; // !! str.print(); } } d.cc: In function ‘int main()’: d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized] void print() const { printf("%d\n", data); } ^ d.cc:13:19: note: ‘str’ was declared here const string& str = str; // !! ^
I suspect this kind of missing diagnostic is only likely to affect a handful of diagnostics which rely on heuristics to detect problems. These would be the ones that give a warning of the form "may be used uninitialized" or "may violate strict aliasing rules", and probably the "array subscript is above array bounds" warning. Those warnings are not 100% accurate and "complicated" logic like loops(!) can cause the compiler to give up trying to analyse the code and fail to give a diagnostic.
IMHO the solution would be to add checking for self-initialized references at the point of initialization, and not rely on detecting it is uninitialized later when it gets used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With