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
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 2903f53..9842397 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -351,7 +351,7 @@
static bool isNoexcept(const FunctionDecl *FD) {
const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
- if (FPT->isNothrow(FD->getASTContext()) || FD->hasAttr<NoThrowAttr>())
+ if (FPT->isNothrow() || FD->hasAttr<NoThrowAttr>())
return true;
return false;
}
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 26d6202..dd2476e 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -251,7 +251,9 @@
}
break;
- case EST_ComputedNoexcept:
+ case EST_DependentNoexcept:
+ case EST_NoexceptFalse:
+ case EST_NoexceptTrue:
I.Fun.NoexceptExpr = NoexceptExpr;
break;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 7b38612..b52ff97 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8322,7 +8322,7 @@
if (Op == OO_New || Op == OO_Array_New) {
const FunctionProtoType *Proto
= FD->getType()->castAs<FunctionProtoType>();
- if (!Proto->isNothrow(Context, /*ResultIfDependent*/true) &&
+ if (!Proto->isNothrow(/*ResultIfDependent*/true) &&
CheckNonNullExpr(*this, RetValExp))
Diag(ReturnLoc, diag::warn_operator_new_returns_null)
<< FD << getLangOpts().CPlusPlus11;
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 0d2d847..36d5523 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1160,7 +1160,7 @@
if (RequiresNoThrowAlloc) {
const auto *FT = OperatorNew->getType()->getAs<FunctionProtoType>();
- if (!FT->isNothrow(S.Context, /*ResultIfDependent*/ false)) {
+ if (!FT->isNothrow(/*ResultIfDependent*/ false)) {
S.Diag(OperatorNew->getLocation(),
diag::err_coroutine_promise_new_requires_nothrow)
<< OperatorNew;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 732853d..62f6583 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9848,7 +9848,7 @@
else if (auto *MPT = T->getAs<MemberPointerType>())
T = MPT->getPointeeType();
if (auto *FPT = T->getAs<FunctionProtoType>())
- if (FPT->isNothrow(Context))
+ if (FPT->isNothrow())
return true;
return false;
};
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 4339c9a..9126f27 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -170,43 +170,40 @@
if (EST == EST_None && Method->hasAttr<NoThrowAttr>())
EST = EST_BasicNoexcept;
- switch(EST) {
+ switch (EST) {
+ case EST_Unparsed:
+ case EST_Uninstantiated:
+ case EST_Unevaluated:
+ llvm_unreachable("should not see unresolved exception specs here");
+
// If this function can throw any exceptions, make a note of that.
case EST_MSAny:
case EST_None:
+ // FIXME: Whichever we see last of MSAny and None determines our result.
+ // We should make a consistent, order-independent choice here.
ClearExceptions();
ComputedEST = EST;
return;
+ case EST_NoexceptFalse:
+ ClearExceptions();
+ ComputedEST = EST_None;
+ return;
// FIXME: If the call to this decl is using any of its default arguments, we
// need to search them for potentially-throwing calls.
// If this function has a basic noexcept, it doesn't affect the outcome.
case EST_BasicNoexcept:
+ case EST_NoexceptTrue:
return;
- // If we're still at noexcept(true) and there's a nothrow() callee,
+ // If we're still at noexcept(true) and there's a throw() callee,
// change to that specification.
case EST_DynamicNone:
if (ComputedEST == EST_BasicNoexcept)
ComputedEST = EST_DynamicNone;
return;
- // Check out noexcept specs.
- case EST_ComputedNoexcept:
- {
- FunctionProtoType::NoexceptResult NR =
- Proto->getNoexceptSpec(Self->Context);
- 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;
- }
- default:
+ case EST_DependentNoexcept:
+ llvm_unreachable(
+ "should not generate implicit declarations for dependent cases");
+ case EST_Dynamic:
break;
}
assert(EST == EST_Dynamic && "EST case not considered earlier.");
@@ -15040,7 +15037,9 @@
case EST_None:
break;
- case EST_ComputedNoexcept:
+ case EST_DependentNoexcept:
+ case EST_NoexceptFalse:
+ case EST_NoexceptTrue:
if (!Finder.TraverseStmt(Proto->getNoexceptExpr()))
return true;
LLVM_FALLTHROUGH;
@@ -15137,31 +15136,17 @@
return;
}
- if (EST == EST_ComputedNoexcept) {
- // If an error occurred, there's no expression here.
- if (NoexceptExpr) {
- assert((NoexceptExpr->isTypeDependent() ||
- NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
- Context.BoolTy) &&
- "Parser should have made sure that the expression is boolean");
- if (IsTopLevel && NoexceptExpr &&
- DiagnoseUnexpandedParameterPack(NoexceptExpr)) {
- ESI.Type = EST_BasicNoexcept;
- return;
- }
-
- if (!NoexceptExpr->isValueDependent()) {
- ExprResult Result = VerifyIntegerConstantExpression(
- NoexceptExpr, nullptr, diag::err_noexcept_needs_constant_expression,
- /*AllowFold*/ false);
- if (Result.isInvalid()) {
- ESI.Type = EST_BasicNoexcept;
- return;
- }
- NoexceptExpr = Result.get();
- }
- ESI.NoexceptExpr = NoexceptExpr;
+ if (isComputedNoexcept(EST)) {
+ assert((NoexceptExpr->isTypeDependent() ||
+ NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
+ Context.BoolTy) &&
+ "Parser should have made sure that the expression is boolean");
+ if (IsTopLevel && DiagnoseUnexpandedParameterPack(NoexceptExpr)) {
+ ESI.Type = EST_BasicNoexcept;
+ return;
}
+
+ ESI.NoexceptExpr = NoexceptExpr;
return;
}
}
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 21ca08f..a81c17b 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -76,6 +76,29 @@
.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 @@
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 @@
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 @@
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 @@
!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;
-
- FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec(S.Context);
- FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec(S.Context);
- 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) {
- S.Diag(NewLoc, DiagID);
- if (NoteID.getDiagID() != 0 && OldLoc.isValid())
- S.Diag(OldLoc, NoteID);
- return true;
- }
-
- // 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;
-
- // 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;
- }
+ CanThrowResult OldCanThrow = Old->canThrow();
+ CanThrowResult NewCanThrow = New->canThrow();
// 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)
+ if (OldCanThrow == CT_Cannot && NewCanThrow == CT_Cannot)
return false;
+ // 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;
+ }
+ }
+
+ // FIXME: We treat dependent noexcept specifications as compatible even if
+ // their expressions are not equivalent.
+ if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept)
+ 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;
+ }
+ }
+
+ if (Success && OldTypes.size() == NewTypes.size())
+ 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 @@
}
}
- // 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 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 && 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 (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 @@
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);
+ ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
+ assert(!isUnresolvedExceptionSpec(SuperEST) &&
+ !isUnresolvedExceptionSpec(SubEST) &&
+ "Shouldn't see unknown exception specifications here");
// 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)
+ if (SuperEST == EST_DependentNoexcept || SubEST == EST_DependentNoexcept)
return false;
- // Another case of the superset containing everything.
- if (SuperNR == FunctionProtoType::NR_Throw)
+ CanThrowResult SuperCanThrow = Superset->canThrow();
+ CanThrowResult SubCanThrow = Subset->canThrow();
+
+ // 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);
- 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)
- 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;
- }
-
- // If the subset contains nothing, we're done.
- if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow)
- 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 @@
if (!FT)
return CT_Can;
- return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can;
+ return FT->canThrow();
}
static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 95ea841..1d56fc8 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4381,7 +4381,7 @@
const FunctionProtoType *CPT =
Operator->getType()->getAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
- if (!CPT || !CPT->isNothrow(C))
+ if (!CPT || !CPT->isNothrow())
return false;
}
}
@@ -4629,7 +4629,7 @@
const FunctionProtoType *CPT =
Destructor->getType()->getAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
- if (!CPT || !CPT->isNothrow(C))
+ if (!CPT || !CPT->isNothrow())
return false;
}
}
@@ -4722,7 +4722,7 @@
return false;
// TODO: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
- if (!CPT->isNothrow(C) || CPT->getNumParams() > 1)
+ if (!CPT->isNothrow() || CPT->getNumParams() > 1)
return false;
}
}
@@ -4761,7 +4761,7 @@
return false;
// FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
- if (!CPT->isNothrow(C) || CPT->getNumParams() > 0)
+ if (!CPT->isNothrow() || CPT->getNumParams() > 0)
return false;
}
}
@@ -5909,27 +5909,23 @@
if (EST2 == EST_None) return ESI2;
if (EST1 == EST_MSAny) return ESI1;
if (EST2 == EST_MSAny) return ESI2;
+ if (EST1 == EST_NoexceptFalse) return ESI1;
+ if (EST2 == EST_NoexceptFalse) return ESI2;
// If either of them is non-throwing, the result is the other.
if (EST1 == EST_DynamicNone) return ESI2;
if (EST2 == EST_DynamicNone) return ESI1;
if (EST1 == EST_BasicNoexcept) return ESI2;
if (EST2 == EST_BasicNoexcept) return ESI1;
+ if (EST1 == EST_NoexceptTrue) return ESI2;
+ if (EST2 == EST_NoexceptTrue) return ESI1;
- // If either of them is a non-value-dependent computed noexcept, that
- // determines the result.
- if (EST2 == EST_ComputedNoexcept && ESI2.NoexceptExpr &&
- !ESI2.NoexceptExpr->isValueDependent())
- return !ESI2.NoexceptExpr->EvaluateKnownConstInt(S.Context) ? ESI2 : ESI1;
- if (EST1 == EST_ComputedNoexcept && ESI1.NoexceptExpr &&
- !ESI1.NoexceptExpr->isValueDependent())
- return !ESI1.NoexceptExpr->EvaluateKnownConstInt(S.Context) ? ESI1 : ESI2;
// If we're left with value-dependent computed noexcept expressions, we're
// stuck. Before C++17, we can just drop the exception specification entirely,
// since it's not actually part of the canonical type. And this should never
// happen in C++17, because it would mean we were computing the composite
// pointer type of dependent types, which should never happen.
- if (EST1 == EST_ComputedNoexcept || EST2 == EST_ComputedNoexcept) {
+ if (EST1 == EST_DependentNoexcept || EST2 == EST_DependentNoexcept) {
assert(!S.getLangOpts().CPlusPlus17 &&
"computing composite pointer type of dependent types");
return FunctionProtoType::ExceptionSpecInfo();
@@ -5942,7 +5938,9 @@
case EST_DynamicNone:
case EST_MSAny:
case EST_BasicNoexcept:
- case EST_ComputedNoexcept:
+ case EST_DependentNoexcept:
+ case EST_NoexceptFalse:
+ case EST_NoexceptTrue:
llvm_unreachable("handled above");
case EST_Dynamic: {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 4b58988..f534589 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1481,7 +1481,7 @@
// Drop 'noexcept' if not present in target type.
if (const auto *FromFPT = dyn_cast<FunctionProtoType>(FromFn)) {
const auto *ToFPT = cast<FunctionProtoType>(ToFn);
- if (FromFPT->isNothrow(Context) && !ToFPT->isNothrow(Context)) {
+ if (FromFPT->isNothrow() && !ToFPT->isNothrow()) {
FromFn = cast<FunctionType>(
Context.getFunctionTypeWithExceptionSpec(QualType(FromFPT, 0),
EST_None)
@@ -2809,9 +2809,9 @@
// Handle exception specification differences on canonical type (in C++17
// onwards).
if (cast<FunctionProtoType>(FromFunction->getCanonicalTypeUnqualified())
- ->isNothrow(Context) !=
+ ->isNothrow() !=
cast<FunctionProtoType>(ToFunction->getCanonicalTypeUnqualified())
- ->isNothrow(Context)) {
+ ->isNothrow()) {
PDiag << ft_noexcept;
return;
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e4a158f..dc3c3a6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1607,7 +1607,7 @@
"saw non-type template parameter with wrong depth");
llvm::APSInt Noexcept(1);
- switch (FunctionProtoArg->canThrow(S.Context)) {
+ switch (FunctionProtoArg->canThrow()) {
case CT_Cannot:
Noexcept = 1;
LLVM_FALLTHROUGH;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 37a9c26..68a6909 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -882,7 +882,7 @@
->containsUnexpandedParameterPack())
return true;
}
- } else if (Chunk.Fun.getExceptionSpecType() == EST_ComputedNoexcept &&
+ } else if (isComputedNoexcept(Chunk.Fun.getExceptionSpecType()) &&
Chunk.Fun.NoexceptExpr->containsUnexpandedParameterPack())
return true;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index a6491e5..47b1659 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4677,7 +4677,7 @@
DynamicExceptions.push_back(FTI.Exceptions[I].Ty);
DynamicExceptionRanges.push_back(FTI.Exceptions[I].Range);
}
- } else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
+ } else if (isComputedNoexcept(FTI.getExceptionSpecType())) {
NoexceptExpr = FTI.NoexceptExpr;
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4de5550..ee38278 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5264,30 +5264,23 @@
assert(ESI.Type != EST_Uninstantiated && ESI.Type != EST_Unevaluated);
// Instantiate a dynamic noexcept expression, if any.
- if (ESI.Type == EST_ComputedNoexcept) {
+ if (isComputedNoexcept(ESI.Type)) {
EnterExpressionEvaluationContext Unevaluated(
getSema(), Sema::ExpressionEvaluationContext::ConstantEvaluated);
ExprResult NoexceptExpr = getDerived().TransformExpr(ESI.NoexceptExpr);
if (NoexceptExpr.isInvalid())
return true;
- // FIXME: This is bogus, a noexcept expression is not a condition.
- NoexceptExpr = getSema().CheckBooleanCondition(Loc, NoexceptExpr.get());
+ ExceptionSpecificationType EST = ESI.Type;
+ NoexceptExpr =
+ getSema().ActOnNoexceptSpec(Loc, NoexceptExpr.get(), EST);
if (NoexceptExpr.isInvalid())
return true;
- if (!NoexceptExpr.get()->isValueDependent()) {
- NoexceptExpr = getSema().VerifyIntegerConstantExpression(
- NoexceptExpr.get(), nullptr,
- diag::err_noexcept_needs_constant_expression,
- /*AllowFold*/false);
- if (NoexceptExpr.isInvalid())
- return true;
- }
-
- if (ESI.NoexceptExpr != NoexceptExpr.get())
+ if (ESI.NoexceptExpr != NoexceptExpr.get() || EST != ESI.Type)
Changed = true;
ESI.NoexceptExpr = NoexceptExpr.get();
+ ESI.Type = EST;
}
if (ESI.Type != EST_Dynamic)