While trying to compile some CUDA code using Intel Threading Building Blocks, I discovered what I think is a bug in nvcc. The following minimal example compiles fine using g++ 5.4:
class Sub;
class Other;
namespace internal {
class Base
{
private:
friend class ::Sub;
static void foo(::Sub& b);
static void foo(::Other& c);
};
}
class Sub : private internal::Base
{
public:
using internal::Base::foo;
};
void internal::Base::foo(Sub& b)
{
}
int main(int argc, char *argv[])
{
Sub *b;
b->foo(*b);
// Sub::foo(*b);
return 0;
}
But if I compile it with nvcc 8.0 combined with the same host compiler, using
nvcc -x cu -arch=sm_35 -c minimal.cc
I get the following interesting error:
../minimal.cc: In function ‘int main(int, char**)’:
../minimal.cc:28:21: error: ‘internal::Base’ is an inaccessible base of ‘Sub’
A somewhat more descriptive error is obtained if Base is moved out of the internal namespace and into to global namespace:
../minimal.cc: In function ‘int main(int, char**)’:
../minimal.cc:6:12: error: ‘class Base Base::Base’ is inaccessible
class Base
^
../minimal.cc:32:5: error: within this context
b->foo(*b);
^
../minimal.cc:32:11: error: ‘Base’ is an inaccessible base of ‘Sub’
b->foo(*b);
Clearly, this seem to be due to the somewhat non-standard way of calling the static method using a pointer, and if that row is replaced by the one commented out, it compiles just fine.
Can someone confirm if this is valid C++ and thus a bug in nvcc, or invalid C++ that g++ somehow still happily accepts?
I dug some more into this, and saw that it indeed is a problem with one of the phases of the
nvcccompilation. Using--save-temps, and checking out the resulting.cu.cpp.iifile, it turns out that this linegets substituted by the following
This does not compile with
g++, since then the fact thatfoois exported as public inSubis lost. After all, this tries to explicitly access it from the base class where it isprivate. Using the other type of invocation (Sub::foo) does not result in any extra code being generated.I conclude that this is an bug in
nvcc. It is interesting that this substitution does not happen if the second overloadedvoid foo(::Other &c)is not declared inBase.