Skip to content

Commit d88250f

Browse files
committed
[Clang][HIP] Suppress availability diagnostics for mismatched host/device overloads
Outside of function bodies, the resolution of host/device overloads for functions in HIP/CUDA operates as if in a host-device context. This means that the device overload is used in the device compilation phase and the host overload is used in the host compilation phase. Therefore, the following code would cause a deprecation warning during host compilation, even though val is only used as part of a device function: __attribute__((host, deprecated)) constexpr int val(void) {return 1;} __attribute__((device)) constexpr int val(void) {return 1;} __attribute__((device)) std::enable_if<(val() > 0), int>::type fun(void) { return 42; } As only the available device overload is used during device compilation, where code for fun is actually generated, this diagnostic is spurious. This patch suppresses availability diagnostics in such situations: When an unavailable host function is used in a device context during host compilation or when an unavailable device function is used in a host context during device compilation.
1 parent f1d13bb commit d88250f

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

clang/lib/Sema/SemaAvailability.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "clang/Sema/DelayedDiagnostic.h"
2121
#include "clang/Sema/ScopeInfo.h"
2222
#include "clang/Sema/Sema.h"
23+
#include "clang/Sema/SemaCUDA.h"
2324
#include "clang/Sema/SemaObjC.h"
2425
#include "llvm/ADT/StringRef.h"
2526
#include <optional>
@@ -156,6 +157,58 @@ static bool ShouldDiagnoseAvailabilityInContext(
156157
}
157158
}
158159

