In the news: GCC 6 breaks Qt 5
-
Reading the news I stumbled over the GCC 6 Release Series Changes and amongst other stuff it says this:
Value range propagation now assumes that the this pointer of C++ member functions is non-null. This eliminates common null pointer checks but also breaks some non-conforming code-bases (such as Qt-5, Chromium, KDevelop). As a temporary work-around -fno-delete-null-pointer-checks can be used. Wrong code can be identified by using -fsanitize=undefined.
So I was like "WTF?". Any comments on this?
-
I think this is already fixed (development list), or at least people are aware of it.
-
It would be interesting how the heck you end up with a null this ... the only case I could think of would be calling a method with a null pointer ...
-
the only case I could think of would be calling a method with a null pointer ...
AFAIK this is undefined behavior in the standard and undefined behavior means it might actually work (i.e. compile and run without crashing), so a check for null this pointer could be used as a defense against such undefined behavior.
The other example I can come up with is something like this:
void killIt(Foo** ptr) { delete *ptr; *ptr = nullptr; } Bar::method() { killIt(this); //this would realistically be hidden deep inside a pile of function calls //this is now null }
All this is of course evil on oh so many levels so I treat it as a contrived example.
-
AFAIK this is undefined behavior in the standard
Quite possible. Although I personally see no problem with calling methods with null pointers, or null
this
in general. It's reflection of the closeness of C++ to the hardware. Methods are oblivious tothis
if you consider the assembly.The other example I can come up with is something like this
But I don't see any difference if you set the pointer to null, or the object just commits suicide and leaves a dangling pointer:
Bar::method() { delete this; // this is now pointing to a very bad place }
and as far as know (but don't hold me to it) the last snippet is allowed ... and if you ask me, it should be allowed ...
-
delete this
is quite useful and allowed. No problem there. The difference is that my example is more likeBar::method() { delete this; this = nullptr; }
I think the reason calling methods on null pointers is undefined behavior, is that it is almost always a bug and not intended behavior. On the other hand, as you said, "this" is meaningless for the low level machine, so imposing any standard dictated runtime checks would be a wasteful overhead. So checking is bad and not checking is (usually) bad -> undefined behavior is the only sane thing to do in the standard.
I personally see no problem with calling methods with null pointers
I guess it's a language design decision. Calling a method without an instance is what static methods are for. If nulls were explicitly allowed in non-static methods you would have
if(!this) return;
or similar on top of virtually every method out there. That would be horrible. -
No problem there. The difference is that my example is more like ...
Sure! However, while this'd look quite awkward syntactically the semantics is quite the same:
this
is pointing to an invalid location. That's what seems strange to me in the referred GCC change. As I see it, no checks are needed, especially for handling some quite obscure bug in code, it's just our responsibility as programmers to do better than that.I think the reason calling methods on null pointers is undefined behavior, is that it is almost always a bug and not intended behavior. On the other hand, as you said, "this" is meaningless for the low level machine, so imposing any standard dictated runtime checks would be a wasteful overhead. So checking is bad and not checking is (usually) bad -> undefined behavior is the only sane thing to do in the standard.
I completely agree!
I guess it's a language design decision. Calling a method without an instance is what static methods are for. If nulls were explicitly allowed in non-static methods you would have if(!this) return; or similar on top of virtually every method out there. That would be horrible.
Granted, but I personally see no reason to forcefully impose that restriction. Nonetheless, as you said, it's probably just a design decision.
-
@kshegunov said:
but I personally see no reason to forcefully impose that restriction
Agreed, there's certainly no technical reason, but I, for one, am glad that c++ has this restriction. I really wouldn't want to review code like this:
static_cast<SomeClass*>(nullptr)->someMethod(42);
As a sidenote to this topic, there are a couple of proposals for C++17 for something called "unified call syntax". Depending on the final wording of the standard it's possible that soon we'll be able to write the above as
someMethod(static_cast<SomeClass*>(nullptr), 42);
which looks a bit less surprising, but personally, I'm not a big fan of this. I think more syntax possibilities brings even more complexity and gotchas to the language, which I don't think C++ is in deficit of.
-
@Chris-Kawa Hooray! More new C++ syntax... :-/
-
I'm not a big fan of this
Neither would any person with any sense ... I can live with C++11 and its somewhat unnecessary additions (not all, but I was perfectly fine writing functors instead of lambdas, and I wouldn't even start on the
auto
'cause it'd would end up as a very long and needlessly opinionated post). But that ... that is ... meh!!I think more syntax possibilities brings even more complexity and gotchas to the language, which I don't think C++ is in deficit of.
... to put it really, really mildly. C++ is already the monster-elephant of complexity ... putting more, in practice, only makes it less usable ...
-
@kshegunov said:
Neither would any person with any sense ...
haha, apparently one of the ones that do would be the creator of the language ;) A bit of background for the unified call proposal. Herb Sutter of Microsoft is even more radical with this. His proposal is that any of
x->f(a,b)
,f(x,a,b)
,f(a,x,b)
,f(a,b,x)
is the same, which is madness if you ask me. -
haha, apparently one of the ones that do would be the creator of the language
He may be, but he's wrong! :)
Thanks for the link though, it's certainly interesting.On a related note, I still don't see how you distinguish these 2 prototypes:
void A::f(int); void f(const A &, int); A a; f(a, 3); //< So, what do we call here or we are to resort to the help of a fortuneteller?
And to pile up no one is preventing anyone from actually invoking "methods" of such structures by
friend
functions. Hell, before having C++ that's how it was done in the first place! :) -
@kshegunov said:
I still don't see how you distinguish these 2 prototypes
I think that's largely the difference between different proposals right now. We'll have to wait and see which (if any) gets voted in. One of them is that "the closest match" is chosen i.e. for
f(a, 3)
a free standing function would be preferred, and, if there isn't one, members of A are checked for match. If I understand correctly the thing that Stroustrup prefers is that these would mean the same, i.e. they would be treated as overloads and abide to the same rules (ambiguous call error in this case), but apparently (fortunately) that would break too much code out there.
I really don't like all this, but to be entirely fair, members are really no different from free functions and "this" injection is just a syntax sugar. On some level it would make sense... i guess... ugh, no, I still don't like it :P -
One of them is that "the closest match" is chosen i.e. for f(a, 3) a free standing function would be preferred, and, if there isn't one, members of A are checked for match.
Which would basically mean to pile on the ever so problematic partial inference of types, and as you said add yet another level of complexity for the programmer. One of the big critiques about C++ is it's implicit conversion (I suppose inherited through C) and this "unified call" syntax just makes that ever more implicit.
but apparently (fortunately) that would break too much code out there.
Thank god for small favors ... :)
I really don't like all this, but to be entirely fair, members are really no different from free functions and "this" injection is just a syntax sugar.
Sure, however the purpose of having members is to have the "this" injection. Otherwise we'd all write functions that operate on POD structures ... oh, well, we used to do it so why would be so reluctant to do it again ...?
On some level it would make sense... i guess... ugh, no, I still don't like it :P
I'm with you, Chris, sadly I don't get to vote!