using private method from base in friend sub class -- compiler bug in NVCC?

94 views Asked by At

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?

1

There are 1 answers

0
kalj On BEST ANSWER

I dug some more into this, and saw that it indeed is a problem with one of the phases of the nvcc compilation. Using --save-temps, and checking out the resulting .cu.cpp.ii file, it turns out that this line

b->foo(*b);

gets substituted by the following

(b->internal::Base::foo(*b));

This does not compile with g++, since then the fact that foo is exported as public in Sub is lost. After all, this tries to explicitly access it from the base class where it is private. 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 overloaded void foo(::Other &c) is not declared in Base.