It's nice that UBSan will detect the problem at runtime, but that requires one to actually use UBSan in development and have all relevant code paths triggered in a UBSan-instrumented build. Which is unrealistic at least when 3rd party code (libraries etc.) comes into play that is not entirely under the application's control. So I would really favor optimizations that do not make a program less safe, or are only applied when can be proven that they do not cause any harm.
It will be, if you use clang-tidy (or clang's static analyzer):
$ clang-tidy main.cpp -- -std=c++14
1 warning generated.
main.cpp:10:5: warning: Forming reference to null pointer [clang-analyzer-core.NonNullParamChecker]
f_unused_parameter(*i);
^
main.cpp:19:7: note: Passing null pointer value via 1st parameter 'i'
f(nullptr);
^
main.cpp:19:5: note: Calling 'f'
f(nullptr);
^
main.cpp:10:5: note: Forming reference to null pointer
f_unused_parameter(*i);
^That is, undefined behavior propagates not only forwards (if you dereference a pointer, from them on you can be sure that it's not null), but also backwards (if you dereference a null pointer, that's a contradiction since null pointers can't be dereferenced, and the contradiction can only be resolved if the statement is unreachable).
Does that happen when the compiler assumes you'll never take branches that lead to UB, and then finds a path through your branches that more or less does nothing? Or can it happen even if you're not branching at all?
Also, say my program is
int main() {
printf("hello world\n");
do_some_UB();
}
Is the compiler really allowed to produce a program that doesn't print?Yes and yes. How far it will propagate these backwards depends on the compiler.
> Is the compiler really allowed to produce a program that doesn't print?
Yes. A program which contains UB is illegal, there are no guarantees about any of its behaviour, including behaviour which precedes any possible actual invocation of UB, this is pointed rather clearly by the standard:
> However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).