This question is motivated by uneven treatment of checking pointer value against nullptr
by clang and gcc. For this
they both emit a warning, but for a pointer taken by using address-of
operator on an object they keep quiet.
I am pretty sure that such pointer should always be valid, because we have experienced bugs due to modern compiler removing such checks on c++ code from happy 90's where it did actually fire.
It bugs me why compilers keep quiet in general case. Is it somehow possible for the if
to trigger, or is it just a design decision in both major compilers? Before I start writing patches or bugging compiler devs, I'd like to be sure I'm not missing something.
#include <iostream>
class A {
void f(){
if(!this) {
std::cout << "This can't trigger, and compilers warn about it.";
}
}
};
void f(A& a){
A* ptr = &a;
if(ptr == nullptr) {
std::cout << "Can this trigger? Because gcc and clang are silent.";
}
}
Even though the question seems pretty dumb, I find it practical. If one does work with smelly code this optimization has fatal results, so a warning would be a really useful diagnostic.
To supplement the case. Both clang and gcc do know that check has constant evaluation, because even for clean code:
void g(A* a){
A* ptr = a;
if(ptr == nullptr) {
std::cout << "Gee, can this trigger? Be cause gcc and clang are silent.";
}
}
void g(A& a) {
g(&a);
}
They generate two versions of g
with if
omitted in g(A& a)
so both are able to determine and assume non-nullability for reference. gcc generates nice readable assembly:
f(A&):
ret
.LC0:
.string "Can this trigger? Be cause gcc and clang are silent."
g(A*):
test rdi, rdi
je .L5
ret
.L5:
mov edx, 52
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
g(A&):
ret
As far as I understand assembly msvc /O2
and icc -fast
leave the check in place.
EDIT: I've missed !
in A::f()
, fixed it.