KhronosGroup / SYCL-Docs

SYCL Open Source Specification
Other
109 stars 67 forks source link

Is restriction on calling virtual member functions from device functions too strict? #565

Open AlexeySachkov opened 3 months ago

AlexeySachkov commented 3 months ago

5.4. Language restrictions for device functions lists the following restriction (emphasis mine):

The odr-use of polymorphic classes and classes with virtual inheritance is allowed. However, no virtual member functions are allowed to be called in a device function.

As I read this, no call to a virtual member function is allowed, even if the call is in fact direct, i.e. it does not involve virtual call mechanism. There are a few situations where the latter could occur:

From class.virtual.16:

Explicit qualification with the scope operator ([expr.prim.id.qual]) suppresses the virtual call mechanism. [Example 10: 

class B { public: virtual void f(); };
class D : public B { public: void f(); };
void D::f() { /* ... */ B::f(); }

Here, the function call in D​::​f really does call B​::​f and not D​::​f. — end example]

Also, expr.call.2 (emphasis mine) suggests that there are cases when a call to a virtual member function is not considered to be a virtual call:

If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider in the dynamic type of the object expression is called; such a call is referred to as a virtual function call. [Note 2: The dynamic type is the type of the object referred to by the current value of the object expression. [class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction. — end note]

I.e. even if a selected function is virtual, but the class member access expression is in a certain form, the call is not considered to be a virtual call, but instead it is just a direct call to the said function. I'm properly lost in all those id-expression, qualified-id and other terms from the C++ draft spec, but my understanding is that there is no virtual call mechanism involved in an example below:

struct Base {
  virtual void foo();
};

void bar() {
  Base b;
  b.foo();
}

If my reading of both specs is correct, then we should update the restriction in the SYCL spec to say something like this (emphasis to highlight the diff):

The odr-use of polymorphic classes and classes with virtual inheritance is allowed. However, no virtual functions calls are allowed to be performed in a device function.

I.e. we only disallow cases where virtual call mechanism is involved, but we allow direct (i.e. non-virtual) calls to functions that are defined as virtual.

gmlueck commented 3 months ago

I.e. even if a selected function is virtual, but the class member access expression is in a certain form, the call is not considered to be a virtual call, but instead it is just a direct call to the said function. I'm properly lost in all those id-expression, qualified-id and other terms from the C++ draft spec, but my understanding is that there is no virtual call mechanism involved in an example below:

I think this is the case that expr.call.2 refers to:

struct Base {
  virtual void foo();
};

struct Derive : public Base {
  virtual void foo();
};

int main() {
  Derive d;
  Base *b = &d;
  b->Base::foo();  // Non-virtual function call to Base::foo
}

I.e. we only disallow cases where virtual call mechanism is involved, but we allow direct (i.e. non-virtual) calls to functions that are defined as virtual.

Yes, I agree. I think this proposed change makes sense. We will need to update the error checking in DPC++, though, because we currently diagnose both cases as an error.