Skip to content

Commit 1c99907

Browse files
authored
[CUDA][HIP] Fix overriding of constexpr virtual function (#121986)
In C++20 constexpr virtual function is allowed. In C++17 although non-pure virtual function is not allowed to be constexpr, pure virtual function is allowed to be constexpr and is allowed to be overriden by non-constexpr virtual function in the derived class. The following code compiles as C++: ``` class A { public: constexpr virtual int f() = 0; }; class B : public A { public: int f() override { return 42; } }; ``` However, it fails to compile as CUDA or HIP code. The reason: A::f() is implicitly host device function whereas B::f() is a host function. Since they have different targets, clang does not treat B::f() as an override of A::f(). Instead, it treats B::f() as a name-hiding non-virtual function for A::f(), and diagnoses it. This causes any CUDA/HIP program using C++ standard header file `<format>` from g++-13 to fail to compile since such usage patten show up there: ``` /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/format:3564:34: error: non-virtual member function marked 'override' hides virtual member function 3564 | _M_format_arg(size_t __id) override | ^ /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/format:3538:30: note: hidden overloaded virtual function 'std::__format::_Scanner<char>::_M_format_arg' declared here 3538 | constexpr virtual void _M_format_arg(size_t __id) = 0; | ^ ``` This is a serious issue and there is no workaround. This patch allows non-constexpr function to override constexpr virtual function for CUDA and HIP. This should be OK since non-constexpr function without explicit host or device attribute can only be called in host functions. Fixes: SWDEV-507350
1 parent c123d0c commit 1c99907

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,7 @@ RISC-V Support
10581058

10591059
CUDA/HIP Language Changes
10601060
^^^^^^^^^^^^^^^^^^^^^^^^^
1061+
- Fixed a bug about overriding a constexpr pure-virtual member function with a non-constexpr virtual member function which causes compilation failure when including standard C++ header `format`.
10611062

10621063
CUDA Support
10631064
^^^^^^^^^^^^

clang/lib/Sema/SemaOverload.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,13 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
13091309
return Ovl_Overload;
13101310
}
13111311

1312+
template <typename AttrT> static bool hasExplicitAttr(const FunctionDecl *D) {
1313+
assert(D && "function decl should not be null");
1314+
if (auto *A = D->getAttr<AttrT>())
1315+
return !A->isImplicit();
1316+
return false;
1317+
}
1318+
13121319
static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
13131320
FunctionDecl *Old,
13141321
bool UseMemberUsingDeclRules,
@@ -1583,6 +1590,7 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
15831590
return true;
15841591
}
15851592

1593+
// At this point, it is known that the two functions have the same signature.
15861594
if (SemaRef.getLangOpts().CUDA && ConsiderCudaAttrs) {
15871595
// Don't allow overloading of destructors. (In theory we could, but it
15881596
// would be a giant change to clang.)
@@ -1595,8 +1603,19 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
15951603

15961604
// Allow overloading of functions with same signature and different CUDA
15971605
// target attributes.
1598-
if (NewTarget != OldTarget)
1606+
if (NewTarget != OldTarget) {
1607+
// Special case: non-constexpr function is allowed to override
1608+
// constexpr virtual function
1609+
if (OldMethod && NewMethod && OldMethod->isVirtual() &&
1610+
OldMethod->isConstexpr() && !NewMethod->isConstexpr() &&
1611+
!hasExplicitAttr<CUDAHostAttr>(Old) &&
1612+
!hasExplicitAttr<CUDADeviceAttr>(Old) &&
1613+
!hasExplicitAttr<CUDAHostAttr>(New) &&
1614+
!hasExplicitAttr<CUDADeviceAttr>(New)) {
1615+
return false;
1616+
}
15991617
return true;
1618+
}
16001619
}
16011620
}
16021621
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -fsyntax-only \
2+
// RUN: -fcuda-is-device %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only %s
4+
5+
// expected-no-diagnostics
6+
7+
#include "Inputs/cuda.h"
8+
9+
class A
10+
{
11+
public:
12+
constexpr virtual int f() = 0;
13+
};
14+
15+
class B : public A
16+
{
17+
public:
18+
int f() override
19+
{
20+
return 42;
21+
}
22+
};
23+
24+
int test()
25+
{
26+
B b;
27+
return b.f();
28+
}

0 commit comments

Comments
 (0)