diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index 22f5a2f663477..984789489098a 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -20,6 +20,7 @@ #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" +#include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaObjC.h" #include "llvm/ADT/StringRef.h" #include @@ -156,6 +157,58 @@ static bool ShouldDiagnoseAvailabilityInContext( } } + if (S.getLangOpts().CUDA || S.getLangOpts().HIP) { + // In CUDA/HIP, do not diagnose uses of unavailable host or device function + // overloads when they occur in the context of a Decl with an explicitly + // given opposite target. + // We encounter this if the OffendingDecl is used outside of a function + // body, e.g., in template arguments for a function's return or parameter + // types. In this case, overloads of the called function are resolved as if + // in a host-device context, i.e., the device overload is chosen in the + // device compilation phase and the host overload in the host compilation + // phase. As code is only generated for the variant with matching targets, + // an availabiliy diagnostic for the variant with non-matching targets would + // be spurious. + + if (auto *OffendingFunDecl = llvm::dyn_cast(OffendingDecl)) { + Decl *ActualCtx = Ctx; + if (auto *FTD = llvm::dyn_cast(Ctx)) { + // Attributes of template Decls are only on the templated Decl + ActualCtx = FTD->getTemplatedDecl(); + } + if (auto *CtxFun = llvm::dyn_cast(ActualCtx)) { + auto TargetIs = [&S](const FunctionDecl *FD, CUDAFunctionTarget FT) { + return S.CUDA().IdentifyTarget(FD, /* IgnoreImplicitHDAttr */ true) == + FT; + }; + + bool CtxIsHost = TargetIs(CtxFun, CUDAFunctionTarget::Host); + bool CtxIsDevice = TargetIs(CtxFun, CUDAFunctionTarget::Device); + + bool OffendingDeclIsHost = + TargetIs(OffendingFunDecl, CUDAFunctionTarget::Host); + bool OffendingDeclIsDevice = + TargetIs(OffendingFunDecl, CUDAFunctionTarget::Device); + + // There is a way to call a device function from host code (and vice + // versa, analogously) that passes semantic analysis: As constexprs, + // when there is no host overload. In this case, a diagnostic is + // necessary. Characteristic for this situation is that the device + // function will also be used in a host context during host compilation. + // Therefore, only suppress diagnostics if a host function is used in a + // device context during host compilation or a device function is used + // in a host context during device compilation. + bool CompilingForDevice = S.getLangOpts().CUDAIsDevice; + bool CompilingForHost = !CompilingForDevice; + + if ((OffendingDeclIsHost && CtxIsDevice && CompilingForHost) || + (OffendingDeclIsDevice && CtxIsHost && CompilingForDevice)) { + return false; + } + } + } + } + // Checks if we should emit the availability diagnostic in the context of C. auto CheckContext = [&](const Decl *C) { if (K == AR_NotYetIntroduced) { diff --git a/clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu b/clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu new file mode 100644 index 0000000000000..c3023d16565cf --- /dev/null +++ b/clang/test/SemaCUDA/suppress-availability-warnings-mismatched-attributes.cu @@ -0,0 +1,149 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify=expected,onhost %s +// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -fsyntax-only -fcuda-is-device -verify=expected,ondevice %s + +template struct my_enable_if {}; + +template struct my_enable_if { + typedef T type; +}; + +__attribute__((host, device)) void use(int x); + +__attribute__((device)) constexpr int OverloadFunHostDepr(void) { return 1; } +__attribute__((host, deprecated("Host variant"))) constexpr int OverloadFunHostDepr(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}} + + +__attribute__((device, deprecated("Device variant"))) constexpr int OverloadFunDeviceDepr(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}} +__attribute__((host)) constexpr int OverloadFunDeviceDepr(void) { return 1; } + + +template +__attribute__((device)) constexpr T TemplateOverloadFun(void) { return 1; } + +template +__attribute__((host, deprecated("Host variant"))) constexpr T TemplateOverloadFun(void) { return 1; } // expected-note 0+ {{has been explicitly marked deprecated here}} + + +__attribute__((device, deprecated)) constexpr int // expected-note 0+ {{has been explicitly marked deprecated here}} +DeviceOnlyFunDeprecated(void) { return 1; } + +__attribute__((host, deprecated)) constexpr int // expected-note 0+ {{has been explicitly marked deprecated here}} +HostOnlyFunDeprecated(void) { return 1; } + +class FunSelector { +public: + template __attribute__((device)) + auto devicefun(void) -> typename my_enable_if<(X == OverloadFunHostDepr()), int>::type { + return 1; + } + + template __attribute__((device)) + auto devicefun(void) -> typename my_enable_if<(X != OverloadFunHostDepr()), int>::type { + return 0; + } + + template __attribute__((device)) + auto devicefun_wrong(void) -> typename my_enable_if<(X == OverloadFunDeviceDepr()), int>::type { // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}} + return 1; + } + + template __attribute__((device)) + auto devicefun_wrong(void) -> typename my_enable_if<(X != OverloadFunDeviceDepr()), int>::type { // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}} + return 0; + } + + template __attribute__((host)) + auto hostfun(void) -> typename my_enable_if<(X == OverloadFunDeviceDepr()), int>::type { + return 1; + } + + template __attribute__((host)) + auto hostfun(void) -> typename my_enable_if<(X != OverloadFunDeviceDepr()), int>::type { + return 0; + } + + template __attribute__((host)) + auto hostfun_wrong(void) -> typename my_enable_if<(X == OverloadFunHostDepr()), int>::type { // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}} + return 1; + } + + template __attribute__((host)) + auto hostfun_wrong(void) -> typename my_enable_if<(X != OverloadFunHostDepr()), int>::type { // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}} + return 0; + } +}; + + +// These should not be diagnosed since the device overload of +// OverloadFunHostDepr is not deprecated: +__attribute__((device)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type +DeviceUserOverloadFunHostDepr1(void) { return 2; } + +my_enable_if<(OverloadFunHostDepr() > 0), int>::type __attribute__((device)) +DeviceUserOverloadFunHostDepr2(void) { return 2; } + +__attribute__((device)) +my_enable_if<(OverloadFunHostDepr() > 0), int>::type constexpr +DeviceUserOverloadFunHostDeprConstexpr(void) { return 2; } + + +// Analogously for OverloadFunDeviceDepr: +__attribute__((host)) my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type +DeviceUserOverloadFunDeviceDepr1(void) { return 2; } + +my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type __attribute__((host)) +DeviceUserOverloadFunDeviceDepr2(void) { return 2; } + +__attribute__((host)) +my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type constexpr +DeviceUserOverloadFunDeviceDeprConstexpr(void) { return 2; } + + +// Actual uses of the deprecated overloads should be diagnosed: +__attribute__((host, device)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}} +HostDeviceUserOverloadFunHostDepr(void) { return 3; } + +__attribute__((host)) my_enable_if<(OverloadFunHostDepr() > 0), int>::type constexpr // onhost-warning {{'OverloadFunHostDepr' is deprecated: Host variant}} +HostUserOverloadFunHostDeprConstexpr(void) { return 3; } + +__attribute__((device)) my_enable_if<(OverloadFunDeviceDepr() > 0), int>::type constexpr // ondevice-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}} +HostUserOverloadFunDeviceDeprConstexpr(void) { return 3; } + + +// Making the offending decl a template shouldn't change anything: +__attribute__((host)) my_enable_if<(TemplateOverloadFun() > 0), int>::type // onhost-warning {{'TemplateOverloadFun' is deprecated: Host variant}} +HostUserTemplateOverloadFun(void) { return 3; } + +__attribute__((device)) my_enable_if<(TemplateOverloadFun() > 0), int>::type +DeviceUserTemplateOverloadFun(void) { return 3; } + + +// If the constexpr function is actually called from the mismatched context, diagnostics should be issued: +__attribute__((host)) +my_enable_if<(DeviceOnlyFunDeprecated() > 0), int>::type constexpr // onhost-warning {{'DeviceOnlyFunDeprecated' is deprecated}} +HostUserDeviceOnlyFunDeprecated(void) { return 3; } + +__attribute__((device)) +my_enable_if<(HostOnlyFunDeprecated() > 0), int>::type constexpr // ondevice-warning {{'HostOnlyFunDeprecated' is deprecated}} +DeviceUserHostOnlyFunDeprecated(void) { return 3; } + +// Diagnostics for uses in function bodies should work as expected: +__attribute__((device, deprecated)) constexpr int DeviceVarConstDepr = 1; // expected-note 0+ {{has been explicitly marked deprecated here}} + +__attribute__((host)) void HostUser(void) { + use(DeviceVarConstDepr); // expected-warning {{'DeviceVarConstDepr' is deprecated}} + use(HostOnlyFunDeprecated()); // expected-warning {{'HostOnlyFunDeprecated' is deprecated}} + use(OverloadFunHostDepr()); // expected-warning {{'OverloadFunHostDepr' is deprecated: Host variant}} + use(TemplateOverloadFun()); // expected-warning {{'TemplateOverloadFun' is deprecated: Host variant}} + + use(OverloadFunDeviceDepr()); +} + +__attribute__((device)) void DeviceUser(void) { + use(DeviceVarConstDepr); // expected-warning {{'DeviceVarConstDepr' is deprecated}} + use(DeviceOnlyFunDeprecated()); // expected-warning {{'DeviceOnlyFunDeprecated' is deprecated}} + use(OverloadFunDeviceDepr()); // expected-warning {{'OverloadFunDeviceDepr' is deprecated: Device variant}} + + use(OverloadFunHostDepr()); + use(TemplateOverloadFun()); +}