160+
if (S.getLangOpts().CUDA || S.getLangOpts().HIP) {
161+
// In CUDA/HIP, do not diagnose uses of unavailable host or device function
162+
// overloads when they occur in the context of a Decl with an explicitly
163+
// given opposite target.
164+
// We encounter this if the OffendingDecl is used outside of a function
165+
// body, e.g., in template arguments for a function's return or parameter
166+
// types. In this case, overloads of the called function are resolved as if
167+
// in a host-device context, i.e., the device overload is chosen in the
168+
// device compilation phase and the host overload in the host compilation
169+
// phase. As code is only generated for the variant with matching targets,
170+
// an availabiliy diagnostic for the variant with non-matching targets would
171+
// be spurious.
172+
173+
if (auto *OffendingFunDecl = llvm::dyn_cast<FunctionDecl>(OffendingDecl)) {
174+
Decl *ActualCtx = Ctx;
175+
if (auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(Ctx)) {
176+
// Attributes of template Decls are only on the templated Decl
177+
ActualCtx = FTD->getTemplatedDecl();
178+
}
179+
if (auto *CtxFun = llvm::dyn_cast<FunctionDecl>(ActualCtx)) {
180+
auto TargetIs = [&S](const FunctionDecl *FD, CUDAFunctionTarget FT) {
181+
return S.CUDA().IdentifyTarget(FD, /* IgnoreImplicitHDAttr */ true) ==
182+
FT;
183+
};
184+
185+
bool CtxIsHost = TargetIs(CtxFun, CUDAFunctionTarget::Host);
186+
bool CtxIsDevice = TargetIs(CtxFun, CUDAFunctionTarget::Device);
187+
188+
bool OffendingDeclIsHost =
189+
TargetIs(OffendingFunDecl, CUDAFunctionTarget::Host);
190+
bool OffendingDeclIsDevice =
191+
TargetIs(OffendingFunDecl, CUDAFunctionTarget::Device);
192+
193+
// There is a way to call a device function from host code (and vice
194+
// versa, analogously) that passes semantic analysis: As constexprs,
195+
// when there is no host overload. In this case, a diagnostic is
196+
// necessary. Characteristic for this situation is that the device
197+
// function will also be used in a host context during host compilation.
198+
// Therefore, only suppress diagnostics if a host function is used in a
199+
// device context during host compilation or a device function is used
200+
// in a host context during device compilation.
201+
bool CompilingForDevice = S.getLangOpts().CUDAIsDevice;
202+
bool CompilingForHost = !CompilingForDevice;
203+
204+
if ((OffendingDeclIsHost && CtxIsDevice && CompilingForHost) ||
205+
(OffendingDeclIsDevice && CtxIsHost && CompilingForDevice)) {
206+
return false;
207+
}
208+
}
209+
}
210+
}
211+
159212
// Checks if we should emit the availability diagnostic in the context of C.
160213
auto CheckContext = [&](const Decl *C) {
161214
if (K == AR_NotYetIntroduced) {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify=expected,onhost %s
2+
// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -fsyntax-only -fcuda-is-device -verify=expected,ondevice %s
3+
4+
template <bool C, class T = void> struct my_enable_if {};
5+
6+
template <class T> struct my_enable_if<true, T> {
7+
typedef T type;
8+
};
9+
10+
__attribute__((host, device)) void use(int x);
11+
12+
__attribute__((device)) constexpr int OverloadFunHostDepr(void) { return 1; }
13+
__attribute__((host, deprecated("Host variant"))) constexpr int OverloadFunHostDepr(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}}
14+
15+
16+
__attribute__((device, deprecated("Device variant"))) constexpr int OverloadFunDeviceDepr(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}}
17+
__attribute__((host)) constexpr int OverloadFunDeviceDepr(void) { return 1; }
18+
19+
20+
template<typename T>
21+
__attribute__((device)) constexpr T TemplateOverloadFun(void) { return 1; }
22+
23+
template<typename T>
24+
__attribute__((host, deprecated("Host variant"))) constexpr T TemplateOverloadFun(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}}
25+
26+
27+
__attribute__((device, deprecated)) constexpr int // expected-note 0+ {{has been explicitly marked deprecated here}}
28+
DeviceOnlyFunDeprecated(void) { return 1; }
29+
30+
__attribute__((host, deprecated)) constexpr int // expected-note 0+ {{has been explicitly marked deprecated here}}
31+
HostOnlyFunDeprecated(void) { return 1; }
32+
33+
class FunSelector {
34+
public:
35+
template<int X> __attribute__((device))
36+
auto devicefun(void) -> typename my_enable_if<(X == OverloadFunHostDepr()), int>::type {
37+
return 1;
38+
}
39+
40+
template<int X> __attribute__((device))
41+
auto devicefun(void) -> typename my_enable_if<(X != OverloadFunHostDepr()), int>::type {
42+
return 0;
43+
}
44+
45+
template<int X> __attribute__((device))
46+
auto devicefun_wrong(void) -> typename my_enable_if<(X == OverloadFunDeviceDepr()), int>::type { // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
47+
return 1;
48+
}
49+
50+
template<int X> __attribute__((device))
51+
auto devicefun_wrong(void) -> typename my_enable_if<(X != OverloadFunDeviceDepr()), int>::type { // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
52+
return 0;
53+
}
54+
55+
template<int X> __attribute__((host))
56+
auto hostfun(void) -> typename my_enable_if<(X == OverloadFunDeviceDepr()), int>::type {
57+
return 1;
58+
}
59+
60+
template<int X> __attribute__((host))
61+
auto hostfun(void) -> typename my_enable_if<(X != OverloadFunDeviceDepr()), int>::type {
62+
return 0;
63+
}
64+
65+
template<int X> __attribute__((host))
66+
auto hostfun_wrong(void) -> typename my_enable_if<(X == OverloadFunHostDepr()), int>::type { // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
67+
return 1;
68+
}
69+
70+
template<int X> __attribute__((host))
71+
auto hostfun_wrong(void) -> typename my_enable_if<(X != OverloadFunHostDepr()), int>::type { // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
72+
return 0;
73+
}
74+
};
75+
76+
77+
// These should not be diagnosed since the device overload of
78+
// OverloadFunHostDepr is not deprecated:
79+
__attribute__((device)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type
80+
DeviceUserOverloadFunHostDepr1(void) { return 2; }
81+
82+
my_enable_if<(OverloadFunHostDepr() > 0), int>::type __attribute__((device))
83+
DeviceUserOverloadFunHostDepr2(void) { return 2; }
84+
85+
__attribute__((device))
86+
my_enable_if<(OverloadFunHostDepr() > 0), int>::type constexpr
87+
DeviceUserOverloadFunHostDeprConstexpr(void) { return 2; }
88+
89+
90+
// Analogously for OverloadFunDeviceDepr:
91+
__attribute__((host)) my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type
92+
DeviceUserOverloadFunDeviceDepr1(void) { return 2; }
93+
94+
my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type __attribute__((host))
95+
DeviceUserOverloadFunDeviceDepr2(void) { return 2; }
96+
97+
__attribute__((host))
98+
my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type constexpr
99+
DeviceUserOverloadFunDeviceDeprConstexpr(void) { return 2; }
100+
101+
102+
// Actual uses of the deprecated overloads should be diagnosed:
103+
__attribute__((host, device)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
104+
HostDeviceUserOverloadFunHostDepr(void) { return 3; }
105+
106+
__attribute__((host)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type constexpr // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
107+
HostUserOverloadFunHostDeprConstexpr(void) { return 3; }
108+
109+
__attribute__((device)) my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type constexpr // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
110+
HostUserOverloadFunDeviceDeprConstexpr(void) { return 3; }
111+
112+
113+
// Making the offending decl a template shouldn't change anything:
114+
__attribute__((host)) my_enable_if<(TemplateOverloadFun<int>() > 0), int>::type // onhost-warning {{'TemplateOverloadFun<int>' is deprecated: Host variant}}
115+
HostUserTemplateOverloadFun(void) { return 3; }
116+
117+
__attribute__((device)) my_enable_if<(TemplateOverloadFun<int>() > 0), int>::type
118+
DeviceUserTemplateOverloadFun(void) { return 3; }
119+
120+
121+
// If the constexpr function is actually called from the mismatched context, diagnostics should be issued:
122+
__attribute__((host))
123+
my_enable_if<(DeviceOnlyFunDeprecated() > 0), int>::type constexpr // onhost-warning {{'DeviceOnlyFunDeprecated' is deprecated}}
124+
HostUserDeviceOnlyFunDeprecated(void) { return 3; }
125+
126+
__attribute__((device))
127+
my_enable_if<(HostOnlyFunDeprecated() > 0), int>::type constexpr // ondevice-warning {{'HostOnlyFunDeprecated' is deprecated}}
128+
DeviceUserHostOnlyFunDeprecated(void) { return 3; }
129+
130+
// Diagnostics for uses in function bodies should work as expected:
131+
__attribute__((device, deprecated)) constexpr int DeviceVarConstDepr = 1; // expected-note 0+ {{has been explicitly marked deprecated here}}
132+
133+
__attribute__((host)) void HostUser(void) {
134+
use(DeviceVarConstDepr); // expected-warning {{'DeviceVarConstDepr' is deprecated}}
135+
use(HostOnlyFunDeprecated()); // expected-warning {{'HostOnlyFunDeprecated' is deprecated}}
136+
use(OverloadFunHostDepr()); // expected-warning {{'OverloadFunHostDepr' is deprecated: Host variant}}
137+
use(TemplateOverloadFun<int>()); // expected-warning {{'TemplateOverloadFun<int>' is deprecated: Host variant}}
138+
139+
use(OverloadFunDeviceDepr());
140+
}
141+
142+
__attribute__((device)) void DeviceUser(void) {
143+
use(DeviceVarConstDepr); // expected-warning {{'DeviceVarConstDepr' is deprecated}}
144+
use(DeviceOnlyFunDeprecated()); // expected-warning {{'DeviceOnlyFunDeprecated' is deprecated}}
145+
use(OverloadFunDeviceDepr()); // expected-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}}
146+
147+
use(OverloadFunHostDepr());
148+
use(TemplateOverloadFun<int>());
149+
}

0 commit comments

Comments
 (0)