diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -123,7 +123,7 @@ return CFT_Device; } else if (hasAttr(D, IgnoreImplicitHDAttr)) { return CFT_Host; - } else if (D->isImplicit() && !IgnoreImplicitHDAttr) { + } else if ((D->isImplicit() || D->isDefaulted()) && !IgnoreImplicitHDAttr) { // Some implicit declarations (like intrinsic functions) are not marked. // Set the most lenient target on them for maximal flexibility. return CFT_HostDevice; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -15159,7 +15159,15 @@ // If this is an array, we'll require the destructor during initialization, so // we can skip over this. We still want to emit exit-time destructor warnings // though. - if (!VD->getType()->isArrayType()) { + bool SkipDtorChecks = VD->getType()->isArrayType(); + + // CUDA: Skip destructor checks for host-only variables during device-side + // compilation + SkipDtorChecks |= + (LangOpts.CUDAIsDevice && VD->hasGlobalStorage() && + !(VD->hasAttr() || VD->hasAttr() || + VD->hasAttr())); + if (!SkipDtorChecks) { MarkFunctionReferenced(VD->getLocation(), Destructor); CheckDestructorAccess(VD->getLocation(), Destructor, PDiag(diag::err_access_dtor_var) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1527,9 +1527,24 @@ bool Sema::isUsualDeallocationFunction(const CXXMethodDecl *Method) { // [CUDA] Ignore this function, if we can't call it. const FunctionDecl *Caller = dyn_cast(CurContext); - if (getLangOpts().CUDA && - IdentifyCUDAPreference(Caller, Method) <= CFP_WrongSide) - return false; + if (getLangOpts().CUDA) { + auto CallPreference = IdentifyCUDAPreference(Caller, Method); + // If it's not callable at all, it's not the right function. + if (CallPreference < CFP_WrongSide) + return false; + if (CallPreference == CFP_WrongSide) { + // Maybe. We have to check if there are better alternatives. + DeclContext::lookup_result R = + Method->getDeclContext()->lookup(Method->getDeclName()); + for (const auto *D : R) { + if (const auto *FD = dyn_cast(D)) { + if (IdentifyCUDAPreference(Caller, FD) > CFP_WrongSide) + return false; + } + } + // We've found no better variants. + } + } SmallVector PreventedBy; bool Result = Method->isUsualDeallocationFunction(PreventedBy); diff --git a/clang/test/SemaCUDA/default-ctor.cu b/clang/test/SemaCUDA/default-ctor.cu --- a/clang/test/SemaCUDA/default-ctor.cu +++ b/clang/test/SemaCUDA/default-ctor.cu @@ -25,7 +25,7 @@ InD ind; InH inh; // expected-error{{no matching constructor for initialization of 'InH'}} InHD inhd; - Out out; // expected-error{{no matching constructor for initialization of 'Out'}} + Out out; OutD outd; OutH outh; // expected-error{{no matching constructor for initialization of 'OutH'}} OutHD outhd; diff --git a/clang/test/SemaCUDA/usual-deallocators.cu b/clang/test/SemaCUDA/usual-deallocators.cu --- a/clang/test/SemaCUDA/usual-deallocators.cu +++ b/clang/test/SemaCUDA/usual-deallocators.cu @@ -93,3 +93,12 @@ test_hd(t); test_hd(t); } + +// This should produce no errors. Defaulted destructor should be treated as HD, +// which allows referencing host-only `operator delete` with a deferred +// diagnostics that would fire if we ever attempt to codegen it on device.. +struct H { + virtual ~H() = default; + static void operator delete(void *) {} +}; +H h;