From cb45a0f42964ab5fa1474b25abcc1ae3a8bd0ab8 Mon Sep 17 00:00:00 2001 From: Sean Hunt Date: Thu, 12 May 2011 22:46:25 +0000 Subject: [PATCH] Hrm git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131259 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 4 +- include/clang/Sema/Sema.h | 17 +- lib/Sema/SemaAccess.cpp | 8 +- lib/Sema/SemaDeclCXX.cpp | 227 ++++++++++++++++++--- lib/Sema/SemaExpr.cpp | 2 +- lib/Sema/SemaExprCXX.cpp | 39 ++-- 6 files changed, 243 insertions(+), 54 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 8d5951f3f3..8ef108d13b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3629,7 +3629,9 @@ def warn_explicit_conversion_functions : Warning< def err_defaulted_default_ctor_params : Error< "an explicitly-defaulted default constructor must have no parameters">; def err_incorrect_defaulted_exception_spec : Error< - "exception specification of explicitly defaulted function is incorrect">; + "exception specification of explicitly defaulted %select{default constructor|" + "copy constructor|copy assignment operator|destructor}0 does not match the " + "calculated one">; def err_out_of_line_default_deletes : Error< "defaulting this %select{default constructor|copy constructor|copy " "assignment operator|destructor}0 would delete it after its first " diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 555060eac1..137121a05c 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2582,9 +2582,17 @@ public: ImplicitExceptionSpecification ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl); + /// \brief Determine what sort of exception specification a defaulted + /// destructor of a class will have. + ImplicitExceptionSpecification + ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl); + /// \brief Determine if a defaulted default constructor ought to be /// deleted. - bool ShouldDeleteDefaultConstructor(CXXConstructorDecl *RD); + bool ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD); + + /// \brief Determine if a defaulted destructor ought to be deleted. + bool ShouldDeleteDestructor(CXXDestructorDecl *DD); /// \brief Declare the implicit default constructor for the given class. /// @@ -2794,7 +2802,8 @@ public: bool addMallocAttr = false); bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, - DeclarationName Name, FunctionDecl* &Operator); + DeclarationName Name, FunctionDecl* &Operator, + bool AllowMissing = false); /// ActOnCXXDelete - Parsed a C++ 'delete' expression ExprResult ActOnCXXDelete(SourceLocation StartLoc, @@ -3259,6 +3268,7 @@ public: void CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record); void CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *Ctor); + void CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *Dtor); //===--------------------------------------------------------------------===// // C++ Derived Classes @@ -3347,7 +3357,8 @@ public: AccessResult CheckAllocationAccess(SourceLocation OperatorLoc, SourceRange PlacementRange, CXXRecordDecl *NamingClass, - DeclAccessPair FoundDecl); + DeclAccessPair FoundDecl, + bool Diagnose = true); AccessResult CheckConstructorAccess(SourceLocation Loc, CXXConstructorDecl *D, const InitializedEntity &Entity, diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index f340bb3fda..80995eb66f 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -1463,7 +1463,8 @@ Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc, Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, SourceRange PlacementRange, CXXRecordDecl *NamingClass, - DeclAccessPair Found) { + DeclAccessPair Found, + bool Diagnose) { if (!getLangOptions().AccessControl || !NamingClass || Found.getAccess() == AS_public) @@ -1471,8 +1472,9 @@ Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, QualType()); - Entity.setDiag(diag::err_access) - << PlacementRange; + if (Diagnose) + Entity.setDiag(diag::err_access) + << PlacementRange; return CheckAccess(*this, OpLoc, Entity); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index b98777bad7..d80bfb9b7e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2998,19 +2998,31 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { } void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) { - for (CXXRecordDecl::ctor_iterator CI = Record->ctor_begin(), - CE = Record->ctor_end(); - CI != CE; ++CI) { - if (!CI->isInvalidDecl() && CI->isExplicitlyDefaulted()) { - if (CI->isDefaultConstructor()) { - CheckExplicitlyDefaultedDefaultConstructor(*CI); - } + for (CXXRecordDecl::method_iterator MI = Record->method_begin(), + ME = Record->method_end(); + MI != ME; ++MI) { + if (!MI->isInvalidDecl() && MI->isExplicitlyDefaulted()) { + switch (getSpecialMember(*MI)) { + case CXXDefaultConstructor: + CheckExplicitlyDefaultedDefaultConstructor( + cast(*MI)); + break; + + case CXXDestructor: + CheckExplicitlyDefaultedDestructor(cast(*MI)); + break; - // FIXME: Do copy and move constructors + case CXXCopyConstructor: + case CXXCopyAssignment: + // FIXME: Do copy and move constructors and assignment operators + break; + + default: + llvm_unreachable("non-special member explicitly defaulted!"); + } } } - // FIXME: Do copy and move assignment and destructors } void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) { @@ -3038,7 +3050,8 @@ void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) { if (CtorType->hasExceptionSpec()) { if (CheckEquivalentExceptionSpec( - PDiag(diag::err_incorrect_defaulted_exception_spec), + PDiag(diag::err_incorrect_defaulted_exception_spec) + << 0 /* default constructor */, PDiag(), ExceptionType, SourceLocation(), CtorType, CD->getLocation())) { @@ -3056,8 +3069,48 @@ void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) { CD->setDeletedAsWritten(); else Diag(CD->getLocation(), diag::err_out_of_line_default_deletes) - << getSpecialMember(CD); + << 0 /* default constructor */; + } +} + +void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) { + assert(DD->isExplicitlyDefaulted()); + + // Whether this was the first-declared instance of the destructor. + bool First = DD == DD->getCanonicalDecl(); + + ImplicitExceptionSpecification Spec + = ComputeDefaultedDtorExceptionSpec(DD->getParent()); + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + const FunctionProtoType *DtorType = DD->getType()->getAs(), + *ExceptionType = Context.getFunctionType( + Context.VoidTy, 0, 0, EPI)->getAs(); + + if (DtorType->hasExceptionSpec()) { + if (CheckEquivalentExceptionSpec( + PDiag(diag::err_incorrect_defaulted_exception_spec) + << 3 /* destructor */, + PDiag(), + ExceptionType, SourceLocation(), + DtorType, DD->getLocation())) { + DD->setInvalidDecl(); + return; + } + } else if (First) { + // We set the declaration to have the computed exception spec here. + // There are no parameters. + DD->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); } + + if (ShouldDeleteDestructor(DD)) { + if (First) + DD->setDeletedAsWritten(); + else + Diag(DD->getLocation(), diag::err_out_of_line_default_deletes) + << 3 /* destructor */; + } + + CheckDestructor(DD); } bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { @@ -3072,8 +3125,6 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { bool Union = RD->isUnion(); bool AllConst = true; - DiagnosticErrorTrap Trap(Diags); - // We do this because we should never actually use an anonymous // union's constructor. if (Union && RD->isAnonymousStructOrUnion()) @@ -3087,6 +3138,10 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), BE = RD->bases_end(); BI != BE; ++BI) { + // We'll handle this one later + if (BI->isVirtual()) + continue; + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); assert(BaseDecl && "base isn't a CXXRecordDecl"); @@ -3099,10 +3154,6 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { AR_accessible) return true; - // We'll handle this one later - if (BI->isVirtual()) - continue; - // -- any [direct base class either] has no default constructor or // overload resolution as applied to [its] default constructor // results in an ambiguity or in a function that is deleted or @@ -3234,6 +3285,109 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { return false; } +bool Sema::ShouldDeleteDestructor(CXXDestructorDecl *DD) { + CXXRecordDecl *RD = DD->getParent(); + assert(!RD->isDependentType() && "do deletion after instantiation"); + if (!LangOpts.CPlusPlus0x) + return false; + + // Do access control from the destructor + ContextRAII CtorContext(*this, DD); + + bool Union = RD->isUnion(); + + // C++0x [class.dtor]p5 + // A defaulted destructor for a class X is defined as deleted if: + for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); + BI != BE; ++BI) { + // We'll handle this one later + if (BI->isVirtual()) + continue; + + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); + assert(BaseDtor && "base has no destructor"); + + // -- any direct or virtual base class has a deleted destructor or + // a destructor that is inaccessible from the defaulted destructor + if (BaseDtor->isDeleted()) + return true; + if (CheckDestructorAccess(SourceLocation(), BaseDtor, PDiag()) != + AR_accessible) + return true; + } + + for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(), + BE = RD->vbases_end(); + BI != BE; ++BI) { + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); + assert(BaseDtor && "base has no destructor"); + + // -- any direct or virtual base class has a deleted destructor or + // a destructor that is inaccessible from the defaulted destructor + if (BaseDtor->isDeleted()) + return true; + if (CheckDestructorAccess(SourceLocation(), BaseDtor, PDiag()) != + AR_accessible) + return true; + } + + for (CXXRecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); + FI != FE; ++FI) { + QualType FieldType = Context.getBaseElementType(FI->getType()); + CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); + if (FieldRecord) { + if (FieldRecord->isUnion() && FieldRecord->isAnonymousStructOrUnion()) { + for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(), + UE = FieldRecord->field_end(); + UI != UE; ++UI) { + QualType UnionFieldType = Context.getBaseElementType(FI->getType()); + CXXRecordDecl *UnionFieldRecord = + UnionFieldType->getAsCXXRecordDecl(); + + // -- X is a union-like class that has a variant member with a non- + // trivial destructor. + if (UnionFieldRecord && !UnionFieldRecord->hasTrivialDestructor()) + return true; + } + // Technically we are supposed to do this next check unconditionally. + // But that makes absolutely no sense. + } else { + CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord); + + // -- any of the non-static data members has class type M (or array + // thereof) and M has a deleted destructor or a destructor that is + // inaccessible from the defaulted destructor + if (FieldDtor->isDeleted()) + return true; + if (CheckDestructorAccess(SourceLocation(), FieldDtor, PDiag()) != + AR_accessible) + return true; + + // -- X is a union-like class that has a variant member with a non- + // trivial destructor. + if (Union && !FieldDtor->isTrivial()) + return true; + } + } + } + + if (DD->isVirtual()) { + FunctionDecl *OperatorDelete = 0; + DeclarationName Name = + Context.DeclarationNames.getCXXOperatorName(OO_Delete); + if (FindDeallocationFunction(SourceLocation(), RD, Name, OperatorDelete, + false)) + return true; + } + + + return false; +} + /// \brief Data used with FindHiddenVirtualMethod namespace { struct FindHiddenVirtualMethodData { @@ -5480,12 +5634,8 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { } } -CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { - // C++ [class.dtor]p2: - // 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. - +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have // an exception-specification. @@ -5522,11 +5672,20 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { LookupDestructor(cast(RecordTy->getDecl()))); } + return ExceptSpec; +} + +CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { + // C++ [class.dtor]p2: + // 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. + + ImplicitExceptionSpecification Spec = + ComputeDefaultedDtorExceptionSpec(ClassDecl); + FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); + // Create the actual destructor declaration. - FunctionProtoType::ExtProtoInfo EPI; - EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType(); - EPI.NumExceptions = ExceptSpec.size(); - EPI.Exceptions = ExceptSpec.data(); QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI); CanQualType ClassType @@ -5540,6 +5699,7 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { /*isInline=*/true, /*isImplicitlyDeclared=*/true); Destructor->setAccess(AS_public); + Destructor->setDefaulted(); Destructor->setImplicit(); Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); @@ -5553,6 +5713,9 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { // This could be uniqued if it ever proves significant. Destructor->setTypeSourceInfo(Context.getTrivialTypeSourceInfo(Ty)); + + if (ShouldDeleteDestructor(Destructor)) + Destructor->setDeletedAsWritten(); AddOverriddenMethods(ClassDecl, Destructor); @@ -5561,7 +5724,7 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, CXXDestructorDecl *Destructor) { - assert((Destructor->isImplicit() && !Destructor->isUsed(false)) && + assert((Destructor->isDefaulted() && !Destructor->isUsed(false)) && "DefineImplicitDestructor - call it for implicit default dtor"); CXXRecordDecl *ClassDecl = Destructor->getParent(); assert(ClassDecl && "DefineImplicitDestructor - invalid destructor"); @@ -7882,6 +8045,14 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { DefineImplicitDefaultConstructor(DefaultLoc, CD); break; } + + case CXXDestructor: { + CXXDestructorDecl *DD = cast(MD); + CheckExplicitlyDefaultedDestructor(DD); + DefineImplicitDestructor(DefaultLoc, DD); + break; + } + default: // FIXME: Do the rest once we have functions break; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index e2426952b2..5a171eca98 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9866,7 +9866,7 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { MarkVTableUsed(Loc, Constructor->getParent()); } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { - if (Destructor->isImplicit() && !Destructor->isUsed(false)) + if (Destructor->isDefaulted() && !Destructor->isUsed(false)) DefineImplicitDestructor(Loc, Destructor); if (Destructor->isVirtual()) MarkVTableUsed(Loc, Destructor->getParent()); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index b2a67aa935..867bd3515b 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1569,7 +1569,7 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, DeclarationName Name, - FunctionDecl* &Operator) { + FunctionDecl* &Operator, bool AllowMissing) { LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName); // Try to find operator delete/operator delete[] in class scope. LookupQualifiedName(Found, RD); @@ -1597,32 +1597,35 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, if (Matches.size() == 1) { Operator = cast(Matches[0]->getUnderlyingDecl()); CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(), - Matches[0]); + Matches[0], !AllowMissing); return false; // We found multiple suitable operators; complain about the ambiguity. } else if (!Matches.empty()) { - Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found) - << Name << RD; - - for (llvm::SmallVectorImpl::iterator - F = Matches.begin(), FEnd = Matches.end(); F != FEnd; ++F) - Diag((*F)->getUnderlyingDecl()->getLocation(), - diag::note_member_declared_here) << Name; + if (!AllowMissing) { + Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found) + << Name << RD; + + for (llvm::SmallVectorImpl::iterator + F = Matches.begin(), FEnd = Matches.end(); F != FEnd; ++F) + Diag((*F)->getUnderlyingDecl()->getLocation(), + diag::note_member_declared_here) << Name; + } return true; } // We did find operator delete/operator delete[] declarations, but // none of them were suitable. if (!Found.empty()) { - Diag(StartLoc, diag::err_no_suitable_delete_member_function_found) - << Name << RD; - - for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); - F != FEnd; ++F) - Diag((*F)->getUnderlyingDecl()->getLocation(), - diag::note_member_declared_here) << Name; - + if (!AllowMissing) { + Diag(StartLoc, diag::err_no_suitable_delete_member_function_found) + << Name << RD; + + for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); + F != FEnd; ++F) + Diag((*F)->getUnderlyingDecl()->getLocation(), + diag::note_member_declared_here) << Name; + } return true; } @@ -1634,7 +1637,7 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Expr* DeallocArgs[1]; DeallocArgs[0] = &Null; if (FindAllocationOverload(StartLoc, SourceRange(), Name, - DeallocArgs, 1, TUDecl, /*AllowMissing=*/false, + DeallocArgs, 1, TUDecl, AllowMissing, Operator)) return true; -- 2.40.0