diff options
Diffstat (limited to 'lib/Sema')
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 184 | ||||
-rw-r--r-- | lib/Sema/SemaExceptionSpec.cpp | 245 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiateDecl.cpp | 20 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 17 |
6 files changed, 371 insertions, 117 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c9678644cb..3b2a062162 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1552,8 +1552,8 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, mergeParamDeclAttributes(*ni, *oi, Context); } -/// MergeVarDecl - We parsed a variable 'New' which has the same name and scope -/// as a previous declaration 'Old'. Figure out how to merge their types, +/// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and +/// scope as a previous declaration 'Old'. Figure out how to merge their types, /// emitting diagnostics as appropriate. /// /// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back @@ -1570,8 +1570,10 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) { if (AT && !AT->isDeduced()) { // We don't know what the new type is until the initializer is attached. return; - } else if (Context.hasSameType(New->getType(), Old->getType())) - return; + } else if (Context.hasSameType(New->getType(), Old->getType())) { + // These could still be something that needs exception specs checked. + return MergeVarDeclExceptionSpecs(New, Old); + } // C++ [basic.link]p10: // [...] the types specified by all declarations referring to a given // object or function shall be identical, except that declarations for an diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 2bce585dd2..f6551054c1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -383,6 +383,48 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) { return Invalid; } +/// \brief Merge the exception specifications of two variable declarations. +/// +/// This is called when there's a redeclaration of a VarDecl. The function +/// checks if the redeclaration might have an exception specification and +/// validates compatibility and merges the specs if necessary. +void Sema::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) { + // Shortcut if exceptions are disabled. + if (!getLangOptions().CXXExceptions) + return; + + assert(Context.hasSameType(New->getType(), Old->getType()) && + "Should only be called if types are otherwise the same."); + + QualType NewType = New->getType(); + QualType OldType = Old->getType(); + + // We're only interested in pointers and references to functions, as well + // as pointers to member functions. + if (const ReferenceType *R = NewType->getAs<ReferenceType>()) { + NewType = R->getPointeeType(); + OldType = OldType->getAs<ReferenceType>()->getPointeeType(); + } else if (const PointerType *P = NewType->getAs<PointerType>()) { + NewType = P->getPointeeType(); + OldType = OldType->getAs<PointerType>()->getPointeeType(); + } else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) { + NewType = M->getPointeeType(); + OldType = OldType->getAs<MemberPointerType>()->getPointeeType(); + } + + if (!NewType->isFunctionProtoType()) + return; + + // There's lots of special cases for functions. For function pointers, system + // libraries are hopefully not as broken so that we don't need these + // workarounds. + if (CheckEquivalentExceptionSpec( + OldType->getAs<FunctionProtoType>(), Old->getLocation(), + NewType->getAs<FunctionProtoType>(), New->getLocation())) { + New->setInvalidDecl(); + } +} + /// CheckCXXDefaultArguments - Verify that the default arguments for a /// function declaration are well-formed according to C++ /// [dcl.fct.default]. @@ -2978,53 +3020,101 @@ namespace { /// implicitly-declared special member functions. class ImplicitExceptionSpecification { ASTContext &Context; - bool AllowsAllExceptions; + // We order exception specifications thus: + // noexcept is the most restrictive, but is only used in C++0x. + // throw() comes next. + // Then a throw(collected exceptions) + // Finally no specification. + // throw(...) is used instead if any called function uses it. + ExceptionSpecificationType ComputedEST; llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen; llvm::SmallVector<QualType, 4> Exceptions; - + + void ClearExceptions() { + ExceptionsSeen.clear(); + Exceptions.clear(); + } + public: explicit ImplicitExceptionSpecification(ASTContext &Context) - : Context(Context), AllowsAllExceptions(false) { } - - /// \brief Whether the special member function should have any - /// exception specification at all. - bool hasExceptionSpecification() const { - return !AllowsAllExceptions; + : Context(Context), ComputedEST(EST_BasicNoexcept) { + if (!Context.getLangOptions().CPlusPlus0x) + ComputedEST = EST_DynamicNone; } - - /// \brief Whether the special member function should have a - /// throw(...) exception specification (a Microsoft extension). - bool hasAnyExceptionSpecification() const { - return false; + + /// \brief Get the computed exception specification type. + ExceptionSpecificationType getExceptionSpecType() const { + assert(ComputedEST != EST_ComputedNoexcept && + "noexcept(expr) should not be a possible result"); + return ComputedEST; } - + /// \brief The number of exceptions in the exception specification. unsigned size() const { return Exceptions.size(); } - + /// \brief The set of exceptions in the exception specification. const QualType *data() const { return Exceptions.data(); } - - /// \brief Note that + + /// \brief Integrate another called method into the collected data. void CalledDecl(CXXMethodDecl *Method) { - // If we already know that we allow all exceptions, do nothing. - if (AllowsAllExceptions || !Method) + // If we have an MSAny spec already, don't bother. + if (!Method || ComputedEST == EST_MSAny) return; - + const FunctionProtoType *Proto = Method->getType()->getAs<FunctionProtoType>(); - + + ExceptionSpecificationType EST = Proto->getExceptionSpecType(); + // If this function can throw any exceptions, make a note of that. - if (!Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec()) { - AllowsAllExceptions = true; - ExceptionsSeen.clear(); - Exceptions.clear(); + if (EST == EST_MSAny || EST == EST_None) { + ClearExceptions(); + ComputedEST = EST; return; } - + + // If this function has a basic noexcept, it doesn't affect the outcome. + if (EST == EST_BasicNoexcept) + return; + + // If we have a throw-all spec at this point, ignore the function. + if (ComputedEST == EST_None) + return; + + // If we're still at noexcept(true) and there's a nothrow() callee, + // change to that specification. + if (EST == EST_DynamicNone) { + if (ComputedEST == EST_BasicNoexcept) + ComputedEST = EST_DynamicNone; + return; + } + + // Check out noexcept specs. + if (EST == EST_ComputedNoexcept) { + FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec(); + assert(NR != FunctionProtoType::NR_NoNoexcept && + "Must have noexcept result for EST_ComputedNoexcept."); + assert(NR != FunctionProtoType::NR_Dependent && + "Should not generate implicit declarations for dependent cases, " + "and don't know how to handle them anyway."); + + // noexcept(false) -> no spec on the new function + if (NR == FunctionProtoType::NR_Throw) { + ClearExceptions(); + ComputedEST = EST_None; + } + // noexcept(true) won't change anything either. + return; + } + + assert(EST == EST_Dynamic && "EST case not considered earlier."); + assert(ComputedEST != EST_None && + "Shouldn't collect exceptions when throw-all is guaranteed."); + ComputedEST = EST_Dynamic; // Record the exceptions in this function's exception specification. for (FunctionProtoType::exception_iterator E = Proto->exception_begin(), EEnd = Proto->exception_end(); - E != EEnd; ++E) + E != EEnd; ++E) if (ExceptionsSeen.insert(Context.getCanonicalType(*E))) Exceptions.push_back(*E); } @@ -4672,7 +4762,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( // exception-specification. [...] ImplicitExceptionSpecification ExceptSpec(Context); - // Direct base-class destructors. + // Direct base-class constructors. for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), BEnd = ClassDecl->bases_end(); B != BEnd; ++B) { @@ -4688,8 +4778,8 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( ExceptSpec.CalledDecl(Constructor); } } - - // Virtual base-class destructors. + + // Virtual base-class constructors. for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(), BEnd = ClassDecl->vbases_end(); B != BEnd; ++B) { @@ -4702,8 +4792,8 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( ExceptSpec.CalledDecl(Constructor); } } - - // Field destructors. + + // Field constructors. for (RecordDecl::field_iterator F = ClassDecl->field_begin(), FEnd = ClassDecl->field_end(); F != FEnd; ++F) { @@ -4720,9 +4810,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( } FunctionProtoType::ExtProtoInfo EPI; - EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ? - (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) : - EST_None; + EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType(); EPI.NumExceptions = ExceptSpec.size(); EPI.Exceptions = ExceptSpec.data(); @@ -4997,16 +5085,14 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { ExceptSpec.CalledDecl( LookupDestructor(cast<CXXRecordDecl>(RecordTy->getDecl()))); } - + // Create the actual destructor declaration. FunctionProtoType::ExtProtoInfo EPI; - EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ? - (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) : - EST_None; + EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType(); EPI.NumExceptions = ExceptSpec.size(); EPI.Exceptions = ExceptSpec.data(); QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI); - + CanQualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); SourceLocation ClassLoc = ClassDecl->getLocation(); @@ -5014,9 +5100,9 @@ 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, - /*isImplicitlyDeclared=*/true); + = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0, + /*isInline=*/true, + /*isImplicitlyDeclared=*/true); Destructor->setAccess(AS_public); Destructor->setImplicit(); Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); @@ -5396,13 +5482,11 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { ExceptSpec.CalledDecl(CopyAssign); } } - + // An implicitly-declared copy assignment operator is an inline public // member of its class. FunctionProtoType::ExtProtoInfo EPI; - EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ? - (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) : - EST_None; + EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType(); EPI.NumExceptions = ExceptSpec.size(); EPI.Exceptions = ExceptSpec.data(); DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); @@ -5860,13 +5944,11 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( ExceptSpec.CalledDecl(CopyConstructor); } } - + // An implicitly-declared copy constructor is an inline public // member of its class. FunctionProtoType::ExtProtoInfo EPI; - EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ? - (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) : - EST_None; + EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType(); EPI.NumExceptions = ExceptSpec.size(); EPI.Exceptions = ExceptSpec.data(); DeclarationName Name diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index dce4c6e220..01ee712254 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -105,7 +105,8 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { New->getType()->getAs<FunctionProtoType>(), New->getLocation(), &MissingExceptionSpecification, - &MissingEmptyExceptionSpecification)) + &MissingEmptyExceptionSpecification, + /*AllowNoexceptAllMatchWithNoSpec=*/true)) return false; // The failure was something other than an empty exception @@ -129,8 +130,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { Context.getSourceManager().isInSystemHeader(Old->getLocation())) && Old->isExternC()) { FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo(); - EPI.ExceptionSpecType = EST_Dynamic; - EPI.NumExceptions = 0; + EPI.ExceptionSpecType = EST_DynamicNone; QualType NewType = Context.getFunctionType(NewProto->getResultType(), NewProto->arg_type_begin(), NewProto->getNumArgs(), @@ -144,11 +144,14 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { = Old->getType()->getAs<FunctionProtoType>(); FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo(); - EPI.ExceptionSpecType = OldProto->hasExceptionSpec() ? - (OldProto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) : - EST_None; - EPI.NumExceptions = OldProto->getNumExceptions(); - EPI.Exceptions = OldProto->exception_begin(); + EPI.ExceptionSpecType = OldProto->getExceptionSpecType(); + if (EPI.ExceptionSpecType == EST_Dynamic) { + EPI.NumExceptions = OldProto->getNumExceptions(); + EPI.Exceptions = OldProto->exception_begin(); + } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { + // FIXME: We can't just take the expression from the old prototype. It + // likely contains references to the old prototype's parameters. + } // Update the type of the function with the appropriate exception // specification. @@ -178,20 +181,43 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { // Warn about the lack of exception specification. llvm::SmallString<128> ExceptionSpecString; llvm::raw_svector_ostream OS(ExceptionSpecString); - OS << "throw("; - bool OnFirstException = true; - for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(), - EEnd = OldProto->exception_end(); - E != EEnd; - ++E) { - if (OnFirstException) - OnFirstException = false; - else - OS << ", "; - - OS << E->getAsString(Context.PrintingPolicy); + switch (OldProto->getExceptionSpecType()) { + case EST_DynamicNone: + OS << "throw()"; + break; + + case EST_Dynamic: { + OS << "throw("; + bool OnFirstException = true; + for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(), + EEnd = OldProto->exception_end(); + E != EEnd; + ++E) { + if (OnFirstException) + OnFirstException = false; + else + OS << ", "; + + OS << E->getAsString(Context.PrintingPolicy); + } + OS << ")"; + break; + } + + case EST_BasicNoexcept: + OS << "noexcept"; + break; + + case EST_ComputedNoexcept: + OS << "noexcept("; + OldProto->getNoexceptExpr()->printPretty(OS, Context, 0, + Context.PrintingPolicy); + OS << ")"; + break; + + default: + assert(false && "This spec type is compatible with none."); } - OS << ")"; OS.flush(); SourceLocation FixItLoc; @@ -236,18 +262,17 @@ bool Sema::CheckEquivalentExceptionSpec( Old, OldLoc, New, NewLoc); } -/// CheckEquivalentExceptionSpec - Check if the two types have equivalent -/// exception specifications. Exception specifications are equivalent if -/// they allow exactly the same set of exception types. It does not matter how -/// that is achieved. See C++ [except.spec]p2. -bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, +/// CheckEquivalentExceptionSpec - Check if the two types have compatible +/// exception specifications. See C++ [except.spec]p3. +bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, - const FunctionProtoType *Old, + const FunctionProtoType *Old, SourceLocation OldLoc, - const FunctionProtoType *New, + const FunctionProtoType *New, SourceLocation NewLoc, bool *MissingExceptionSpecification, - bool *MissingEmptyExceptionSpecification) { + bool*MissingEmptyExceptionSpecification, + bool AllowNoexceptAllMatchWithNoSpec) { // Just completely ignore this under -fno-exceptions. if (!getLangOptions().CXXExceptions) return false; @@ -258,29 +283,109 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, if (MissingEmptyExceptionSpecification) *MissingEmptyExceptionSpecification = false; - bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec(); - bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec(); + // C++0x [except.spec]p3: Two exception-specifications are compatible if: + // - both are non-throwing, regardless of their form, + // - both have the form noexcept(constant-expression) and the constant- + // expressions are equivalent, + // - one exception-specification is a noexcept-specification allowing all + // exceptions and the other is of the form throw(type-id-list), or + // - both are dynamic-exception-specifications that have the same set of + // adjusted types. + // + // C++0x [except.spec]p12: An exception-specifcation is non-throwing if it is + // of the form throw(), noexcept, or noexcept(constant-expression) where the + // constant-expression yields true. + // + // CWG 1073 Proposed resolution: Strike the third bullet above. + // + // C++0x [except.spec]p4: If any declaration of a function has an exception- + // specifier that is not a noexcept-specification allowing all exceptions, + // all declarations [...] of that function shall have a compatible + // exception-specification. + // + // That last point basically means that noexcept(false) matches no spec. + // It's considered when AllowNoexceptAllMatchWithNoSpec is true. + + ExceptionSpecificationType OldEST = Old->getExceptionSpecType(); + ExceptionSpecificationType NewEST = New->getExceptionSpecType(); + + // Shortcut the case where both have no spec. + if (OldEST == EST_None && NewEST == EST_None) + return false; + + FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec(); + FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec(); + if (OldNR == FunctionProtoType::NR_BadNoexcept || + NewNR == FunctionProtoType::NR_BadNoexcept) + return false; + + // Dependent noexcept specifiers are compatible with each other, but nothing + // else. + // One noexcept is compatible with another if the argument is the same + if (OldNR == NewNR && + OldNR != FunctionProtoType::NR_NoNoexcept && + NewNR != FunctionProtoType::NR_NoNoexcept) + return false; + if (OldNR != NewNR && + OldNR != FunctionProtoType::NR_NoNoexcept && + NewNR != FunctionProtoType::NR_NoNoexcept) { + Diag(NewLoc, DiagID); + if (NoteID.getDiagID() != 0) + Diag(OldLoc, NoteID); + return true; + } + if (getLangOptions().Microsoft) { // Treat throw(whatever) as throw(...) to be compatible with MS headers. - if (New->hasExceptionSpec() && New->getNumExceptions() > 0) - NewAny = true; - if (Old->hasExceptionSpec() && Old->getNumExceptions() > 0) - OldAny = true; + if (OldEST == EST_Dynamic) + OldEST = EST_MSAny; + if (NewEST == EST_Dynamic) + NewEST = EST_MSAny; } - if (OldAny && NewAny) + // The MS extension throw(...) is compatible with itself. + if (OldEST == EST_MSAny && NewEST == EST_MSAny) + return false; + + // It's also compatible with no spec. + if ((OldEST == EST_None && NewEST == EST_MSAny) || + (OldEST == EST_MSAny && NewEST == EST_None)) + return false; + + // It's also compatible with noexcept(false). + if (OldEST == EST_MSAny && NewNR == FunctionProtoType::NR_Throw) + return false; + if (NewEST == EST_MSAny && OldNR == FunctionProtoType::NR_Throw) return false; - if (OldAny || NewAny) { + + // As described above, noexcept(false) matches no spec only for functions. + if (AllowNoexceptAllMatchWithNoSpec) { + if (OldEST == EST_None && NewNR == FunctionProtoType::NR_Throw) + return false; + if (NewEST == EST_None && OldNR == FunctionProtoType::NR_Throw) + return false; + } + + // Any non-throwing specifications are compatible. + bool OldNonThrowing = OldNR == FunctionProtoType::NR_Nothrow || + OldEST == EST_DynamicNone; + bool NewNonThrowing = NewNR == FunctionProtoType::NR_Nothrow || + NewEST == EST_DynamicNone; + if (OldNonThrowing && NewNonThrowing) + return false; + + // At this point, the only remaining valid case is two matching dynamic + // specifications. We return here unless both specifications are dynamic. + if (OldEST != EST_Dynamic || NewEST != EST_Dynamic) { if (MissingExceptionSpecification && Old->hasExceptionSpec() && !New->hasExceptionSpec()) { // The old type has an exception specification of some sort, but // the new type does not. *MissingExceptionSpecification = true; - if (MissingEmptyExceptionSpecification && - !Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0) { - // The old type has a throw() exception specification and the - // new type has no exception specification, and the caller asked + if (MissingEmptyExceptionSpecification && OldNonThrowing) { + // The old type has a throw() or noexcept(true) exception specification + // and the new type has no exception specification, and the caller asked // to handle this itself. *MissingEmptyExceptionSpecification = true; } @@ -294,8 +399,11 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, return true; } + assert(OldEST == EST_Dynamic && NewEST == EST_Dynamic && + "Exception compatibility logic error: non-dynamic spec slipped through."); + bool Success = true; - // Both have a definite exception spec. Collect the first set, then compare + // Both have a dynamic exception spec. Collect the first set, then compare // to the second. llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes; for (FunctionProtoType::exception_iterator I = Old->exception_begin(), @@ -340,19 +448,66 @@ bool Sema::CheckExceptionSpecSubset( if (!SubLoc.isValid()) SubLoc = SuperLoc; + ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType(); + // If superset contains everything, we're done. - if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec()) + if (SuperEST == EST_None || SuperEST == EST_MSAny) return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + // If there are dependent noexcept specs, assume everything is fine. Unlike + // with the equivalency check, this is safe in this case, because we don't + // want to merge declarations. Checks after instantiation will catch any + // omissions we make here. + // We also shortcut checking if a noexcept expression was bad. + + FunctionProtoType::NoexceptResult SuperNR =Superset->getNoexceptSpec(); + if (SuperNR == FunctionProtoType::NR_BadNoexcept || + SuperNR == FunctionProtoType::NR_Dependent) + return false; + + // Another case of the superset containing everything. + if (SuperNR == FunctionProtoType::NR_Throw) + return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + + ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); + // It does not. If the subset contains everything, we've failed. - if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) { + if (SubEST == EST_None || SubEST == EST_MSAny) { + Diag(SubLoc, DiagID); + if (NoteID.getDiagID() != 0) + Diag(SuperLoc, NoteID); + return true; + } + + FunctionProtoType::NoexceptResult SubNR = Subset->getNoexceptSpec(); + if (SubNR == FunctionProtoType::NR_BadNoexcept || + SubNR == FunctionProtoType::NR_Dependent) + return false; + + // Another case of the subset containing everything. + if (SubNR == FunctionProtoType::NR_Throw) { Diag(SubLoc, DiagID); if (NoteID.getDiagID() != 0) Diag(SuperLoc, NoteID); return true; } - // Neither contains everything. Do a proper comparison. + // If the subset contains nothing, we're done. + if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow) + return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + + // Otherwise, if the superset contains nothing, we've failed. + if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) { + Diag(SubLoc, DiagID); + if (NoteID.getDiagID() != 0) + Diag(SuperLoc, NoteID); + return true; + } + + assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic && + "Exception spec subset: non-dynamic case slipped through."); + + // Neither contains everything or nothing. Do a proper comparison. for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(), SubE = Subset->exception_end(); SubI != SubE; ++SubI) { // Take one type from the subset. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 25f042c473..a1cd43ac23 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1501,10 +1501,12 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, } FunctionProtoType::ExtProtoInfo EPI; - EPI.ExceptionSpecType = EST_Dynamic; if (HasBadAllocExceptionSpec) { + EPI.ExceptionSpecType = EST_Dynamic; EPI.NumExceptions = 1; EPI.Exceptions = &BadAllocType; + } else { + EPI.ExceptionSpecType = EST_DynamicNone; } QualType FnType = Context.getFunctionType(Return, &Argument, 1, EPI); @@ -2421,7 +2423,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T, FoundAssign = true; const FunctionProtoType *CPT = Operator->getType()->getAs<FunctionProtoType>(); - if (!CPT->hasEmptyExceptionSpec()) { + if (!CPT->isNothrow()) { AllNoThrow = false; break; } @@ -2461,9 +2463,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T, FoundConstructor = true; const FunctionProtoType *CPT = Constructor->getType()->getAs<FunctionProtoType>(); - // TODO: check whether evaluating default arguments can throw. + // FIXME: check whether evaluating default arguments can throw. // For now, we'll be conservative and assume that they can throw. - if (!CPT->hasEmptyExceptionSpec() || CPT->getNumArgs() > 1) { + if (!CPT->isNothrow() || CPT->getNumArgs() > 1) { AllNoThrow = false; break; } @@ -2498,7 +2500,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T, = Constructor->getType()->getAs<FunctionProtoType>(); // TODO: check whether evaluating default arguments can throw. // For now, we'll be conservative and assume that they can throw. - return CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0; + return CPT->isNothrow() && CPT->getNumArgs() == 0; } } } diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e60882d6cb..441149f374 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2085,8 +2085,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, const FunctionProtoType *Proto = Tmpl->getType()->getAs<FunctionProtoType>(); assert(Proto && "Function template without prototype?"); - if (Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec() || - Proto->getNoReturnAttr()) { + if (Proto->hasExceptionSpec() || Proto->getNoReturnAttr()) { // The function has an exception specification or a "noreturn" // attribute. Substitute into each of the exception types. llvm::SmallVector<QualType, 4> Exceptions; @@ -2100,7 +2099,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, Unexpanded); assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); - + bool Expand = false; bool RetainExpansion = false; llvm::Optional<unsigned> NumExpansions @@ -2114,7 +2113,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, RetainExpansion, NumExpansions)) break; - + if (!Expand) { // We can't expand this pack expansion into separate arguments yet; // just substitute into the pattern and create a new pack expansion @@ -2130,7 +2129,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, Exceptions.push_back(T); continue; } - + // Substitute into the pack expansion pattern for each template bool Invalid = false; for (unsigned ArgIdx = 0; ArgIdx != *NumExpansions; ++ArgIdx) { @@ -2143,13 +2142,13 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, Invalid = true; break; } - + Exceptions.push_back(T); } - + if (Invalid) break; - + continue; } @@ -2166,9 +2165,8 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, // Rebuild the function type FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo(); - EPI.ExceptionSpecType = Proto->hasExceptionSpec() ? - (Proto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) : - EST_None; + // FIXME: Handle noexcept + EPI.ExceptionSpecType = Proto->getExceptionSpecType(); EPI.NumExceptions = Exceptions.size(); EPI.Exceptions = Exceptions.data(); EPI.ExtInfo = Proto->getExtInfo(); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 62bf8e63ae..f49a49c28f 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1901,7 +1901,22 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, EPI.NumExceptions = Exceptions.size(); EPI.Exceptions = Exceptions.data(); } else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) { - EPI.NoexceptExpr = FTI.NoexceptExpr; + // If an error occurred, there's no expression here. + if (Expr *NoexceptExpr = FTI.NoexceptExpr) { + assert((NoexceptExpr->isTypeDependent() || + NoexceptExpr->getType()->getCanonicalTypeUnqualified() == + Context.BoolTy) && + "Parser should have made sure that the expression is boolean"); + SourceLocation ErrLoc; + llvm::APSInt Dummy; + if (!NoexceptExpr->isValueDependent() && + !NoexceptExpr->isIntegerConstantExpr(Dummy, Context, &ErrLoc, + /*evaluated*/false)) + Diag(ErrLoc, diag::err_noexcept_needs_constant_expression) + << NoexceptExpr->getSourceRange(); + else + EPI.NoexceptExpr = NoexceptExpr; + } } T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), EPI); |