Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_AST_DECLCXX_H #define LLVM_CLANG_AST_DECLCXX_H +#include "clang/AST/ASTContext.h" #include "clang/AST/ASTUnresolvedSet.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" @@ -978,7 +979,11 @@ /// Determine whether this class needs an implicit destructor to /// be lazily declared. - bool needsImplicitDestructor() const { + bool needsImplicitDestructor(LangAS AS = LangAS::Default) const { + if (getASTContext().getLangOpts().OpenCL) { + return getDestructor(AS) == nullptr; + } + return !(data().DeclaredSpecialMembers & SMF_Destructor); } @@ -1476,7 +1481,7 @@ } /// Returns the destructor decl for this class. - CXXDestructorDecl *getDestructor() const; + CXXDestructorDecl *getDestructor(LangAS AS = LangAS::Default) const; /// Returns true if the class destructor, or any implicitly invoked /// destructors are marked noreturn. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -4042,13 +4042,10 @@ LOLR_StringTemplatePack, }; - SpecialMemberOverloadResult LookupSpecialMember(CXXRecordDecl *D, - CXXSpecialMember SM, - bool ConstArg, - bool VolatileArg, - bool RValueThis, - bool ConstThis, - bool VolatileThis); + SpecialMemberOverloadResult + LookupSpecialMember(CXXRecordDecl *D, CXXSpecialMember SM, bool ConstArg, + bool VolatileArg, bool RValueThis, bool ConstThis, + bool VolatileThis, LangAS ASThis = LangAS::Default); typedef std::function TypoDiagnosticGenerator; typedef std::function @@ -4144,7 +4141,8 @@ unsigned Quals); CXXMethodDecl *LookupMovingAssignment(CXXRecordDecl *Class, unsigned Quals, bool RValueThis, unsigned ThisQuals); - CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); + CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class, + LangAS AS = LangAS::Default); bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id, bool IsUDSuffix); Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -1884,7 +1884,7 @@ return nullptr; } -CXXDestructorDecl *CXXRecordDecl::getDestructor() const { +CXXDestructorDecl *CXXRecordDecl::getDestructor(LangAS AS) const { ASTContext &Context = getASTContext(); QualType ClassType = Context.getTypeDeclType(this); @@ -1894,7 +1894,24 @@ DeclContext::lookup_result R = lookup(Name); - return R.empty() ? nullptr : dyn_cast(R.front()); + if (AS == LangAS::Default) + return R.empty() ? nullptr : dyn_cast(R.front()); + + CXXDestructorDecl *Best = nullptr; + + for (DeclContext::lookup_result::iterator I = R.begin(), E = R.end(); I != E; + ++I) { + auto *Dtor = dyn_cast(*I); + LangAS DtorAS = Dtor->getMethodQualifiers().getAddressSpace(); + if (Qualifiers::isAddressSpaceSupersetOf(DtorAS, AS) && + (Best == nullptr || + Qualifiers::isAddressSpaceSupersetOf( + Best->getMethodQualifiers().getAddressSpace(), DtorAS))) { + Best = Dtor; + } + } + + return Best; } static bool isDeclContextInNamespace(const DeclContext *DC) { Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -13529,7 +13529,7 @@ // If a class has no user-declared destructor, a destructor is // declared implicitly. An implicitly-declared destructor is an // inline public member of its class. - assert(ClassDecl->needsImplicitDestructor()); + assert(ClassDecl->needsImplicitDestructor(getDefaultCXXMethodAddrSpace())); DeclaringSpecialMember DSM(*this, ClassDecl, CXXDestructor); if (DSM.isAlreadyBeingDeclared()) @@ -15451,7 +15451,11 @@ if (VD->isNoDestroy(getASTContext())) return; - CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl); + LangAS AS = LangAS::Default; + if (Context.getLangOpts().OpenCL) { + AS = VD->getType().getAddressSpace(); + } + CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl, AS); // 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 Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -3054,13 +3054,10 @@ Functions.append(Operators.begin(), Operators.end()); } -Sema::SpecialMemberOverloadResult Sema::LookupSpecialMember(CXXRecordDecl *RD, - CXXSpecialMember SM, - bool ConstArg, - bool VolatileArg, - bool RValueThis, - bool ConstThis, - bool VolatileThis) { +Sema::SpecialMemberOverloadResult +Sema::LookupSpecialMember(CXXRecordDecl *RD, CXXSpecialMember SM, bool ConstArg, + bool VolatileArg, bool RValueThis, bool ConstThis, + bool VolatileThis, LangAS ASThis) { assert(CanDeclareSpecialMemberFunction(RD) && "doing special member lookup into record that isn't fully complete"); RD = RD->getDefinition(); @@ -3082,6 +3079,7 @@ ID.AddInteger(RValueThis); ID.AddInteger(ConstThis); ID.AddInteger(VolatileThis); + ID.AddInteger((unsigned)ASThis); void *InsertPoint; SpecialMemberOverloadResultEntry *Result = @@ -3096,12 +3094,12 @@ SpecialMemberCache.InsertNode(Result, InsertPoint); if (SM == CXXDestructor) { - if (RD->needsImplicitDestructor()) { + if (RD->needsImplicitDestructor(ASThis)) { runWithSufficientStackSpace(RD->getLocation(), [&] { DeclareImplicitDestructor(RD); }); } - CXXDestructorDecl *DD = RD->getDestructor(); + CXXDestructorDecl *DD = RD->getDestructor(ASThis); Result->setMethod(DD); Result->setKind(DD && !DD->isDeleted() ? SpecialMemberOverloadResult::Success @@ -3362,10 +3360,11 @@ /// CXXRecordDecl::getDestructor(). /// /// \returns The destructor for this class. -CXXDestructorDecl *Sema::LookupDestructor(CXXRecordDecl *Class) { +CXXDestructorDecl *Sema::LookupDestructor(CXXRecordDecl *Class, LangAS AS) { return cast(LookupSpecialMember(Class, CXXDestructor, - false, false, false, - false, false).getMethod()); + false, false, false, false, + false, AS) + .getMethod()); } /// LookupLiteralOperator - Determine which literal operator should be used for Index: clang/test/SemaOpenCLCXX/addrspace-destructors.clcpp =================================================================== --- /dev/null +++ clang/test/SemaOpenCLCXX/addrspace-destructors.clcpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 %s -pedantic -ast-dump | FileCheck %s + +// CHECK: CXXDestructorDecl {{.*}} used ~ExactDtor 'void () __private noexcept' +struct ExactDtor { + ~ExactDtor() __private; +}; + +// CHECK: CXXDestructorDecl +// CHECK-NOT: used +// CHECK-SAME: ~OverloadedDtor 'void () __generic' +// CHECK: CXXDestructorDecl {{.*}} used ~OverloadedDtor 'void () __private noexcept' +struct OverloadedDtor { + ~OverloadedDtor() __generic; + ~OverloadedDtor() __private; +}; + +// CHECK: CXXDestructorDecl +// CHECK-NOT: used +// CHECK-SAME: ~ImplicitDtor 'void () __global' +// CHECK: CXXDestructorDecl {{.*}} implicit used ~ImplicitDtor 'void () __generic noexcept' +struct ImplicitDtor { + ~ImplicitDtor() __global; +}; + +// CHECK: CXXDestructorDecl {{.*}} used ~Templated 'void () __generic noexcept' +// CHECK: CXXDestructorDecl +// CHECK-NOT: used +// CHECK-SAME: ~Templated 'void () __global' +template +struct Templated { + ~Templated() __generic; + ~Templated() __global; +}; + +// CHECK: CXXDestructorDecl {{.*}} used ~BothUsed 'void () __private noexcept' +// CHECK: CXXDestructorDecl {{.*}} used ~BothUsed 'void () __global noexcept' +struct BothUsed { + ~BothUsed() __private; + ~BothUsed() __global; +}; + +__global BothUsed g_inheriting; + +kernel void k() { + __private ExactDtor exact; + __private OverloadedDtor overloaded; + __private ImplicitDtor implicit; + __private Templated templated; + __private BothUsed inheriting; +}