forked from OSchip/llvm-project
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
This commit is contained in:
parent
58fce7e54b
commit
eaf11ad709
|
@ -3446,7 +3446,7 @@ public:
|
|||
/// Explicitly-specified list of exception types.
|
||||
ArrayRef<QualType> Exceptions;
|
||||
|
||||
/// Noexcept expression, if this is EST_ComputedNoexcept.
|
||||
/// Noexcept expression, if this is a computed noexcept specification.
|
||||
Expr *NoexceptExpr = nullptr;
|
||||
|
||||
/// The function whose exception specification this is, for
|
||||
|
@ -3549,20 +3549,35 @@ private:
|
|||
return reinterpret_cast<const ExtParameterInfo *>(ptr);
|
||||
}
|
||||
|
||||
size_t getExceptionSpecSize() const {
|
||||
switch (getExceptionSpecType()) {
|
||||
case EST_None: return 0;
|
||||
case EST_DynamicNone: return 0;
|
||||
case EST_MSAny: return 0;
|
||||
case EST_BasicNoexcept: return 0;
|
||||
case EST_Unparsed: return 0;
|
||||
case EST_Dynamic: return getNumExceptions() * sizeof(QualType);
|
||||
case EST_ComputedNoexcept: return sizeof(Expr*);
|
||||
case EST_Uninstantiated: return 2 * sizeof(FunctionDecl*);
|
||||
case EST_Unevaluated: return sizeof(FunctionDecl*);
|
||||
static size_t getExceptionSpecSize(ExceptionSpecificationType EST,
|
||||
unsigned NumExceptions) {
|
||||
switch (EST) {
|
||||
case EST_None:
|
||||
case EST_DynamicNone:
|
||||
case EST_MSAny:
|
||||
case EST_BasicNoexcept:
|
||||
case EST_Unparsed:
|
||||
return 0;
|
||||
|
||||
case EST_Dynamic:
|
||||
return NumExceptions * sizeof(QualType);
|
||||
|
||||
case EST_DependentNoexcept:
|
||||
case EST_NoexceptFalse:
|
||||
case EST_NoexceptTrue:
|
||||
return sizeof(Expr *);
|
||||
|
||||
case EST_Uninstantiated:
|
||||
return 2 * sizeof(FunctionDecl *);
|
||||
|
||||
case EST_Unevaluated:
|
||||
return sizeof(FunctionDecl *);
|
||||
}
|
||||
llvm_unreachable("bad exception specification kind");
|
||||
}
|
||||
size_t getExceptionSpecSize() const {
|
||||
return getExceptionSpecSize(getExceptionSpecType(), getNumExceptions());
|
||||
}
|
||||
|
||||
public:
|
||||
unsigned getNumParams() const { return NumParams; }
|
||||
|
@ -3586,7 +3601,7 @@ public:
|
|||
EPI.RefQualifier = getRefQualifier();
|
||||
if (EPI.ExceptionSpec.Type == EST_Dynamic) {
|
||||
EPI.ExceptionSpec.Exceptions = exceptions();
|
||||
} else if (EPI.ExceptionSpec.Type == EST_ComputedNoexcept) {
|
||||
} else if (isComputedNoexcept(EPI.ExceptionSpec.Type)) {
|
||||
EPI.ExceptionSpec.NoexceptExpr = getNoexceptExpr();
|
||||
} else if (EPI.ExceptionSpec.Type == EST_Uninstantiated) {
|
||||
EPI.ExceptionSpec.SourceDecl = getExceptionSpecDecl();
|
||||
|
@ -3626,33 +3641,13 @@ public:
|
|||
/// spec.
|
||||
bool hasInstantiationDependentExceptionSpec() const;
|
||||
|
||||
/// Result type of getNoexceptSpec().
|
||||
enum NoexceptResult {
|
||||
/// There is no noexcept specifier.
|
||||
NR_NoNoexcept,
|
||||
|
||||
/// The noexcept specifier has a bad expression.
|
||||
NR_BadNoexcept,
|
||||
|
||||
/// The noexcept specifier is dependent.
|
||||
NR_Dependent,
|
||||
|
||||
/// The noexcept specifier evaluates to false.
|
||||
NR_Throw,
|
||||
|
||||
/// The noexcept specifier evaluates to true.
|
||||
NR_Nothrow
|
||||
};
|
||||
|
||||
/// Get the meaning of the noexcept spec on this function, if any.
|
||||
NoexceptResult getNoexceptSpec(const ASTContext &Ctx) const;
|
||||
unsigned getNumExceptions() const { return NumExceptions; }
|
||||
QualType getExceptionType(unsigned i) const {
|
||||
assert(i < NumExceptions && "Invalid exception number!");
|
||||
return exception_begin()[i];
|
||||
}
|
||||
Expr *getNoexceptExpr() const {
|
||||
if (getExceptionSpecType() != EST_ComputedNoexcept)
|
||||
if (!isComputedNoexcept(getExceptionSpecType()))
|
||||
return nullptr;
|
||||
// NoexceptExpr sits where the arguments end.
|
||||
return *reinterpret_cast<Expr *const *>(param_type_end());
|
||||
|
@ -3681,14 +3676,14 @@ public:
|
|||
|
||||
/// Determine whether this function type has a non-throwing exception
|
||||
/// specification.
|
||||
CanThrowResult canThrow(const ASTContext &Ctx) const;
|
||||
CanThrowResult canThrow() const;
|
||||
|
||||
/// Determine whether this function type has a non-throwing exception
|
||||
/// specification. If this depends on template arguments, returns
|
||||
/// \c ResultIfDependent.
|
||||
bool isNothrow(const ASTContext &Ctx, bool ResultIfDependent = false) const {
|
||||
return ResultIfDependent ? canThrow(Ctx) != CT_Can
|
||||
: canThrow(Ctx) == CT_Cannot;
|
||||
bool isNothrow(bool ResultIfDependent = false) const {
|
||||
return ResultIfDependent ? canThrow() != CT_Can
|
||||
: canThrow() == CT_Cannot;
|
||||
}
|
||||
|
||||
bool isVariadic() const { return Variadic; }
|
||||
|
|
|
@ -3760,7 +3760,7 @@ AST_POLYMORPHIC_MATCHER(isNoThrow,
|
|||
if (isUnresolvedExceptionSpec(FnTy->getExceptionSpecType()))
|
||||
return true;
|
||||
|
||||
return FnTy->isNothrow(Finder->getASTContext());
|
||||
return FnTy->isNothrow();
|
||||
}
|
||||
|
||||
/// \brief Matches constexpr variable and function declarations.
|
||||
|
|
|
@ -24,7 +24,9 @@ enum ExceptionSpecificationType {
|
|||
EST_Dynamic, ///< throw(T1, T2)
|
||||
EST_MSAny, ///< Microsoft throw(...) extension
|
||||
EST_BasicNoexcept, ///< noexcept
|
||||
EST_ComputedNoexcept, ///< noexcept(expression)
|
||||
EST_DependentNoexcept,///< noexcept(expression), value-dependent
|
||||
EST_NoexceptFalse, ///< noexcept(expression), evals to 'false'
|
||||
EST_NoexceptTrue, ///< noexcept(expression), evals to 'true'
|
||||
EST_Unevaluated, ///< not evaluated yet, for special member function
|
||||
EST_Uninstantiated, ///< not instantiated yet
|
||||
EST_Unparsed ///< not parsed yet
|
||||
|
@ -34,8 +36,13 @@ inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) {
|
|||
return ESpecType >= EST_DynamicNone && ESpecType <= EST_MSAny;
|
||||
}
|
||||
|
||||
inline bool isComputedNoexcept(ExceptionSpecificationType ESpecType) {
|
||||
return ESpecType >= EST_DependentNoexcept &&
|
||||
ESpecType <= EST_NoexceptTrue;
|
||||
}
|
||||
|
||||
inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
|
||||
return ESpecType == EST_BasicNoexcept || ESpecType == EST_ComputedNoexcept;
|
||||
return ESpecType == EST_BasicNoexcept || isComputedNoexcept(ESpecType);
|
||||
}
|
||||
|
||||
inline bool isUnresolvedExceptionSpec(ExceptionSpecificationType ESpecType) {
|
||||
|
|
|
@ -4716,7 +4716,7 @@ public:
|
|||
|
||||
/// \brief Get the computed exception specification type.
|
||||
ExceptionSpecificationType getExceptionSpecType() const {
|
||||
assert(ComputedEST != EST_ComputedNoexcept &&
|
||||
assert(!isComputedNoexcept(ComputedEST) &&
|
||||
"noexcept(expr) should not be a possible result");
|
||||
return ComputedEST;
|
||||
}
|
||||
|
@ -4744,7 +4744,7 @@ public:
|
|||
/// C++11 [except.spec]p14:
|
||||
/// The exception-specification is noexcept(false) if the set of
|
||||
/// potential exceptions of the special member function contains "any"
|
||||
ESI.Type = EST_ComputedNoexcept;
|
||||
ESI.Type = EST_NoexceptFalse;
|
||||
ESI.NoexceptExpr = Self->ActOnCXXBoolLiteral(SourceLocation(),
|
||||
tok::kw_false).get();
|
||||
}
|
||||
|
@ -4795,6 +4795,11 @@ public:
|
|||
/// special member function.
|
||||
void EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD);
|
||||
|
||||
/// Check the given noexcept-specifier, convert its expression, and compute
|
||||
/// the appropriate ExceptionSpecificationType.
|
||||
ExprResult ActOnNoexceptSpec(SourceLocation NoexceptLoc, Expr *NoexceptExpr,
|
||||
ExceptionSpecificationType &EST);
|
||||
|
||||
/// \brief Check the given exception-specification and update the
|
||||
/// exception specification information with the results.
|
||||
void checkExceptionSpecification(bool IsTopLevel,
|
||||
|
|
|
@ -3420,6 +3420,11 @@ static bool isCanonicalExceptionSpecification(
|
|||
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 @@ static bool isCanonicalExceptionSpecification(
|
|||
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 @@ QualType ASTContext::getFunctionTypeInternal(
|
|||
// 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 @@ QualType ASTContext::getFunctionTypeInternal(
|
|||
// 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 @@ QualType ASTContext::getFunctionTypeInternal(
|
|||
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 @@ QualType ASTContext::getFunctionTypeInternal(
|
|||
// 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
|
||||
|
|
|
@ -441,7 +441,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||
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;
|
||||
|
|
|
@ -672,7 +672,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
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,
|
||||
|
|
|
@ -169,8 +169,8 @@ void CXXNewExpr::AllocateArgsArray(const ASTContext &C, bool isArray,
|
|||
}
|
||||
|
||||
bool CXXNewExpr::shouldNullCheckAllocation(const ASTContext &Ctx) const {
|
||||
return getOperatorNew()->getType()->castAs<FunctionProtoType>()->isNothrow(
|
||||
Ctx) &&
|
||||
return getOperatorNew()->getType()->castAs<FunctionProtoType>()
|
||||
->isNothrow() &&
|
||||
!getOperatorNew()->isReservedGlobalPlacementOperator();
|
||||
}
|
||||
|
||||
|
|
|
@ -2702,7 +2702,7 @@ void CXXNameMangler::mangleType(const FunctionProtoType *T) {
|
|||
// 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 @@ void CXXNameMangler::mangleType(const FunctionProtoType *T) {
|
|||
mangleType(ExceptTy);
|
||||
Out << "E";
|
||||
}
|
||||
} else if (T->isNothrow(getASTContext())) {
|
||||
} else if (T->isNothrow()) {
|
||||
Out << "Do";
|
||||
}
|
||||
|
||||
|
|
|
@ -2800,19 +2800,21 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
|
|||
|
||||
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 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
|
|||
// 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 @@ bool FunctionProtoType::hasInstantiationDependentExceptionSpec() const {
|
|||
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 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
|
|||
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) {
|
||||
|
|
|
@ -672,7 +672,9 @@ FunctionProtoType::printExceptionSpecification(raw_ostream &OS,
|
|||
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);
|
||||
|
|
|
@ -2350,7 +2350,7 @@ static bool CanThrow(Expr *E, ASTContext &Ctx) {
|
|||
if (FT) {
|
||||
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
|
||||
if (!isUnresolvedExceptionSpec(Proto->getExceptionSpecType()) &&
|
||||
Proto->isNothrow(Ctx))
|
||||
Proto->isNothrow())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -1679,7 +1679,7 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
|
|||
return;
|
||||
|
||||
if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType()) &&
|
||||
FPT->isNothrow(Ctx))
|
||||
FPT->isNothrow())
|
||||
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
|
||||
}
|
||||
|
||||
|
|
|
@ -442,11 +442,9 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
|
|||
return;
|
||||
|
||||
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
|
||||
if (isNoexceptExceptionSpec(EST)) {
|
||||
if (Proto->getNoexceptSpec(getContext()) == FunctionProtoType::NR_Nothrow) {
|
||||
// noexcept functions are simple terminate scopes.
|
||||
EHStack.pushTerminate();
|
||||
}
|
||||
if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) {
|
||||
// noexcept functions are simple terminate scopes.
|
||||
EHStack.pushTerminate();
|
||||
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
|
||||
// TODO: Revisit exception specifications for the MS ABI. There is a way to
|
||||
// encode these in an object file but MSVC doesn't do anything with it.
|
||||
|
@ -521,10 +519,8 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
|
|||
return;
|
||||
|
||||
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
|
||||
if (isNoexceptExceptionSpec(EST)) {
|
||||
if (Proto->getNoexceptSpec(getContext()) == FunctionProtoType::NR_Nothrow) {
|
||||
EHStack.popTerminate();
|
||||
}
|
||||
if (isNoexceptExceptionSpec(EST) && Proto->canThrow() == CT_Cannot) {
|
||||
EHStack.popTerminate();
|
||||
} else if (EST == EST_Dynamic || EST == EST_DynamicNone) {
|
||||
// TODO: Revisit exception specifications for the MS ABI. There is a way to
|
||||
// encode these in an object file but MSVC doesn't do anything with it.
|
||||
|
|
|
@ -3476,7 +3476,7 @@ static unsigned extractPBaseFlags(ASTContext &Ctx, QualType &Type) {
|
|||
Flags |= ItaniumRTTIBuilder::PTI_Incomplete;
|
||||
|
||||
if (auto *Proto = Type->getAs<FunctionProtoType>()) {
|
||||
if (Proto->isNothrow(Ctx)) {
|
||||
if (Proto->isNothrow()) {
|
||||
Flags |= ItaniumRTTIBuilder::PTI_Noexcept;
|
||||
Type = Ctx.getFunctionTypeWithExceptionSpec(Type, EST_None);
|
||||
}
|
||||
|
|
|
@ -3586,15 +3586,11 @@ Parser::tryParseExceptionSpecification(bool Delayed,
|
|||
// There is an argument.
|
||||
BalancedDelimiterTracker T(*this, tok::l_paren);
|
||||
T.consumeOpen();
|
||||
NoexceptType = EST_ComputedNoexcept;
|
||||
NoexceptExpr = ParseConstantExpression();
|
||||
T.consumeClose();
|
||||
// The argument must be contextually convertible to bool. We use
|
||||
// CheckBooleanCondition for this purpose.
|
||||
// FIXME: Add a proper Sema entry point for this.
|
||||
if (!NoexceptExpr.isInvalid()) {
|
||||
NoexceptExpr =
|
||||
Actions.CheckBooleanCondition(KeywordLoc, NoexceptExpr.get());
|
||||
NoexceptExpr = Actions.ActOnNoexceptSpec(KeywordLoc, NoexceptExpr.get(),
|
||||
NoexceptType);
|
||||
NoexceptRange = SourceRange(KeywordLoc, T.getCloseLocation());
|
||||
} else {
|
||||
NoexceptType = EST_BasicNoexcept;
|
||||
|
|
|
@ -351,7 +351,7 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD,
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -251,7 +251,9 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto,
|
|||
}
|
||||
break;
|
||||
|
||||
case EST_ComputedNoexcept:
|
||||
case EST_DependentNoexcept:
|
||||
case EST_NoexceptFalse:
|
||||
case EST_NoexceptTrue:
|
||||
I.Fun.NoexceptExpr = NoexceptExpr;
|
||||
break;
|
||||
|
||||
|
|
|
@ -8322,7 +8322,7 @@ Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
|
|||
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;
|
||||
|
|
|
@ -1160,7 +1160,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
|
||||
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;
|
||||
|
|
|
@ -9848,7 +9848,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
|
|||
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;
|
||||
};
|
||||
|
|
|
@ -170,43 +170,40 @@ Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,
|
|||
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 @@ bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) {
|
|||
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 @@ void Sema::checkExceptionSpecification(
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
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 @@ 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 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 @@ 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);
|
||||
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 @@ 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) {
|
||||
|
|
|
@ -4381,7 +4381,7 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
|
|||
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 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
|
|||
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 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
|
|||
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 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
|
|||
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 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
|
|||
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 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
|
|||
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: {
|
||||
|
|
|
@ -1481,7 +1481,7 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
|
|||
// 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 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
|
|||
// 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;
|
||||
}
|
||||
|
|
|
@ -1607,7 +1607,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
|
|||
"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;
|
||||
|
|
|
@ -882,7 +882,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
|
|||
->containsUnexpandedParameterPack())
|
||||
return true;
|
||||
}
|
||||
} else if (Chunk.Fun.getExceptionSpecType() == EST_ComputedNoexcept &&
|
||||
} else if (isComputedNoexcept(Chunk.Fun.getExceptionSpecType()) &&
|
||||
Chunk.Fun.NoexceptExpr->containsUnexpandedParameterPack())
|
||||
return true;
|
||||
|
||||
|
|
|
@ -4677,7 +4677,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5264,30 +5264,23 @@ bool TreeTransform<Derived>::TransformExceptionSpec(
|
|||
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)
|
||||
|
|
|
@ -6392,7 +6392,7 @@ void ASTReader::readExceptionSpec(ModuleFile &ModuleFile,
|
|||
for (unsigned I = 0, N = Record[Idx++]; I != N; ++I)
|
||||
Exceptions.push_back(readType(ModuleFile, Record, Idx));
|
||||
ESI.Exceptions = Exceptions;
|
||||
} else if (EST == EST_ComputedNoexcept) {
|
||||
} else if (isComputedNoexcept(EST)) {
|
||||
ESI.NoexceptExpr = ReadExpr(ModuleFile);
|
||||
} else if (EST == EST_Uninstantiated) {
|
||||
ESI.SourceDecl = ReadDeclAs<FunctionDecl>(ModuleFile, Record, Idx);
|
||||
|
|
|
@ -294,7 +294,7 @@ static void addExceptionSpec(const FunctionProtoType *T,
|
|||
Record.push_back(T->getNumExceptions());
|
||||
for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I)
|
||||
Record.AddTypeRef(T->getExceptionType(I));
|
||||
} else if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||
} else if (isComputedNoexcept(T->getExceptionSpecType())) {
|
||||
Record.AddStmt(T->getNoexceptExpr());
|
||||
} else if (T->getExceptionSpecType() == EST_Uninstantiated) {
|
||||
Record.AddDeclRef(T->getExceptionSpecDecl());
|
||||
|
|
|
@ -549,7 +549,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
|
|||
if (const FunctionDecl *FD = CNE->getOperatorNew()) {
|
||||
QualType Ty = FD->getType();
|
||||
if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
|
||||
if (!ProtoType->isNothrow(getContext()))
|
||||
if (!ProtoType->isNothrow())
|
||||
State = State->assume(RetVal.castAs<DefinedOrUnknownSVal>(), true);
|
||||
}
|
||||
|
||||
|
@ -622,7 +622,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
|
|||
if (FD) {
|
||||
QualType Ty = FD->getType();
|
||||
if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
|
||||
if (!ProtoType->isNothrow(getContext()))
|
||||
if (!ProtoType->isNothrow())
|
||||
if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
|
||||
State = State->assume(*dSymVal, true);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fno-modules-error-recovery -fno-spell-checking -verify -std=c++17 %s
|
||||
|
||||
#pragma clang module build a
|
||||
module a {}
|
||||
#pragma clang module contents
|
||||
#pragma clang module begin a
|
||||
constexpr bool return_true() { return true; }
|
||||
struct X {
|
||||
static void f() noexcept(return_true());
|
||||
};
|
||||
#pragma clang module end
|
||||
#pragma clang module endbuild
|
||||
|
||||
#pragma clang module build b
|
||||
module b {}
|
||||
#pragma clang module contents
|
||||
#pragma clang module begin b
|
||||
#pragma clang module import a
|
||||
using T = decltype(return_true() && noexcept(X::f()));
|
||||
#pragma clang module end
|
||||
#pragma clang module endbuild
|
||||
|
||||
#pragma clang module import a
|
||||
#pragma clang module import b
|
||||
|
||||
// Trigger import of return_true and then X::f in the same pass. This causes
|
||||
// the type of X::f to be loaded while we have a pending body for return_true,
|
||||
// which means evaluation of its exception specification at that point would
|
||||
// fail.
|
||||
T t;
|
||||
|
||||
static_assert(noexcept(X().f()));
|
||||
|
||||
// expected-no-diagnostics
|
Loading…
Reference in New Issue