diff options
Diffstat (limited to 'lib/Sema/SemaDeclCXX.cpp')
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 479 |
1 files changed, 251 insertions, 228 deletions
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1480c2ef98..9c4272de0c 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -128,8 +128,8 @@ namespace { void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, CXXMethodDecl *Method) { - // If we have an MSAny or unknown spec already, don't bother. - if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) + // If we have an MSAny spec already, don't bother. + if (!Method || ComputedEST == EST_MSAny) return; const FunctionProtoType *Proto @@ -141,7 +141,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, ExceptionSpecificationType EST = Proto->getExceptionSpecType(); // If this function can throw any exceptions, make a note of that. - if (EST == EST_Delayed || EST == EST_MSAny || EST == EST_None) { + if (EST == EST_MSAny || EST == EST_None) { ClearExceptions(); ComputedEST = EST; return; @@ -198,7 +198,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, } void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) { - if (!E || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) + if (!E || ComputedEST == EST_MSAny) return; // FIXME: @@ -3881,9 +3881,6 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { // instantiated (e.g. meta-functions). This doesn't apply to classes that // have inherited constructors. DeclareInheritedConstructors(Record); - - if (!Record->isDependentType()) - CheckExplicitlyDefaultedMethods(Record); } void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) { @@ -3989,6 +3986,45 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, return true; } +static Sema::ImplicitExceptionSpecification +computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, CXXMethodDecl *MD) { + switch (S.getSpecialMember(MD)) { + case Sema::CXXDefaultConstructor: + return S.ComputeDefaultedDefaultCtorExceptionSpec(Loc, MD); + case Sema::CXXCopyConstructor: + return S.ComputeDefaultedCopyCtorExceptionSpec(MD); + case Sema::CXXCopyAssignment: + return S.ComputeDefaultedCopyAssignmentExceptionSpec(MD); + case Sema::CXXMoveConstructor: + return S.ComputeDefaultedMoveCtorExceptionSpec(MD); + case Sema::CXXMoveAssignment: + return S.ComputeDefaultedMoveAssignmentExceptionSpec(MD); + case Sema::CXXDestructor: + return S.ComputeDefaultedDtorExceptionSpec(MD); + case Sema::CXXInvalid: + break; + } + llvm_unreachable("only special members have implicit exception specs"); +} + +void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) { + const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); + if (FPT->getExceptionSpecType() != EST_Unevaluated) + return; + + // Evaluate the exception specification and update the type of the special + // member to use it. + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + computeImplicitExceptionSpec(*this, Loc, MD).getEPI(EPI); + const FunctionProtoType *NewFPT = cast<FunctionProtoType>( + Context.getFunctionType(FPT->getResultType(), FPT->arg_type_begin(), + FPT->getNumArgs(), EPI)); + MD->setType(QualType(NewFPT, 0)); +} + +static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl); +static bool isImplicitCopyAssignmentArgConst(Sema &S, CXXRecordDecl *ClassDecl); + void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { CXXRecordDecl *RD = MD->getParent(); CXXSpecialMember CSM = getSpecialMember(MD); @@ -4023,40 +4059,28 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { const FunctionProtoType *Type = MD->getType()->getAs<FunctionProtoType>(); - // Compute implicit exception specification, argument constness, constexpr - // and triviality. - ImplicitExceptionSpecification Spec(*this); + // Compute argument constness, constexpr, and triviality. bool CanHaveConstParam = false; bool Trivial; switch (CSM) { case CXXDefaultConstructor: - Spec = ComputeDefaultedDefaultCtorExceptionSpec(RD); - if (Spec.isDelayed()) - // Exception specification depends on some deferred part of the class. - // We'll try again when the class's definition has been fully processed. - return; Trivial = RD->hasTrivialDefaultConstructor(); break; case CXXCopyConstructor: - llvm::tie(Spec, CanHaveConstParam) = - ComputeDefaultedCopyCtorExceptionSpecAndConst(RD); + CanHaveConstParam = isImplicitCopyCtorArgConst(*this, RD); Trivial = RD->hasTrivialCopyConstructor(); break; case CXXCopyAssignment: - llvm::tie(Spec, CanHaveConstParam) = - ComputeDefaultedCopyAssignmentExceptionSpecAndConst(RD); + CanHaveConstParam = isImplicitCopyAssignmentArgConst(*this, RD); Trivial = RD->hasTrivialCopyAssignment(); break; case CXXMoveConstructor: - Spec = ComputeDefaultedMoveCtorExceptionSpec(RD); Trivial = RD->hasTrivialMoveConstructor(); break; case CXXMoveAssignment: - Spec = ComputeDefaultedMoveAssignmentExceptionSpec(RD); Trivial = RD->hasTrivialMoveAssignment(); break; case CXXDestructor: - Spec = ComputeDefaultedDtorExceptionSpec(RD); Trivial = RD->hasTrivialDestructor(); break; case CXXInvalid: @@ -4126,11 +4150,15 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { HadError = true; } - // Rebuild the type with the implicit exception specification added. - FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo(); - Spec.getEPI(EPI); - const FunctionProtoType *ImplicitType = cast<FunctionProtoType>( - Context.getFunctionType(ReturnType, &ArgType, ExpectedParams, EPI)); + // Rebuild the type with the implicit exception specification added, if we + // are going to need it. + const FunctionProtoType *ImplicitType = 0; + if (First || Type->hasExceptionSpec()) { + FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo(); + computeImplicitExceptionSpec(*this, MD->getLocation(), MD).getEPI(EPI); + ImplicitType = cast<FunctionProtoType>( + Context.getFunctionType(ReturnType, &ArgType, ExpectedParams, EPI)); + } // C++11 [dcl.fct.def.default]p2: // An explicitly-defaulted function may be declared constexpr only if it @@ -6685,7 +6713,10 @@ namespace { } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { +Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc, + CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] @@ -6732,7 +6763,21 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (Expr *E = F->getInClassInitializer()) ExceptSpec.CalledExpr(E); else if (!F->isInvalidDecl()) - ExceptSpec.SetDelayed(); + // DR1351: + // If the brace-or-equal-initializer of a non-static data member + // invokes a defaulted default constructor of its class or of an + // enclosing class in a potentially evaluated subexpression, the + // program is ill-formed. + // + // This resolution is unworkable: the exception specification of the + // default constructor can be needed in an unevaluated context, in + // particular, in the operand of a noexcept-expression, and we can be + // unable to compute an exception specification for an enclosed class. + // + // We do not allow an in-class initializer to require the evaluation + // of the exception specification for any in-class initializer whose + // definition is not lexically complete. + Diag(Loc, diag::err_in_class_initializer_references_def_ctor) << MD; } else if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs<RecordType>()) { CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl()); @@ -6761,10 +6806,6 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( assert(!ClassDecl->hasUserDeclaredConstructor() && "Should not build implicit default constructor!"); - ImplicitExceptionSpecification Spec = - ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, CXXDefaultConstructor, false); @@ -6777,15 +6818,20 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( = Context.DeclarationNames.getCXXConstructorName(ClassType); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXConstructorDecl *DefaultCon = CXXConstructorDecl::Create( - Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(Context.VoidTy, 0, 0, EPI), /*TInfo=*/0, + Context, ClassDecl, ClassLoc, NameInfo, /*Type*/QualType(), /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true, Constexpr); DefaultCon->setAccess(AS_public); DefaultCon->setDefaulted(); DefaultCon->setImplicit(); DefaultCon->setTrivial(ClassDecl->hasTrivialDefaultConstructor()); - + + // Build an exception specification pointing back at this constructor. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = DefaultCon; + DefaultCon->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); + // Note that we have declared this constructor. ++ASTContext::NumImplicitDefaultConstructorsDeclared; @@ -6830,58 +6876,14 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, } } -/// Get any existing defaulted default constructor for the given class. Do not -/// implicitly define one if it does not exist. -static CXXConstructorDecl *getDefaultedDefaultConstructorUnsafe(Sema &Self, - CXXRecordDecl *D) { - ASTContext &Context = Self.Context; - QualType ClassType = Context.getTypeDeclType(D); - DeclarationName ConstructorName - = Context.DeclarationNames.getCXXConstructorName( - Context.getCanonicalType(ClassType.getUnqualifiedType())); - - DeclContext::lookup_const_iterator Con, ConEnd; - for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName); - Con != ConEnd; ++Con) { - // A function template cannot be defaulted. - if (isa<FunctionTemplateDecl>(*Con)) - continue; - - CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con); - if (Constructor->isDefaultConstructor()) - return Constructor->isDefaulted() ? Constructor : 0; - } - return 0; -} - void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { if (!D) return; AdjustDeclIfTemplate(D); CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(D); - CXXConstructorDecl *CtorDecl - = getDefaultedDefaultConstructorUnsafe(*this, ClassDecl); - - if (!CtorDecl) return; - - // Compute the exception specification for the default constructor. - const FunctionProtoType *CtorTy = - CtorDecl->getType()->castAs<FunctionProtoType>(); - if (CtorTy->getExceptionSpecType() == EST_Delayed) { - // FIXME: Don't do this unless the exception spec is needed. - ImplicitExceptionSpecification Spec = - ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl); - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); - assert(EPI.ExceptionSpecType != EST_Delayed); - CtorDecl->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); - } - - // If the default constructor is explicitly defaulted, checking the exception - // specification is deferred until now. - if (!CtorDecl->isInvalidDecl() && CtorDecl->isExplicitlyDefaulted() && - !ClassDecl->isDependentType()) - CheckExplicitlyDefaultedSpecialMember(CtorDecl); + if (!ClassDecl->isDependentType()) + CheckExplicitlyDefaultedMethods(ClassDecl); } void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { @@ -7065,7 +7067,9 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { +Sema::ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have // an exception-specification. @@ -7112,14 +7116,8 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { // 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. - QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI); - CanQualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); SourceLocation ClassLoc = ClassDecl->getLocation(); @@ -7127,24 +7125,27 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { = Context.DeclarationNames.getCXXDestructorName(ClassType); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXDestructorDecl *Destructor - = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0, - /*isInline=*/true, + = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, + QualType(), 0, /*isInline=*/true, /*isImplicitlyDeclared=*/true); Destructor->setAccess(AS_public); Destructor->setDefaulted(); Destructor->setImplicit(); Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); - + + // Build an exception specification pointing back at this destructor. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = Destructor; + Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); + // Note that we have declared this destructor. ++ASTContext::NumImplicitDestructorsDeclared; - + // Introduce this destructor into its scope. if (Scope *S = getScopeForContext(ClassDecl)) PushOnScopeChains(Destructor, S, false); ClassDecl->addDecl(Destructor); - - // This could be uniqued if it ever proves significant. - Destructor->setTypeSourceInfo(Context.getTrivialTypeSourceInfo(Ty)); AddOverriddenMethods(ClassDecl, Destructor); @@ -7194,15 +7195,6 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, /// \brief Perform any semantic analysis which needs to be delayed until all /// pending class member declarations have been parsed. void Sema::ActOnFinishCXXMemberDecls() { - // Now we have parsed all exception specifications, determine the implicit - // exception specifications for destructors. - for (unsigned i = 0, e = DelayedDestructorExceptionSpecs.size(); - i != e; ++i) { - CXXDestructorDecl *Dtor = DelayedDestructorExceptionSpecs[i]; - AdjustDestructorExceptionSpec(Dtor->getParent(), Dtor, true); - } - DelayedDestructorExceptionSpecs.clear(); - // Perform any deferred checking of exception specifications for virtual // destructors. for (unsigned i = 0, e = DelayedDestructorExceptionSpecChecks.size(); @@ -7217,44 +7209,33 @@ void Sema::ActOnFinishCXXMemberDecls() { DelayedDestructorExceptionSpecChecks.clear(); } -void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *classDecl, - CXXDestructorDecl *destructor, - bool WasDelayed) { +void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, + CXXDestructorDecl *Destructor) { + assert(getLangOpts().CPlusPlus0x && + "adjusting dtor exception specs was introduced in c++11"); + // C++11 [class.dtor]p3: // A declaration of a destructor that does not have an exception- // specification is implicitly considered to have the same exception- // specification as an implicit declaration. - const FunctionProtoType *dtorType = destructor->getType()-> + const FunctionProtoType *DtorType = Destructor->getType()-> getAs<FunctionProtoType>(); - if (!WasDelayed && dtorType->hasExceptionSpec()) + if (DtorType->hasExceptionSpec()) return; - ImplicitExceptionSpecification exceptSpec = - ComputeDefaultedDtorExceptionSpec(classDecl); - // Replace the destructor's type, building off the existing one. Fortunately, // the only thing of interest in the destructor type is its extended info. // The return and arguments are fixed. - FunctionProtoType::ExtProtoInfo epi = dtorType->getExtProtoInfo(); - epi.ExceptionSpecType = exceptSpec.getExceptionSpecType(); - epi.NumExceptions = exceptSpec.size(); - epi.Exceptions = exceptSpec.data(); - QualType ty = Context.getFunctionType(Context.VoidTy, 0, 0, epi); - - destructor->setType(ty); - - // If we can't compute the exception specification for this destructor yet - // (because it depends on an exception specification which we have not parsed - // yet), make a note that we need to try again when the class is complete. - if (epi.ExceptionSpecType == EST_Delayed) { - assert(!WasDelayed && "couldn't compute destructor exception spec"); - DelayedDestructorExceptionSpecs.push_back(destructor); - } + FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo(); + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = Destructor; + Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); // FIXME: If the destructor has a body that could throw, and the newly created // spec doesn't allow exceptions, we should emit a warning, because this // change in behavior can break conforming C++03 programs at runtime. - // However, we don't have a body yet, so it needs to be done somewhere else. + // However, we don't have a body or an exception specification yet, so it + // needs to be done somewhere else. } /// \brief Builds a statement that copies/moves the given entity from \p From to @@ -7456,11 +7437,13 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, Loc, Copy.take()); } -std::pair<Sema::ImplicitExceptionSpecification, bool> -Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( - CXXRecordDecl *ClassDecl) { +/// Determine whether an implicit copy assignment operator for ClassDecl has a +/// const argument. +/// FIXME: It ought to be possible to store this on the record. +static bool isImplicitCopyAssignmentArgConst(Sema &S, + CXXRecordDecl *ClassDecl) { if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(*this), true); + return true; // C++ [class.copy]p10: // If the class definition does not explicitly declare a copy @@ -7471,37 +7454,34 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // X& X::operator=(const X&) // // if - bool HasConstCopyAssignment = true; - // -- each direct base class B of X has a copy assignment operator // whose parameter is of type const B&, const volatile B& or B, // and for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); - HasConstCopyAssignment && Base != BaseEnd; ++Base) { + Base != BaseEnd; ++Base) { // We'll handle this below - if (LangOpts.CPlusPlus0x && Base->isVirtual()) + if (S.getLangOpts().CPlusPlus0x && Base->isVirtual()) continue; assert(!Base->getType()->isDependentType() && "Cannot generate implicit members for class with dependent bases."); CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl(); - HasConstCopyAssignment &= - (bool)LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, - false, 0); + if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, false, 0)) + return false; } // In C++11, the above citation has "or virtual" added - if (LangOpts.CPlusPlus0x) { + if (S.getLangOpts().CPlusPlus0x) { for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), BaseEnd = ClassDecl->vbases_end(); - HasConstCopyAssignment && Base != BaseEnd; ++Base) { + Base != BaseEnd; ++Base) { assert(!Base->getType()->isDependentType() && "Cannot generate implicit members for class with dependent bases."); CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl(); - HasConstCopyAssignment &= - (bool)LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, - false, 0); + if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, + false, 0)) + return false; } } @@ -7511,23 +7491,36 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // const volatile M& or M. for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); - HasConstCopyAssignment && Field != FieldEnd; - ++Field) { - QualType FieldType = Context.getBaseElementType(Field->getType()); - if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - HasConstCopyAssignment &= - (bool)LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const, - false, 0); - } + Field != FieldEnd; ++Field) { + QualType FieldType = S.Context.getBaseElementType(Field->getType()); + if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) + if (!S.LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const, + false, 0)) + return false; } // Otherwise, the implicitly declared copy assignment operator will // have the form // // X& X::operator=(X&) - + + return true; +} + +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + + ImplicitExceptionSpecification ExceptSpec(*this); + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + + const FunctionProtoType *T = MD->getType()->castAs<FunctionProtoType>(); + assert(T->getNumArgs() == 1 && "not a copy assignment op"); + unsigned ArgQuals = T->getArgType(0).getNonReferenceType().getCVRQualifiers(); + // C++ [except.spec]p14: - // An implicitly declared special member function (Clause 12) shall have an + // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] // It is unspecified whether or not an implicit copy assignment operator @@ -7536,8 +7529,6 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // Based on a similar decision made for constness in C++0x, we're erring on // the side of assuming such calls to be made regardless of whether they // actually happen. - ImplicitExceptionSpecification ExceptSpec(*this); - unsigned ArgQuals = HasConstCopyAssignment ? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); Base != BaseEnd; ++Base) { @@ -7575,7 +7566,7 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( } } - return std::make_pair(ExceptSpec, HasConstCopyAssignment); + return ExceptSpec; } CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { @@ -7584,26 +7575,19 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { // for determining the argument type of the operator. Note also that // operators taking an object instead of a reference are allowed. - ImplicitExceptionSpecification Spec(*this); - bool Const; - llvm::tie(Spec, Const) = - ComputeDefaultedCopyAssignmentExceptionSpecAndConst(ClassDecl); - QualType ArgType = Context.getTypeDeclType(ClassDecl); QualType RetType = Context.getLValueReferenceType(ArgType); - if (Const) + if (isImplicitCopyAssignmentArgConst(*this, ClassDecl)) ArgType = ArgType.withConst(); ArgType = Context.getLValueReferenceType(ArgType); // An implicitly-declared copy assignment operator is an inline public // member of its class. - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXMethodDecl *CopyAssignment - = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(RetType, &ArgType, 1, EPI), + = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isStatic=*/false, /*StorageClassAsWritten=*/SC_None, /*isInline=*/true, /*isConstexpr=*/false, @@ -7612,7 +7596,13 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { CopyAssignment->setDefaulted(); CopyAssignment->setImplicit(); CopyAssignment->setTrivial(ClassDecl->hasTrivialCopyAssignment()); - + + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = CopyAssignment; + CopyAssignment->setType(Context.getFunctionType(RetType, &ArgType, 1, EPI)); + // Add the parameter to the operator. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment, ClassLoc, ClassLoc, /*Id=*/0, @@ -7950,9 +7940,10 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { - ImplicitExceptionSpecification ExceptSpec(*this); +Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + ImplicitExceptionSpecification ExceptSpec(*this); if (ClassDecl->isInvalidDecl()) return ExceptSpec; @@ -8120,22 +8111,17 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { // Note: The following rules are largely analoguous to the move // constructor rules. - ImplicitExceptionSpecification Spec( - ComputeDefaultedMoveAssignmentExceptionSpec(ClassDecl)); - QualType ArgType = Context.getTypeDeclType(ClassDecl); QualType RetType = Context.getLValueReferenceType(ArgType); ArgType = Context.getRValueReferenceType(ArgType); // An implicitly-declared move assignment operator is an inline public // member of its class. - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationNameInfo NameInfo(Name, ClassLoc); CXXMethodDecl *MoveAssignment - = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(RetType, &ArgType, 1, EPI), + = CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isStatic=*/false, /*StorageClassAsWritten=*/SC_None, /*isInline=*/true, @@ -8146,6 +8132,12 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { MoveAssignment->setImplicit(); MoveAssignment->setTrivial(ClassDecl->hasTrivialMoveAssignment()); + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = MoveAssignment; + MoveAssignment->setType(Context.getFunctionType(RetType, &ArgType, 1, EPI)); + // Add the parameter to the operator. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveAssignment, ClassLoc, ClassLoc, /*Id=*/0, @@ -8496,10 +8488,12 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, } } -std::pair<Sema::ImplicitExceptionSpecification, bool> -Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { +/// Determine whether an implicit copy constructor for ClassDecl has a const +/// argument. +/// FIXME: It ought to be possible to store this on the record. +static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl) { if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(*this), true); + return true; // C++ [class.copy]p5: // The implicitly-declared copy constructor for a class X will @@ -8508,60 +8502,71 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { // X::X(const X&) // // if - // FIXME: It ought to be possible to store this on the record. - bool HasConstCopyConstructor = true; - // -- each direct or virtual base class B of X has a copy // constructor whose first parameter is of type const B& or // const volatile B&, and for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); - HasConstCopyConstructor && Base != BaseEnd; - ++Base) { + Base != BaseEnd; ++Base) { // Virtual bases are handled below. if (Base->isVirtual()) continue; - + CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - HasConstCopyConstructor &= - (bool)LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const); + // FIXME: This lookup is wrong. If the copy ctor for a member or base is + // ambiguous, we should still produce a constructor with a const-qualified + // parameter. + if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const)) + return false; } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), BaseEnd = ClassDecl->vbases_end(); - HasConstCopyConstructor && Base != BaseEnd; - ++Base) { + Base != BaseEnd; ++Base) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - HasConstCopyConstructor &= - (bool)LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const); + if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const)) + return false; } - + // -- for all the nonstatic data members of X that are of a // class type M (or array thereof), each such class type // has a copy constructor whose first parameter is of type // const M& or const volatile M&. for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); - HasConstCopyConstructor && Field != FieldEnd; - ++Field) { - QualType FieldType = Context.getBaseElementType(Field->getType()); + Field != FieldEnd; ++Field) { + QualType FieldType = S.Context.getBaseElementType(Field->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - HasConstCopyConstructor &= - (bool)LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const); + if (!S.LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const)) + return false; } } + // Otherwise, the implicitly declared copy constructor will have // the form // // X::X(X&) - + + return true; +} + +Sema::ImplicitExceptionSpecification +Sema::ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + + ImplicitExceptionSpecification ExceptSpec(*this); + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + + const FunctionProtoType *T = MD->getType()->castAs<FunctionProtoType>(); + assert(T->getNumArgs() >= 1 && "not a copy ctor"); + unsigned Quals = T->getArgType(0).getNonReferenceType().getCVRQualifiers(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] - ImplicitExceptionSpecification ExceptSpec(*this); - unsigned Quals = HasConstCopyConstructor? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); Base != BaseEnd; @@ -8599,7 +8604,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { } } - return std::make_pair(ExceptSpec, HasConstCopyConstructor); + return ExceptSpec; } CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( @@ -8608,18 +8613,12 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // If the class definition does not explicitly declare a copy // constructor, one is declared implicitly. - ImplicitExceptionSpecification Spec(*this); - bool Const; - llvm::tie(Spec, Const) = - ComputeDefaultedCopyCtorExceptionSpecAndConst(ClassDecl); - QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ArgType = ClassType; + bool Const = isImplicitCopyCtorArgConst(*this, ClassDecl); if (Const) ArgType = ArgType.withConst(); ArgType = Context.getLValueReferenceType(ArgType); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, CXXCopyConstructor, @@ -8634,14 +8633,20 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // An implicitly-declared copy constructor is an inline public // member of its class. CXXConstructorDecl *CopyConstructor = CXXConstructorDecl::Create( - Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0, + Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true, Constexpr); CopyConstructor->setAccess(AS_public); CopyConstructor->setDefaulted(); CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor()); + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = CopyConstructor; + CopyConstructor->setType( + Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + // Note that we have declared this constructor. ++ASTContext::NumImplicitCopyConstructorsDeclared; @@ -8705,7 +8710,9 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation, } Sema::ImplicitExceptionSpecification -Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { +Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXMethodDecl *MD) { + CXXRecordDecl *ClassDecl = MD->getParent(); + // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] @@ -8788,13 +8795,8 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( return 0; } - ImplicitExceptionSpecification Spec( - ComputeDefaultedMoveCtorExceptionSpec(ClassDecl)); - QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ArgType = Context.getRValueReferenceType(ClassType); - - FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, CXXMoveConstructor, @@ -8810,14 +8812,20 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( // An implicitly-declared copy/move constructor is an inline public // member of its class. CXXConstructorDecl *MoveConstructor = CXXConstructorDecl::Create( - Context, ClassDecl, ClassLoc, NameInfo, - Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0, + Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/0, /*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true, Constexpr); MoveConstructor->setAccess(AS_public); MoveConstructor->setDefaulted(); MoveConstructor->setTrivial(ClassDecl->hasTrivialMoveConstructor()); + // Build an exception specification pointing back at this member. + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = MoveConstructor; + MoveConstructor->setType( + Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI)); + // Add the parameter to the constructor. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, MoveConstructor, ClassLoc, ClassLoc, @@ -10427,10 +10435,11 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { if (Primary == Primary->getCanonicalDecl()) return; + CheckExplicitlyDefaultedSpecialMember(MD); + switch (Member) { case CXXDefaultConstructor: { CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD); - CheckExplicitlyDefaultedSpecialMember(CD); if (!CD->isInvalidDecl()) DefineImplicitDefaultConstructor(DefaultLoc, CD); break; @@ -10438,14 +10447,12 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { case CXXCopyConstructor: { CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD); - CheckExplicitlyDefaultedSpecialMember(CD); if (!CD->isInvalidDecl()) DefineImplicitCopyConstructor(DefaultLoc, CD); break; } case CXXCopyAssignment: { - CheckExplicitlyDefaultedSpecialMember(MD); if (!MD->isInvalidDecl()) DefineImplicitCopyAssignment(DefaultLoc, MD); break; @@ -10453,7 +10460,6 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { case CXXDestructor: { CXXDestructorDecl *DD = cast<CXXDestructorDecl>(MD); - CheckExplicitlyDefaultedSpecialMember(DD); if (!DD->isInvalidDecl()) DefineImplicitDestructor(DefaultLoc, DD); break; @@ -10461,14 +10467,12 @@ void Sema::SetDeclDefaulted(Decl *Dcl, |