I found one more case when compiler thinks that static_cast may return nullptr on non-nullptr argument. It seems that this time there is no undefined behavior, like was before, but maybe I'm missing something.
Here's the code compiled with gcc with -O1 -fPIC -Wnull-dereference -Werror options:
struct IR {
virtual ~IR() = default;
};
struct IF {
virtual ~IF() = default;
virtual void get() {};
};
struct SC : IR, IF {
bool h(); // { return true; };
};
struct HD : public IR {
virtual void g() {
if (r_ && r_->h()) {
static_cast<IF*>(r_)->get();
}
}
SC* r_ = nullptr;
};
bool SC::h() { return true; }
HD hd;
Any one of the following conditions will eliminate potential null pointer dereference warning:
making SC::h() inline;
making IF::get() non-virtual;
changing optimization level from -O1 to -O0;
removing -fPIC option...
So, is there a GCC bug, or still UB in code, or static_cast for non-nullptr can results in nullptr?
Link to godbolt.
If you use -fPIC and don't use an inline function for h, then the compiler can't make any assumptions about the behavior of h because GCC's default semantics are to allow any shared library to override the function with alternative semantics. You can additionally specify -fno-semantic-interposition to allow GCC to optimize under the assumption that other implementations of the function loaded from a shared library will not behave differently.
As far as I understand -Wnull-dereference isn't guaranteed to only warn if there is a definitive null pointer dereference, only if there is a potential one (although documentation seems a bit unclear, see also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86172). The compiler can't be sure that r_->h() doesn't modify r_ for the reason above, and therefore the null pointer check cannot prove that static_cast<IF*>(r_)->get(); won't dereference a null pointer.
The warning doesn't appear for -O0 because it is documented that the warning only works when -fdelete-null-pointer-checks is enabled, which it is only with optimizations enabled.
I guess the warning doesn't appear if get isn't virtual either intentionally because the compiler can be sure that the function call won't actually cause a null dereference in the practical sense even if the object pointer is a null pointer (although it is still UB per standard), or alternatively the function is inlined earlier than the null pointer check is performed. (If the function is virtual the compiler can again make no assumptions about this call, since it could be calling a derived class override instead.)
Related
I've run into some trouble trying to clean up g++ compiler warnings.
Say I have this class:
class A
{
public:
[[noreturn]] virtual void will_throw() { throw 0; }
};
And inside a non-void function I invoke will_throw without returning.
If I do this by value, i.e.:
int g()
{
A a;
a.will_throw();
}
then I get no -Wreturn-type warnings.
If I do it by pointer:
int g()
{
A a;
A* aptr = &a;
aptr->will_throw();
}
Then I get "warning: no return statement in function returning non-void [-Wreturn-type]"
If I remove virtual from the declaration of A::will_throw then calling it on a pointer also produces no warnings. Calling the method on a reference seems to produce a warning if the method is pure-virtual, but not otherwise.
I wasn't able to find anything saying this is how it's supposed to work, and none of these cases produce warnings in Clang. Is this a bug in GCC?
Since the function is virtual, the compiler doesn’t know (without tracking assignments) that the call through a pointer (or reference) is to A::will_throw and not to some overriding function that might not be noreturn. Since it’s just a warning, both behaviors (or never warning, or always warning!) are conforming.
When I am compiling the following program with clang++ version 5.0, it is resulting in
error: initializer on function does not look like a pure-specifier
extern void print(void *ptr);
#define NULL __null
class IInterface
{
public:
virtual void method1() = NULL;
};
int main()
{
void *ptr = NULL;
print(ptr);
return 0;
}
It seems __null is not supported by clang? But some posts in stackoverflow suggests clang supports __null. If so why am I getting this error. Could somebody suggest whats going on here?
Link: https://godbolt.org/z/-f3Kf9
The syntax for pure virtual function is only = 0. For example, virtual void do_stuff() = 0 is allowed, but virtual void do_stuff() = nullptr is not allowed.
Clang does support __null but that compiler internal is not meant to declare virtual functions.
The only reason that NULL might work for pure virtual functions is that some system define it as 0, but you should not rely on that. Some compiler might also use 0LL or (void*)0.
If you can, you should use nullptr instead of NULL for declaring null pointers. This is the best solution and can be emulated in older standards.
For declaring pure virtual functions, use = 0. This is the simplest and the usual way to declare pure virtual functions.
This seems to be some sort of misunderstanding here. Making a virtual function pure is not the same thing as initializing it to a null pointer. In fact, some compilers will set the vtable entry for a pure function to point to a special function called __cxa_pure_virtual or something like that.
Since you are not actually initializing a pointer, you can't use __null here. Not even GCC 4.1 allows it. Nor can you use nullptr, which is the standard replacement for __null. Just write = 0; here.
What is going on here is:
For some reason, you're using a compiler internal (__null) instead of the standard nullptr (at worst, the old NULL was already in the standard and did not need defining by you)
For some reason, you're trying to use __null (or NULL (or nullptr)) as a pure virtual function specifier. You can't do that. It's supposed to be = 0. I believe GCC permits, or used to permit, what you're doing as an extension; Clang doesn't.
Simple as that really!
Your next steps:
Stop using __null for null pointers; use nullptr instead.
Stop using null pointers for pure virtual function specifiers. It's = 0.
I was reading about dynamic_cast and then I encountered the following statement (from cplusplus.com):
Compatibility note: This type of dynamic_cast requires Run-Time Type
Information (RTTI) to keep track of dynamic types. Some compilers
support this feature as an option which is disabled by default. This
needs to be enabled for runtime type checking using dynamic_cast to
work properly with these types.
After the example:
// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;
class Base { virtual void dummy() {} };
class Derived: public Base { int a; };
int main () {
try {
Base * pba = new Derived;
Base * pbb = new Base;
Derived * pd;
pd = dynamic_cast<Derived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast.\n";
pd = dynamic_cast<Derived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast.\n";
} catch (exception& e) {cout << "Exception: " << e.what();}
return 0;
}
What does the author mean by "this type of dynamic_cast"? Isn't dynamic_cast only used for polymorphic classes(almost)? And he mentions this RTTI as something that is needed for the dynamic cast to work, does that mean that you have to use dynamic_cast with caution because you do not know if it is supported fully by the compiler and therefore makes it riskier than the other casting operators which do not need this RTTI?
The compatibility note relates to the immediately preceding paragraph (and code example):
But dynamic_cast can also downcast (convert from pointer-to-base to pointer-to-derived) polymorphic classes (those with virtual members) if -and only if- the pointed object is a valid complete object of the target type.
And it's true: downcasting requires an object of polymorphic type, and RTTI to traverse the object's inheritance tree at runtime.
The other type of dynamic_cast is explained in the paragraph before that:
This naturally includes pointer upcast (converting from pointer-to-derived to pointer-to-base), in the same way as allowed as an implicit conversion.
No RTTI is required here as an object's base(s) is/are always known statically.
So you only need to fully read the surrounding text in order to understand the context of the words you're reading.
I would note, however, that in my experience a compiler with RTTI disabled by default is basically unheard of. I'm not saying that none exist — there may be some niche, industry-specific compilers targeting embedded platforms that do this to save the programmer a few bytes in their Makefile. But the compilers that most people use (GCC, Clang, Visual Studio, ICC, Comeau) all, to the best of my knowledge, pack RTTI as standard and leave it on until you ask for it to be turned off.
The author in the section mentioned by you referred to the cases when you are using dynamic_cast with polymorphic types: to be a little bit more precise, when you write something like dynamic_cast<X*>(p).
In cases like that, you are going to need Run-Time Type Information so that dynamic_cast can be used at all (see the example below).
You can make the compiler disable the generation of such information about every class with virtual functions by using the mentioned compiler option, -fno-rtti, but it's rarely recommended.
"Other" cases are about the usage of dynamic_cast for void*s.
For example, consider the following code:
class A {
public:
virtual ~A() = default;
};
class B : public A {};
int main()
{
A *p = new B();
void *pv = dynamic_cast<void*>(p);
//B *pb = dynamic_cast<B*>(p);
delete p;
return 0;
}
If you compile the code with g++ test.cpp -std=c++11 -fno-rtti, it's gonna be just fine. But, if you do the same after uncommenting B *pb = dynamic_cast<B*>(p);, the compiler is going to give the following error message for this specific line: error: ‘dynamic_cast’ not permitted with -fno-rtti. Note that the cast to void* works even if using -fno-rtti (which has been set either manually or by default).
rtti is expensive at runtime and some embedded systems compile with flags, disabling it. All it gives you are dynamic_cast and typeid.
Therefore, I interpret
because you do not know if it is supported fully by the compiler
as
because your code could have been compiled with rtti disabled.
Let's say I have a class that's something like this:
class View
{
public:
View(DataContainer &c)
: _c(c)
{
}
inline Elem getElemForCoords(double x, double y)
{
int idx = /* some computation here... */;
return _c.data[idx];
}
private:
DataContainer& _c;
};
If I have a function using this class, is the compiler allowed to optimize it away entirely and just inline the data access?
Is the same still true if View::_c happens to be a std::shared_ptr?
If I have a function using this class, is the compiler allowed to
optimize it away entirely and just inline the data access?
Is the same still true if View::_c happens to be a std::shared_ptr?
Absolutely, yes, and yes; as long as it doesn't violate the as-if rule (as already pointed out by Pentadecagon). Whether this optimization really happens is a much more interesting question; it is allowed by the standard. For this code:
#include <memory>
#include <vector>
template <class DataContainer>
class View {
public:
View(DataContainer& c) : c(c) { }
int getElemForCoords(double x, double y) {
int idx = x*y; // some dumb computation
return c->at(idx);
}
private:
DataContainer& c;
};
template <class DataContainer>
View<DataContainer> make_view(DataContainer& c) {
return View<DataContainer>(c);
}
int main(int argc, char* argv[]) {
auto ptr2vec = std::make_shared<std::vector<int>>(2);
auto view = make_view(ptr2vec);
return view.getElemForCoords(1, argc);
}
I have verified, by inspecting the assembly code (g++ -std=c++11 -O3 -S -fwhole-program optaway.cpp), that the View class is like it is not there, it adds zero overhead.
Some unsolicited advice.
Inspect the assembly code of your programs; you will learn a lot and start worrying about the right things. shared_ptr is a heavy-weight object (compared to, for example, unique_ptr), partly because of all that multi-threading machinery under the hood. If you look at the assembly code, you will worry much more about the overhead of the shared pointer and less about element access. ;)
The inline in your code is just noise, that function is implicitly inline anyway. Please don't trash your code with the inline keyword; the optimizer is free to treat it as whitespace anyway. Use link time optimization instead (-flto with gcc). GCC and Clang are surprisingly smart compilers and generate good code.
Profile your code instead of guessing and doing premature optimization. Perf is a great tool.
Want speed? Measure. (by Howard Hinnant)
In general, compilers don't optimize away classes. Usually, they optimize functions.
The compiler may decide to take the content of simple inlined functions and paste the content where the function is invoked, rather than making the inlined function a hard-coded function (i.e. it would have an address). This optimization depends on the compiler's optimization level.
The compiler and linker may decide to drop functions that are not used, whether they be class methods or free standing.
Think of the class as a stencil for describing an object. The stencil isn't any good without an instance. An exception is a public static function within the class (static methods don't require object instances). The class is usually kept in the compiler's dictionary.
Comparing virtual functions in C++ and virtual tables in C, do compilers in general (and for sufficiently large projects) do as good a job at devirtualization?
Naively, it seems like virtual functions in C++ have slightly more semantics, thus may be easier to devirtualize.
Update: Mooing Duck mentioned inlining devirtualized functions. A quick check shows missed optimizations with virtual tables:
struct vtab {
int (*f)();
};
struct obj {
struct vtab *vtab;
int data;
};
int f()
{
return 5;
}
int main()
{
struct vtab vtab = {f};
struct obj obj = {&vtab, 10};
printf("%d\n", obj.vtab->f());
}
My GCC will not inline f, although it is called directly, i.e., devirtualized. The equivalent in C++,
class A
{
public:
virtual int f() = 0;
};
class B
{
public:
int f() {return 5;}
};
int main()
{
B b;
printf("%d\n", b.f());
}
does even inline f. So there's a first difference between C and C++, although I don't think that the added semantics in the C++ version are relevant in this case.
Update 2: In order to devirtualize in C, the compiler has to prove that the function pointer in the virtual table has a certain value. In order to devirtualize in C++, the compiler has to prove that the object is an instance of a particular class. It would seem that the proof is harder in the first case. However, virtual tables are typically modified in only very few places, and most importantly: just because it looks harder, doesn't mean that compilers aren't as good in it (for otherwise you might argue that xoring is generally faster than adding two integers).
The difference is that in C++, the compiler can guarantee that the virtual table address never changes. In C then it's just another pointer and you could wreak any kind of havoc with it.
However, virtual tables are typically modified in only very few places
The compiler doesn't know that in C. In C++, it can assume that it never changes.
I tried to summarize in http://hubicka.blogspot.ca/2014/01/devirtualization-in-c-part-2-low-level.html why generic optimizations have hard time to devirtualize. Your testcase gets inlined for me with GCC 4.8.1, but in slightly less trivial testcase where you pass pointer to your "object" out of main it will not.
The reason is that to prove that the virtual table pointer in obj and the virtual table itself did not change the alias analysis module has to track all possible places you can point to it. In a non-trivial code where you pass things outside of the current compilation unit this is often a lost game.
C++ gives you more information on when type of object may change and when it is known. GCC makes use of it and it will make a lot more use of it in the next release. (I will write on that soon, too).
Yes, if it is possible for the compiler to deduce the exact type of a virtualized type, it can "devirtualize" (or even inline!) the call. A compiler can only do this if it can guarantee that no matter what, this is the function needed.
The major concern is basically threading. In the C++ example, the guarantees hold even in a threaded environment. In C, that can't be guaranteed, because the object could be grabbed by another thread/process, and overwritten (deliberately or otherwise), so the function is never "devirtualized" or called directly. In C the lookup will always be there.
struct A {
virtual void func() {std::cout << "A";};
}
struct B : A {
virtual void func() {std::cout << "B";}
}
int main() {
B b;
b.func(); //this will inline in optimized builds.
}
It depends on what you are comparing compiler inlining to. Compared to link time or profile guided or just in time optimizations, compilers have less information to use. With less information, the compile time optimizations will be more conservative (and do less inlining overall).
A compiler will still generally be pretty decent at inlining virtual functions as it is equivalent to inlining function pointer calls (say, when you pass a free function to an STL algorithm function like sort or for_each).