3 ms·
I am not a compiler dev or a high skill coder so my opinion might not matter much but I'll still lay them out here. > The first case is the compiler hint... T
by Lvl999Noob 5y ago
I am not a compiler dev or a high skill coder so my opinion might not matter much but I'll still lay them out here.
> The first case is the compiler hint...
The compiler should refuse to compile if it comes upon such a case. Those hints are as much used by programmers as they are by the compiler. It should emit a warning and necessary checks + abort code if those hints can neither be proven nor disproven statically.
> The second case is uninitialized memory...
For the first example, unless the compiler knows that f() must always be true, it should give a compile time error. In case that it does know that f() is always true, it should still emit a warning (or give an error anyways).
> The third category is traps...
I am honestly not sure about this one. It would depend on the behaviour defined for multiple references / dereferences done simultaneously and type casting. I would still probably expect the compiler to give out warnings atleast.
Edit: language / grammar / typos
- jcranmer 5y agoAs a rule of thumb, yes, compilers should issue warnings where undefined behavior is obviously occurring. (And there is something to be said for compiling with -Werror). However, that's not going to always work, and there are two reasons for this. The first reason is that undefined behavior is ultimately a statement about dynamic execution of the program. The set of expressions that could potentially cause undefined behavior is essentially all of them--every signed arithmetic expression, every pointer dereference, hell, almost all function calls in C++--and figuring out whether or not they actually do cause undefined behavior is effectively impossible at the compiler level. This is why sanitizers were developed, and also why sanitizers only work as a dynamic property. For a concrete example, consider the following code: extern void do_something(int * restrict a, int * restrict b, size_t n); void my_function(int *y, int *z, size_t x, size_t n) { if (x > n) do_something(y, z, n); } This code could produce undefined behavior. Or it could not. It depends on whether or not y and z overlaps. Maybe the check of the if statement is sufficient to guarantee it. Maybe it's not. It's hard to advocate that the compiler should warn, let alone error, about this kind of code. The second issue to be aware of is that there is a general separation of concerns between the part of the compiler that gives warnings and errors (the frontend), and the part that is actually optimizing the code. It is difficult, if not impossible, to give any kind of useful warning or error message in the guts of the optimizer; by the time code reaches that stage, it is often incredibly transformed from the original source code, to the point that its correlation with the original can be difficult to divine. So I once came across some really weird code that broke an optimization pass I was working on. It looked roughly like this (approximate C translation of the actual IR): if (nullptr != nullptr) { int *x = nullptr; do { /* do some stuff with x */ x++; } while (x != nullptr); } What hideous code creates a loop that iterates a pointer through all of memory? Why, this (after reducing the test case): void foo() { std::vector<std::set<int>> x; x.emplace_back(); } So the crazy code was generated from the compiler very heavily inlining the entire details of the STL, and the original code was a more natural iteration from a start to an end value. The compiler figured out enough to realize that the start and end values were both null pointers, but didn't quite manage to actually fully elide the original loop in that case. Warning the user about the resulting undefined behavior in this case is completely counterproductive; it's not arising from anything they did, and there isn't much they can to do to silence that warning.