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/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index d510714..bf79e07 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3420,6 +3420,11 @@
if (ESI.Type == EST_BasicNoexcept)
return true;
+ // A noexcept(expr) specification is (possibly) canonical if expr is
+ // value-dependent.
+ if (ESI.Type == EST_DependentNoexcept)
+ return true;
+
// A dynamic exception specification is canonical if it only contains pack
// expansions (so we can't tell whether it's non-throwing) and all its
// contained types are canonical.
@@ -3434,11 +3439,6 @@
return AnyPackExpansions;
}
- // A noexcept(expr) specification is (possibly) canonical if expr is
- // value-dependent.
- if (ESI.Type == EST_ComputedNoexcept)
- return ESI.NoexceptExpr && ESI.NoexceptExpr->isValueDependent();
-
return false;
}
@@ -3466,7 +3466,7 @@
// noexcept expression, or we're just looking for a canonical type.
// Otherwise, we're going to need to create a type
// sugar node to hold the concrete expression.
- if (OnlyWantCanonical || EPI.ExceptionSpec.Type != EST_ComputedNoexcept ||
+ if (OnlyWantCanonical || !isComputedNoexcept(EPI.ExceptionSpec.Type) ||
EPI.ExceptionSpec.NoexceptExpr == FPT->getNoexceptExpr())
return Existing;
@@ -3513,7 +3513,7 @@
// We don't know yet. It shouldn't matter what we pick here; no-one
// should ever look at this.
LLVM_FALLTHROUGH;
- case EST_None: case EST_MSAny:
+ case EST_None: case EST_MSAny: case EST_NoexceptFalse:
CanonicalEPI.ExceptionSpec.Type = EST_None;
break;
@@ -3535,24 +3535,12 @@
break;
}
- case EST_DynamicNone: case EST_BasicNoexcept:
+ case EST_DynamicNone: case EST_BasicNoexcept: case EST_NoexceptTrue:
CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
break;
- case EST_ComputedNoexcept:
- llvm::APSInt Value(1);
- auto *E = CanonicalEPI.ExceptionSpec.NoexceptExpr;
- if (!E || !E->isIntegerConstantExpr(Value, *this, nullptr,
- /*IsEvaluated*/false)) {
- // This noexcept specification is invalid.
- // FIXME: Should this be able to happen?
- CanonicalEPI.ExceptionSpec.Type = EST_None;
- break;
- }
-
- CanonicalEPI.ExceptionSpec.Type =
- Value.getBoolValue() ? EST_BasicNoexcept : EST_None;
- break;
+ case EST_DependentNoexcept:
+ llvm_unreachable("dependent noexcept is already canonical");
}
} else {
CanonicalEPI.ExceptionSpec = FunctionProtoType::ExceptionSpecInfo();
@@ -3577,18 +3565,10 @@
// Instead of the exception types, there could be a noexcept
// expression, or information used to resolve the exception
// specification.
- size_t Size = sizeof(FunctionProtoType) +
- NumArgs * sizeof(QualType);
-
- if (EPI.ExceptionSpec.Type == EST_Dynamic) {
- Size += EPI.ExceptionSpec.Exceptions.size() * sizeof(QualType);
- } else if (EPI.ExceptionSpec.Type == EST_ComputedNoexcept) {
- Size += sizeof(Expr*);
- } else if (EPI.ExceptionSpec.Type == EST_Uninstantiated) {
- Size += 2 * sizeof(FunctionDecl*);
- } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) {
- Size += sizeof(FunctionDecl*);
- }
+ size_t Size =
+ sizeof(FunctionProtoType) + NumArgs * sizeof(QualType) +
+ FunctionProtoType::getExceptionSpecSize(
+ EPI.ExceptionSpec.Type, EPI.ExceptionSpec.Exceptions.size());
// Put the ExtParameterInfos last. If all were equal, it would make
// more sense to put these before the exception specification, because
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 05f414e..b543991 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -441,7 +441,7 @@
Proto2->getExceptionType(I)))
return false;
}
- } else if (Proto1->getExceptionSpecType() == EST_ComputedNoexcept) {
+ } else if (isComputedNoexcept(Proto1->getExceptionSpecType())) {
if (!IsStructurallyEquivalent(Context, Proto1->getNoexceptExpr(),
Proto2->getNoexceptExpr()))
return false;
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index b2c5303..c6c9d85 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -672,7 +672,7 @@
Proto += ")";
} else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) {
Proto += " noexcept";
- if (FT->getExceptionSpecType() == EST_ComputedNoexcept) {
+ if (isComputedNoexcept(FT->getExceptionSpecType())) {
Proto += "(";
llvm::raw_string_ostream EOut(Proto);
FT->getNoexceptExpr()->printPretty(EOut, nullptr, SubPolicy,
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index eb2e8c5..3a204c2 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -169,8 +169,8 @@
}
bool CXXNewExpr::shouldNullCheckAllocation(const ASTContext &Ctx) const {
- return getOperatorNew()->getType()->castAs<FunctionProtoType>()->isNothrow(
- Ctx) &&
+ return getOperatorNew()->getType()->castAs<FunctionProtoType>()
+ ->isNothrow() &&
!getOperatorNew()->isReservedGlobalPlacementOperator();
}
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 610400d..1a656b57 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2702,7 +2702,7 @@
// Mangle instantiation-dependent exception-specification, if present,
// per cxx-abi-dev proposal on 2016-10-11.
if (T->hasInstantiationDependentExceptionSpec()) {
- if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
+ if (isComputedNoexcept(T->getExceptionSpecType())) {
Out << "DO";
mangleExpression(T->getNoexceptExpr());
Out << "E";
@@ -2713,7 +2713,7 @@
mangleType(ExceptTy);
Out << "E";
}
- } else if (T->isNothrow(getASTContext())) {
+ } else if (T->isNothrow()) {
Out << "Do";
}
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 571dc2a..93c3f63 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2800,19 +2800,21 @@
exnSlot[I++] = ExceptionType;
}
- } else if (getExceptionSpecType() == EST_ComputedNoexcept) {
+ } else if (isComputedNoexcept(getExceptionSpecType())) {
+ assert(epi.ExceptionSpec.NoexceptExpr && "computed noexcept with no expr");
+ assert((getExceptionSpecType() == EST_DependentNoexcept) ==
+ epi.ExceptionSpec.NoexceptExpr->isValueDependent());
+
// Store the noexcept expression and context.
auto **noexSlot = reinterpret_cast<Expr **>(argSlot + NumParams);
*noexSlot = epi.ExceptionSpec.NoexceptExpr;
- if (epi.ExceptionSpec.NoexceptExpr) {
- if (epi.ExceptionSpec.NoexceptExpr->isValueDependent() ||
- epi.ExceptionSpec.NoexceptExpr->isInstantiationDependent())
- setInstantiationDependent();
+ if (epi.ExceptionSpec.NoexceptExpr->isValueDependent() ||
+ epi.ExceptionSpec.NoexceptExpr->isInstantiationDependent())
+ setInstantiationDependent();
- if (epi.ExceptionSpec.NoexceptExpr->containsUnexpandedParameterPack())
- setContainsUnexpandedParameterPack();
- }
+ if (epi.ExceptionSpec.NoexceptExpr->containsUnexpandedParameterPack())
+ setContainsUnexpandedParameterPack();
} else if (getExceptionSpecType() == EST_Uninstantiated) {
// Store the function decl from which we will resolve our
// exception specification.
@@ -2832,7 +2834,7 @@
// then it's a dependent type. This only happens in C++17 onwards.
if (isCanonicalUnqualified()) {
if (getExceptionSpecType() == EST_Dynamic ||
- getExceptionSpecType() == EST_ComputedNoexcept) {
+ getExceptionSpecType() == EST_DependentNoexcept) {
assert(hasDependentExceptionSpec() && "type should not be canonical");
setDependent();
}
@@ -2870,52 +2872,36 @@
return false;
}
-FunctionProtoType::NoexceptResult
-FunctionProtoType::getNoexceptSpec(const ASTContext &ctx) const {
- ExceptionSpecificationType est = getExceptionSpecType();
- if (est == EST_BasicNoexcept)
- return NR_Nothrow;
+CanThrowResult FunctionProtoType::canThrow() const {
+ switch (getExceptionSpecType()) {
+ case EST_Unparsed:
+ case EST_Unevaluated:
+ case EST_Uninstantiated:
+ llvm_unreachable("should not call this with unresolved exception specs");
- if (est != EST_ComputedNoexcept)
- return NR_NoNoexcept;
-
- Expr *noexceptExpr = getNoexceptExpr();
- if (!noexceptExpr)
- return NR_BadNoexcept;
- if (noexceptExpr->isValueDependent())
- return NR_Dependent;
-
- llvm::APSInt value;
- bool isICE = noexceptExpr->isIntegerConstantExpr(value, ctx, nullptr,
- /*evaluated*/false);
- (void)isICE;
- assert(isICE && "AST should not contain bad noexcept expressions.");
-
- return value.getBoolValue() ? NR_Nothrow : NR_Throw;
-}
-
-CanThrowResult FunctionProtoType::canThrow(const ASTContext &Ctx) const {
- ExceptionSpecificationType EST = getExceptionSpecType();
- assert(EST != EST_Unevaluated && EST != EST_Uninstantiated);
- if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
+ case EST_DynamicNone:
+ case EST_BasicNoexcept:
+ case EST_NoexceptTrue:
return CT_Cannot;
- if (EST == EST_Dynamic) {
+ case EST_None:
+ case EST_MSAny:
+ case EST_NoexceptFalse:
+ return CT_Can;
+
+ case EST_Dynamic:
// A dynamic exception specification is throwing unless every exception
// type is an (unexpanded) pack expansion type.
for (unsigned I = 0, N = NumExceptions; I != N; ++I)
if (!getExceptionType(I)->getAs<PackExpansionType>())
return CT_Can;
return CT_Dependent;
+
+ case EST_DependentNoexcept:
+ return CT_Dependent;
}
- if (EST != EST_ComputedNoexcept)
- return CT_Can;
-
- NoexceptResult NR = getNoexceptSpec(Ctx);
- if (NR == NR_Dependent)
- return CT_Dependent;
- return NR == NR_Nothrow ? CT_Cannot : CT_Can;
+ llvm_unreachable("unexpected exception specification kind");
}
bool FunctionProtoType::isTemplateVariadic() const {
@@ -2965,8 +2951,7 @@
if (epi.ExceptionSpec.Type == EST_Dynamic) {
for (QualType Ex : epi.ExceptionSpec.Exceptions)
ID.AddPointer(Ex.getAsOpaquePtr());
- } else if (epi.ExceptionSpec.Type == EST_ComputedNoexcept &&
- epi.ExceptionSpec.NoexceptExpr) {
+ } else if (isComputedNoexcept(epi.ExceptionSpec.Type)) {
epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, Canonical);
} else if (epi.ExceptionSpec.Type == EST_Uninstantiated ||
epi.ExceptionSpec.Type == EST_Unevaluated) {
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c99148e..c278dc9 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -672,7 +672,9 @@
OS << ')';
} else if (isNoexceptExceptionSpec(getExceptionSpecType())) {
OS << " noexcept";
- if (getExceptionSpecType() == EST_ComputedNoexcept) {
+ // FIXME:Is it useful to print out the expression for a non-dependent
+ // noexcept specification?
+ if (isComputedNoexcept(getExceptionSpecType())) {
OS << '(';
if (getNoexceptExpr())
getNoexceptExpr()->printPretty(OS, nullptr, Policy);