diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-05-03 03:58:32 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-05-03 03:58:32 +0000 |
commit | eaf11ad70904679593d3c7c100bcbcc395c91a89 (patch) | |
tree | 257b3b16ed8dd6c92f949e57987c29e3741a169b /clang/lib/Sema/SemaExceptionSpec.cpp | |
parent | 58fce7e54b50af99023b34a24992fc881ace77b6 (diff) | |
download | bcm5719-llvm-eaf11ad70904679593d3c7c100bcbcc395c91a89.tar.gz bcm5719-llvm-eaf11ad70904679593d3c7c100bcbcc395c91a89.zip |
Track the result of evaluating a computed noexcept specification on the
FunctionProtoType.
We previously re-evaluated the expression each time we wanted to know whether
the type is noexcept or not. We now evaluate the expression exactly once.
This is not quite "no functional change": it fixes a crasher bug during AST
deserialization where we would try to evaluate the noexcept specification in a
situation where we have not deserialized sufficient portions of the AST to
permit such evaluation.
llvm-svn: 331428
Diffstat (limited to 'clang/lib/Sema/SemaExceptionSpec.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExceptionSpec.cpp | 248 |
1 files changed, 106 insertions, 142 deletions
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 21ca08f360a..a81c17bb7fa 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -76,6 +76,29 @@ bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) { .Default(false); } +ExprResult Sema::ActOnNoexceptSpec(SourceLocation NoexceptLoc, + Expr *NoexceptExpr, + ExceptionSpecificationType &EST) { + // FIXME: This is bogus, a noexcept expression is not a condition. + ExprResult Converted = CheckBooleanCondition(NoexceptLoc, NoexceptExpr); + if (Converted.isInvalid()) + return Converted; + + if (Converted.get()->isValueDependent()) { + EST = EST_DependentNoexcept; + return Converted; + } + + llvm::APSInt Result; + Converted = VerifyIntegerConstantExpression( + Converted.get(), &Result, + diag::err_noexcept_needs_constant_expression, + /*AllowFold*/ false); + if (!Converted.isInvalid()) + EST = !Result ? EST_NoexceptFalse : EST_NoexceptTrue; + return Converted; +} + /// CheckSpecifiedExceptionType - Check if the given type is valid in an /// exception specification. Incomplete types, or pointers to incomplete types /// other than void are not allowed. @@ -309,13 +332,19 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { FunctionProtoType::ExceptionSpecInfo ESI = OldProto->getExceptionSpecType(); if (ESI.Type == EST_Dynamic) { + // FIXME: What if the exceptions are described in terms of the old + // prototype's parameters? ESI.Exceptions = OldProto->exceptions(); } - if (ESI.Type == EST_ComputedNoexcept) { - // For computed noexcept, we can't just take the expression from the old - // prototype. It likely contains references to the old prototype's - // parameters. + if (ESI.Type == EST_NoexceptFalse) + ESI.Type = EST_None; + if (ESI.Type == EST_NoexceptTrue) + ESI.Type = EST_BasicNoexcept; + + // For dependent noexcept, we can't just take the expression from the old + // prototype. It likely contains references to the old prototype's parameters. + if (ESI.Type == EST_DependentNoexcept) { New->setInvalidDecl(); } else { // Update the type of the function with the appropriate exception @@ -325,12 +354,12 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { NewProto->getExtProtoInfo().withExceptionSpec(ESI))); } - if (getLangOpts().MicrosoftExt && ESI.Type != EST_ComputedNoexcept) { + if (getLangOpts().MicrosoftExt && ESI.Type != EST_DependentNoexcept) { // Allow missing exception specifications in redeclarations as an extension. DiagID = diag::ext_ms_missing_exception_specification; ReturnValueOnError = false; } else if (New->isReplaceableGlobalAllocationFunction() && - ESI.Type != EST_ComputedNoexcept) { + ESI.Type != EST_DependentNoexcept) { // Allow missing exception specifications in redeclarations as an extension, // when declaring a replaceable global allocation function. DiagID = diag::ext_missing_exception_specification; @@ -367,7 +396,9 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { OS << "noexcept"; break; - case EST_ComputedNoexcept: + case EST_DependentNoexcept: + case EST_NoexceptFalse: + case EST_NoexceptTrue: OS << "noexcept("; assert(OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr"); OldProto->getNoexceptExpr()->printPretty(OS, nullptr, getPrintingPolicy()); @@ -478,63 +509,56 @@ static bool CheckEquivalentExceptionSpecImpl( !isUnresolvedExceptionSpec(NewEST) && "Shouldn't see unknown exception specifications here"); - // Shortcut the case where both have no spec. - if (OldEST == EST_None && NewEST == EST_None) - return false; + CanThrowResult OldCanThrow = Old->canThrow(); + CanThrowResult NewCanThrow = New->canThrow(); - FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec(S.Context); - FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec(S.Context); - if (OldNR == FunctionProtoType::NR_BadNoexcept || - NewNR == FunctionProtoType::NR_BadNoexcept) + // Any non-throwing specifications are compatible. + if (OldCanThrow == CT_Cannot && NewCanThrow == CT_Cannot) 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) { - S.Diag(NewLoc, DiagID); - if (NoteID.getDiagID() != 0 && OldLoc.isValid()) - S.Diag(OldLoc, NoteID); - return true; + // Any throws-anything specifications are usually compatible. + if (OldCanThrow == CT_Can && OldEST != EST_Dynamic && + NewCanThrow == CT_Can && NewEST != EST_Dynamic) { + // The exception is that the absence of an exception specification only + // matches noexcept(false) for functions, as described above. + if (!AllowNoexceptAllMatchWithNoSpec && + ((OldEST == EST_None && NewEST == EST_NoexceptFalse) || + (OldEST == EST_NoexceptFalse && NewEST == EST_None))) { + // This is the disallowed case. + } else { + return false; + } } - // The MS extension throw(...) is compatible with itself. - if (OldEST == EST_MSAny && NewEST == EST_MSAny) + // FIXME: We treat dependent noexcept specifications as compatible even if + // their expressions are not equivalent. + if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept) 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; + // Dynamic exception specifications with the same set of adjusted types + // are compatible. + if (OldEST == EST_Dynamic && NewEST == EST_Dynamic) { + bool Success = true; + // Both have a dynamic exception spec. Collect the first set, then compare + // to the second. + llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes; + for (const auto &I : Old->exceptions()) + OldTypes.insert(S.Context.getCanonicalType(I).getUnqualifiedType()); + + for (const auto &I : New->exceptions()) { + CanQualType TypePtr = S.Context.getCanonicalType(I).getUnqualifiedType(); + if (OldTypes.count(TypePtr)) + NewTypes.insert(TypePtr); + else { + Success = false; + break; + } + } - // 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) + if (Success && OldTypes.size() == NewTypes.size()) 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; - // As a special compatibility feature, under C++0x we accept no spec and // throw(std::bad_alloc) as equivalent for operator new and operator new[]. // This is because the implicit declaration changed, but old code would break. @@ -560,54 +584,24 @@ static bool CheckEquivalentExceptionSpecImpl( } } - // 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 && 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; - } - - return true; + // If the caller wants to handle the case that the new function is + // incompatible due to a missing exception specification, let it. + if (MissingExceptionSpecification && OldEST != EST_None && + NewEST == EST_None) { + // The old type has an exception specification of some sort, but + // the new type does not. + *MissingExceptionSpecification = true; + + if (MissingEmptyExceptionSpecification && OldCanThrow == CT_Cannot) { + // 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; } - S.Diag(NewLoc, DiagID); - if (NoteID.getDiagID() != 0 && OldLoc.isValid()) - S.Diag(OldLoc, NoteID); return true; } - assert(OldEST == EST_Dynamic && NewEST == EST_Dynamic && - "Exception compatibility logic error: non-dynamic spec slipped through."); - - bool Success = true; - // Both have a dynamic exception spec. Collect the first set, then compare - // to the second. - llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes; - for (const auto &I : Old->exceptions()) - OldTypes.insert(S.Context.getCanonicalType(I).getUnqualifiedType()); - - for (const auto &I : New->exceptions()) { - CanQualType TypePtr = S.Context.getCanonicalType(I).getUnqualifiedType(); - if (OldTypes.count(TypePtr)) - NewTypes.insert(TypePtr); - else - Success = false; - } - - Success = Success && OldTypes.size() == NewTypes.size(); - - if (Success) { - return false; - } S.Diag(NewLoc, DiagID); if (NoteID.getDiagID() != 0 && OldLoc.isValid()) S.Diag(OldLoc, NoteID); @@ -740,62 +734,32 @@ bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID, return false; ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType(); - - // If superset contains everything, we're done. - if (SuperEST == EST_None || SuperEST == EST_MSAny) - return CheckParamExceptionSpec(NestedDiagID, 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(Context); - 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(NestedDiagID, NoteID, Superset, SuperLoc, - Subset, SubLoc); - ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); - assert(!isUnresolvedExceptionSpec(SuperEST) && !isUnresolvedExceptionSpec(SubEST) && "Shouldn't see unknown exception specifications here"); - // It does not. If the subset contains everything, we've failed. - if (SubEST == EST_None || SubEST == EST_MSAny) { - Diag(SubLoc, DiagID); - if (NoteID.getDiagID() != 0) - Diag(SuperLoc, NoteID); - return true; - } - - FunctionProtoType::NoexceptResult SubNR = Subset->getNoexceptSpec(Context); - if (SubNR == FunctionProtoType::NR_BadNoexcept || - SubNR == FunctionProtoType::NR_Dependent) + // 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. + if (SuperEST == EST_DependentNoexcept || SubEST == EST_DependentNoexcept) 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; - } + CanThrowResult SuperCanThrow = Superset->canThrow(); + CanThrowResult SubCanThrow = Subset->canThrow(); - // If the subset contains nothing, we're done. - if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow) + // If the superset contains everything or the subset contains nothing, we're + // done. + if ((SuperCanThrow == CT_Can && SuperEST != EST_Dynamic) || + SubCanThrow == CT_Cannot) return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, Subset, SubLoc); - // Otherwise, if the superset contains nothing, we've failed. - if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) { + // If the subset contains everything or the superset contains nothing, we've + // failed. + if ((SubCanThrow == CT_Can && SubEST != EST_Dynamic) || + SuperCanThrow == CT_Cannot) { Diag(SubLoc, DiagID); if (NoteID.getDiagID() != 0) Diag(SuperLoc, NoteID); @@ -1026,7 +990,7 @@ static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) { if (!FT) return CT_Can; - return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can; + return FT->canThrow(); } static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) { |