summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaExceptionSpec.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2018-05-03 03:58:32 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2018-05-03 03:58:32 +0000
commiteaf11ad70904679593d3c7c100bcbcc395c91a89 (patch)
tree257b3b16ed8dd6c92f949e57987c29e3741a169b /clang/lib/Sema/SemaExceptionSpec.cpp
parent58fce7e54b50af99023b34a24992fc881ace77b6 (diff)
downloadbcm5719-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.cpp248
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) {
OpenPOWER on IntegriCloud