diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5559,6 +5559,11 @@ void DefineImplicitDestructor(SourceLocation CurrentLocation, CXXDestructorDecl *Destructor); + /// Define an implicit complete constructor for classes with vbases in the MS + /// ABI. + void DefineImplicitCompleteDestructor(SourceLocation CurrentLocation, + CXXDestructorDecl *Dtor); + /// Build an exception spec for destructors that don't have one. /// /// C++11 says that user-defined destructors with no exception spec get one 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 @@ -13202,6 +13202,53 @@ } } +void Sema::DefineImplicitCompleteDestructor(SourceLocation CurrentLocation, + CXXDestructorDecl *Destructor) { + if (Destructor->isInvalidDecl()) + return; + + CXXRecordDecl *ClassDecl = Destructor->getParent(); + assert(Context.getTargetInfo().getCXXABI().isMicrosoft() && + "implicit complete dtors unneeded outside MS ABI"); + assert(ClassDecl->getNumVBases() > 0 && + "complete dtor only exists for classes with vbases"); + + SynthesizedFunctionScope Scope(*this, Destructor); + + // Add a context note for diagnostics produced after this point. + Scope.addContextNote(CurrentLocation); + + // No need to resolve the exception specification of the base destructor or + // mark vtables referenced. That will be done when the base dtor is defined. + + // Similar to what is done above in MarkBaseAndMemberDestructorsReferenced, + // but only for virtual bases. + for (const CXXBaseSpecifier &VBase : ClassDecl->vbases()) { + // Bases are always records in a well-formed non-dependent class. + CXXRecordDecl *BaseRD = VBase.getType()->getAsCXXRecordDecl(); + + // If our base class is invalid, we probably can't get its dtor anyway. + if (!BaseRD || BaseRD->isInvalidDecl() || BaseRD->hasIrrelevantDestructor()) + continue; + + CXXDestructorDecl *Dtor = LookupDestructor(BaseRD); + assert(Dtor && "No dtor found for BaseRD!"); + if (CheckDestructorAccess( + ClassDecl->getLocation(), Dtor, + PDiag(diag::err_access_dtor_vbase) + << Context.getTypeDeclType(ClassDecl) << VBase.getType(), + Context.getTypeDeclType(ClassDecl)) == AR_accessible) { + CheckDerivedToBaseConversion( + Context.getTypeDeclType(ClassDecl), VBase.getType(), + diag::err_access_dtor_vbase, 0, ClassDecl->getLocation(), + SourceRange(), DeclarationName(), nullptr); + } + + MarkFunctionReferenced(Dtor->getLocation(), Dtor); + DiagnoseUseOfDecl(Dtor, Dtor->getLocation()); + } +} + /// Perform any semantic analysis which needs to be delayed until all /// pending class member declarations have been parsed. void Sema::ActOnFinishCXXMemberDecls() { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15998,10 +15998,19 @@ } else if (CXXDestructorDecl *Destructor = dyn_cast(Func)) { Destructor = cast(Destructor->getFirstDecl()); - if (Destructor->isDefaulted() && !Destructor->isDeleted()) { - if (Destructor->isTrivial() && !Destructor->hasAttr()) - return; - DefineImplicitDestructor(Loc, Destructor); + if (!Destructor->isDeleted()) { + if (Destructor->isDefaulted()) { + if (Destructor->isTrivial() && + !Destructor->hasAttr()) + return; + DefineImplicitDestructor(Loc, Destructor); + } else if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { + // In the MS ABI, the complete destructor is implicitly defined, + // even if the base destructor is user defined. + // FIXME: Need a way to do it only once. "setBody" seems wrong. + if (Destructor->getParent()->getNumVBases() > 0) + DefineImplicitCompleteDestructor(Loc, Destructor); + } } if (Destructor->isVirtual() && getLangOpts().AppleKext) MarkVTableUsed(Loc, Destructor->getParent());