> I'm not asking that it ensures that the line after the error executes, I'm asking that it not be eliminated.
I'm not sure what 'eliminated' even mean here. If the pointed to object has been replaced by a register, there might not even be a pointer to check. I.e. there might not be any meaningful code the compiler could generate for the line.
Compiler transformations attempt to preserve semantics of the code, do not attempt a faithful line-by-line translation (beyond toy compilers). If there is no semantic for a line, how can the compiler translate it?
Consider this code:
static inline int * get_x(void* ctx) { return (int*)ctx; }
static inline int indirect( int*(*getter)(void*), void*ctx) {
int * ptr = getter(ctx);
if(!ptr) { return 0; } // 1
return *ptr;
}
int main() {
int x = 10;
return indirect(get_x, &x);
}
After optimization, the code can be (and indeed is: https://godbolt.org/z/5vb9PdnYs) transformed to a simple "return 10". There is no meaningful translation for the line at [1] as there isn't actually any pointer or memory location the pointer could be pointing to in the translated program.
It doesn't even make sense to warn, as main, get_x and indirect could be in completely independent libraries and each make sense as written on its own (getter+ctx is just an hand built closure, so the code is far from non-realistic).
> If the pointed to object has been replaced by a register, there might not even be a pointer to check.
I dunno if that is relevant. In the example you posted, simply removing the `static inline` leaves a pointer to check. The value isn't moved to a register.
We are arguing the cases when the source lines are there, but then are not emitted. In the code samples under discussion, the pointer values are still there and can be checked. Their values are not sitting in some register.
> (getter+ctx is just an hand built closure, so the code is far from non-realistic).
Qualifying with `static inline` is unusual, and none of the objections are using static+inlined code as samples of poor code generation. The examples being presented are a lot more real (as they are taken from existing project, not contrived to make an argument).
The important bit is that the functions are inlined into main, so for that specific code path the check is removed. The fact it would be left there in the non-called function is not really relevant. I used static inline (just inline would have been enough) just to get rid of irrelevant code.
The code under discussion does not compile, so it is hard to discuss it. Please consider this variant: https://godbolt.org/z/EMzr4naMP
As you can see, while the check is still present in the non-inlined foo, main doesn't actually call it and omits the check. There is nothing left in main to check as there is no myvar object left.
This example and my previous one are similar: in both examples, not only the compiler could statically compute the value of the pointer, but it can track the pointee directly ('int x' in my example, the null pointer in your), hence it doesn't need to allocate any memory or registers for the pointer.
Separately, in both cases the compiler was able to statically prove that the pointer must be pointing to something (in my example because it knows the target, in yours because of the assignment through it), so the null check can be removed as the value of the condition is statically known to be always true.
Finally, I believe that in your example GCC found the contradiction with the pointer being both null and not null and realized that the whole main function is not possibly reachable, hence it isolates it with ud2 (clang is just silly).
What code would you expect GCC to generate in main to test the condition? Should it allocate a register, initialize it just for the purpose of performing the branch? Should it do it just for your example or also for mine?
I'm not asking for that. I'm asking that the compiler not remove code because of a prior error!"
If the program prints garbage, that's good enough to fail on a test.
If the program cannot print anything at all, then so be it.
What's actually* happening is that an over-eager compiler is removing something that exists. I'm asking that it not do that.
I'm not asking that it ensures that the line after the error executes, I'm asking that it not be eliminated.