Propagate the new exception information to FunctionProtoType.
Change the interface to expose the new information and deal with the enormous fallout.
Introduce the new ExceptionSpecificationType value EST_DynamicNone to more easily deal with empty throw specifications.
Update the tests for noexcept and fix the various bugs uncovered, such as lack of tentative parsing support.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@127537 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index fcfd230..c128ce5 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -1909,7 +1909,7 @@
// Unique functions, to guarantee there is only one function of a particular
// structure.
llvm::FoldingSetNodeID ID;
- FunctionProtoType::Profile(ID, ResultTy, ArgArray, NumArgs, EPI);
+ FunctionProtoType::Profile(ID, ResultTy, ArgArray, NumArgs, EPI, this);
void *InsertPos = 0;
if (FunctionProtoType *FTP =
@@ -1953,14 +1953,20 @@
// FunctionProtoType objects are allocated with extra bytes after them
// for two variable size arrays (for parameter and exception types) at the
- // end of them.
+ // end of them. Instead of the exception types, there could be a noexcept
+ // expression and a context pointer.
size_t Size = sizeof(FunctionProtoType) +
- NumArgs * sizeof(QualType) +
- EPI.NumExceptions * sizeof(QualType);
+ NumArgs * sizeof(QualType);
+ if (EPI.ExceptionSpecType == EST_Dynamic)
+ Size += EPI.NumExceptions * sizeof(QualType);
+ else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
+ Size += sizeof(Expr*) + sizeof(ASTContext*);
+ }
FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment);
FunctionProtoType::ExtProtoInfo newEPI = EPI;
newEPI.ExtInfo = EPI.ExtInfo.withCallingConv(CallConv);
- new (FTP) FunctionProtoType(ResultTy, ArgArray, NumArgs, Canonical, newEPI);
+ new (FTP) FunctionProtoType(ResultTy, ArgArray, NumArgs, Canonical, newEPI,
+ this);
Types.push_back(FTP);
FunctionProtoTypes.InsertNode(FTP, InsertPos);
return QualType(FTP, 0);
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index b00c788..895f442 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -521,16 +521,21 @@
}
if (Proto1->isVariadic() != Proto2->isVariadic())
return false;
- if (Proto1->hasExceptionSpec() != Proto2->hasExceptionSpec())
+ if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType())
return false;
- if (Proto1->hasAnyExceptionSpec() != Proto2->hasAnyExceptionSpec())
- return false;
- if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
- return false;
- for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
+ if (Proto1->getExceptionSpecType() == EST_Dynamic) {
+ if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
+ return false;
+ for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
+ if (!IsStructurallyEquivalent(Context,
+ Proto1->getExceptionType(I),
+ Proto2->getExceptionType(I)))
+ return false;
+ }
+ } else if (Proto1->getExceptionSpecType() == EST_ComputedNoexcept) {
if (!IsStructurallyEquivalent(Context,
- Proto1->getExceptionType(I),
- Proto2->getExceptionType(I)))
+ Proto1->getNoexceptExpr(),
+ Proto2->getNoexceptExpr()))
return false;
}
if (Proto1->getTypeQuals() != Proto2->getTypeQuals())
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index c6ae128..2781632 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -412,22 +412,32 @@
if (TypeQuals & Qualifiers::Restrict)
Proto += " restrict";
}
-
- if (FT && FT->hasExceptionSpec()) {
+
+ if (FT && FT->hasDynamicExceptionSpec()) {
Proto += " throw(";
- if (FT->hasAnyExceptionSpec())
+ if (FT->getExceptionSpecType() == EST_MSAny)
Proto += "...";
else
for (unsigned I = 0, N = FT->getNumExceptions(); I != N; ++I) {
if (I)
Proto += ", ";
-
-
+
std::string ExceptionType;
FT->getExceptionType(I).getAsStringInternal(ExceptionType, SubPolicy);
Proto += ExceptionType;
}
Proto += ")";
+ } else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) {
+ Proto += " noexcept";
+ if (FT->getExceptionSpecType() == EST_ComputedNoexcept) {
+ Proto += "(";
+ llvm::raw_string_ostream EOut(Proto);
+ FT->getNoexceptExpr()->printPretty(EOut, Context, 0, SubPolicy,
+ Indentation);
+ EOut.flush();
+ Proto += EOut.str();
+ Proto += ")";
+ }
}
if (D->hasAttr<NoReturnAttr>())
diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp
index 9d828fc..9a34722 100644
--- a/lib/AST/DumpXML.cpp
+++ b/lib/AST/DumpXML.cpp
@@ -975,15 +975,16 @@
dispatch(*I);
pop();
- if (T->hasExceptionSpec()) {
+ if (T->hasDynamicExceptionSpec()) {
push("exception_specifiers");
- setFlag("any", T->hasAnyExceptionSpec());
+ setFlag("any", T->getExceptionSpecType() == EST_MSAny);
completeAttrs();
for (FunctionProtoType::exception_iterator
I = T->exception_begin(), E = T->exception_end(); I != E; ++I)
dispatch(*I);
pop();
}
+ // FIXME: noexcept specifier
}
void visitTemplateSpecializationTypeChildren(TemplateSpecializationType *T) {
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 4793b11..1c7807e 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1659,7 +1659,7 @@
if (!FT)
return Expr::CT_Can;
- return FT->hasEmptyExceptionSpec() ? Expr::CT_Cannot : Expr::CT_Can;
+ return FT->isNothrow() ? Expr::CT_Cannot : Expr::CT_Can;
}
static Expr::CanThrowResult CanDynamicCastThrow(const CXXDynamicCastExpr *DC) {
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index 966c1fe..fd94dd1 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -102,7 +102,7 @@
bool CXXNewExpr::shouldNullCheckAllocation() const {
return getOperatorNew()->getType()->
- castAs<FunctionProtoType>()->hasNonThrowingExceptionSpec();
+ castAs<FunctionProtoType>()->isNothrow();
}
// CXXDeleteExpr
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index eea08f6..eb5254e 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -1165,7 +1166,8 @@
FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
unsigned numArgs, QualType canonical,
- const ExtProtoInfo &epi)
+ const ExtProtoInfo &epi,
+ const ASTContext *context)
: FunctionType(FunctionProto, result, epi.Variadic, epi.TypeQuals,
epi.RefQualifier, canonical,
result->isDependentType(),
@@ -1173,8 +1175,7 @@
result->containsUnexpandedParameterPack(),
epi.ExtInfo),
NumArgs(numArgs), NumExceptions(epi.NumExceptions),
- HasExceptionSpec(isDynamicExceptionSpec(epi.ExceptionSpecType)),
- HasAnyExceptionSpec(epi.ExceptionSpecType == EST_DynamicAny)
+ ExceptionSpecType(epi.ExceptionSpecType)
{
// Fill in the trailing argument array.
QualType *argSlot = reinterpret_cast<QualType*>(this+1);
@@ -1187,20 +1188,54 @@
argSlot[i] = args[i];
}
-
- // Fill in the exception array.
- QualType *exnSlot = argSlot + numArgs;
- for (unsigned i = 0, e = epi.NumExceptions; i != e; ++i) {
- if (epi.Exceptions[i]->isDependentType())
- setDependent();
- if (epi.Exceptions[i]->containsUnexpandedParameterPack())
- setContainsUnexpandedParameterPack();
+ if (getExceptionSpecType() == EST_Dynamic) {
+ // Fill in the exception array.
+ QualType *exnSlot = argSlot + numArgs;
+ for (unsigned i = 0, e = epi.NumExceptions; i != e; ++i) {
+ if (epi.Exceptions[i]->isDependentType())
+ setDependent();
- exnSlot[i] = epi.Exceptions[i];
+ if (epi.Exceptions[i]->containsUnexpandedParameterPack())
+ setContainsUnexpandedParameterPack();
+
+ exnSlot[i] = epi.Exceptions[i];
+ }
+ } else if (getExceptionSpecType() == EST_ComputedNoexcept) {
+ // Store the noexcept expression and context.
+ Expr **noexSlot = reinterpret_cast<Expr**>(argSlot + numArgs);
+ *noexSlot = epi.NoexceptExpr;
+ const ASTContext **contextSlot = const_cast<const ASTContext**>(
+ reinterpret_cast<ASTContext**>(noexSlot + 1));
+ *contextSlot = context;
}
}
+FunctionProtoType::NoexceptResult
+FunctionProtoType::getNoexceptSpec() const {
+ ExceptionSpecificationType est = getExceptionSpecType();
+ if (est == EST_BasicNoexcept)
+ return NR_Nothrow;
+
+ if (est != EST_ComputedNoexcept)
+ return NR_NoNoexcept;
+
+ Expr *noexceptExpr = getNoexceptExpr();
+ if (!noexceptExpr)
+ return NR_BadNoexcept;
+ if (noexceptExpr->isValueDependent())
+ return NR_Dependent;
+
+ llvm::APSInt value;
+ ASTContext& ctx = const_cast<ASTContext&>(*getContext());
+ bool isICE = noexceptExpr->isIntegerConstantExpr(value, ctx, 0,
+ /*evaluated*/false);
+ (void)isICE;
+ assert(isICE && "AST should not contain bad noexcept expressions.");
+
+ return value.getBoolValue() ? NR_Nothrow : NR_Throw;
+}
+
bool FunctionProtoType::isTemplateVariadic() const {
for (unsigned ArgIdx = getNumArgs(); ArgIdx; --ArgIdx)
if (isa<PackExpansionType>(getArgType(ArgIdx - 1)))
@@ -1211,23 +1246,27 @@
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
const QualType *ArgTys, unsigned NumArgs,
- const ExtProtoInfo &epi) {
+ const ExtProtoInfo &epi,
+ const ASTContext *Context) {
ID.AddPointer(Result.getAsOpaquePtr());
for (unsigned i = 0; i != NumArgs; ++i)
ID.AddPointer(ArgTys[i].getAsOpaquePtr());
ID.AddBoolean(epi.Variadic);
ID.AddInteger(epi.TypeQuals);
ID.AddInteger(epi.RefQualifier);
- if (isDynamicExceptionSpec(epi.ExceptionSpecType)) {
- ID.AddBoolean(epi.ExceptionSpecType == EST_DynamicAny);
+ ID.AddInteger(epi.ExceptionSpecType);
+ if (epi.ExceptionSpecType == EST_Dynamic) {
for (unsigned i = 0; i != epi.NumExceptions; ++i)
ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr());
+ } else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){
+ epi.NoexceptExpr->Profile(ID, *Context, true);
}
epi.ExtInfo.Profile(ID);
}
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, getResultType(), arg_type_begin(), NumArgs, getExtProtoInfo());
+ Profile(ID, getResultType(), arg_type_begin(), NumArgs, getExtProtoInfo(),
+ getContext());
}
QualType TypedefType::desugar() const {
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index 254f463..d0267ca 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -421,12 +421,12 @@
S += " &&";
break;
}
-
- if (T->hasExceptionSpec()) {
+
+ if (T->hasDynamicExceptionSpec()) {
S += " throw(";
- if (T->hasAnyExceptionSpec())
+ if (T->getExceptionSpecType() == EST_MSAny)
S += "...";
- else
+ else
for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I) {
if (I)
S += ", ";
@@ -436,6 +436,16 @@
S += ExceptionType;
}
S += ")";
+ } else if (isNoexceptExceptionSpec(T->getExceptionSpecType())) {
+ S += " noexcept";
+ if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
+ S += "(";
+ llvm::raw_string_ostream EOut(S);
+ T->getNoexceptExpr()->printPretty(EOut, 0, Policy);
+ EOut.flush();
+ S += EOut.str();
+ S += ")";
+ }
}
print(T->getResultType(), S);
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index ca7469f..8d27c0c 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -1128,7 +1128,7 @@
const FunctionType *FT = Ty->getAs<FunctionType>();
if (FT) {
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
- if (Proto->hasEmptyExceptionSpec())
+ if (Proto->isNothrow())
return false;
}
return true;
diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp
index 158e3a3..d72e6dc 100644
--- a/lib/CodeGen/CGCall.cpp
+++ b/lib/CodeGen/CGCall.cpp
@@ -709,7 +709,7 @@
FuncAttrs |= llvm::Attribute::NoUnwind;
else if (const FunctionDecl *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
const FunctionProtoType *FPT = Fn->getType()->getAs<FunctionProtoType>();
- if (FPT && FPT->hasEmptyExceptionSpec())
+ if (FPT && FPT->isNothrow())
FuncAttrs |= llvm::Attribute::NoUnwind;
}
diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp
index 0b248bd..f05bc70 100644
--- a/lib/CodeGen/CGException.cpp
+++ b/lib/CodeGen/CGException.cpp
@@ -449,9 +449,8 @@
if (Proto == 0)
return;
- assert(!Proto->hasAnyExceptionSpec() && "function with parameter pack");
-
- if (!Proto->hasExceptionSpec())
+ // FIXME: What about noexcept?
+ if (!Proto->hasDynamicExceptionSpec())
return;
unsigned NumExceptions = Proto->getNumExceptions();
@@ -477,7 +476,7 @@
if (Proto == 0)
return;
- if (!Proto->hasExceptionSpec())
+ if (!Proto->hasDynamicExceptionSpec())
return;
EHStack.popFilter();
diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp
index 0ca0240..c9c8a6a 100644
--- a/lib/CodeGen/CGExprCXX.cpp
+++ b/lib/CodeGen/CGExprCXX.cpp
@@ -982,7 +982,7 @@
// exception spec; for this part, we inline
// CXXNewExpr::shouldNullCheckAllocation()) and we have an
// interesting initializer.
- bool nullCheck = allocatorType->hasNonThrowingExceptionSpec() &&
+ bool nullCheck = allocatorType->isNothrow() &&
!(allocType->isPODType() && !E->hasInitializer());
llvm::BasicBlock *nullCheckBB = 0;
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 7b11e92..2cd2398 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -2042,6 +2042,11 @@
SourceLocation LParenLoc = ConsumeParen();
NoexceptType = EST_ComputedNoexcept;
NoexceptExpr = ParseConstantExpression();
+ // The argument must be contextually convertible to bool. We use
+ // ActOnBooleanCondition for this purpose.
+ if (!NoexceptExpr.isInvalid())
+ NoexceptExpr = Actions.ActOnBooleanCondition(getCurScope(), KeywordLoc,
+ NoexceptExpr.get());
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
NoexceptRange = SourceRange(KeywordLoc, RParenLoc);
} else {
@@ -2090,7 +2095,7 @@
if (!Tok.is(tok::l_paren)) {
Diag(Tok, diag::err_expected_lparen_after) << "throw";
SpecificationRange.setEnd(SpecificationRange.getBegin());
- return EST_Dynamic;
+ return EST_DynamicNone;
}
SourceLocation LParenLoc = ConsumeParen();
@@ -2102,7 +2107,7 @@
Diag(EllipsisLoc, diag::ext_ellipsis_exception_spec);
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
SpecificationRange.setEnd(RParenLoc);
- return EST_DynamicAny;
+ return EST_MSAny;
}
// Parse the sequence of type-ids.
@@ -2132,7 +2137,7 @@
}
SpecificationRange.setEnd(MatchRHSPunctuation(tok::r_paren, LParenLoc));
- return EST_Dynamic;
+ return Exceptions.empty() ? EST_DynamicNone : EST_Dynamic;
}
/// ParseTrailingReturnType - Parse a trailing return type on a new-style
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index 1099327..68b599b 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -1238,6 +1238,16 @@
if (!SkipUntil(tok::r_paren))
return TPResult::Error();
}
+ if (Tok.is(tok::kw_noexcept)) {
+ ConsumeToken();
+ // Possibly an expression as well.
+ if (Tok.is(tok::l_paren)) {
+ // Find the matching rparen.
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren))
+ return TPResult::Error();
+ }
+ }
return TPResult::Ambiguous();
}
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c967864..3b2a062 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1552,8 +1552,8 @@
mergeParamDeclAttributes(*ni, *oi, Context);
}
-/// MergeVarDecl - We parsed a variable 'New' which has the same name and scope
-/// as a previous declaration 'Old'. Figure out how to merge their types,
+/// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and
+/// scope as a previous declaration 'Old'. Figure out how to merge their types,
/// emitting diagnostics as appropriate.
///
/// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
@@ -1570,8 +1570,10 @@
if (AT && !AT->isDeduced()) {
// We don't know what the new type is until the initializer is attached.
return;
- } else if (Context.hasSameType(New->getType(), Old->getType()))
- return;
+ } else if (Context.hasSameType(New->getType(), Old->getType())) {
+ // These could still be something that needs exception specs checked.
+ return MergeVarDeclExceptionSpecs(New, Old);
+ }
// C++ [basic.link]p10:
// [...] the types specified by all declarations referring to a given
// object or function shall be identical, except that declarations for an
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 2bce585..f655105 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -383,6 +383,48 @@
return Invalid;
}
+/// \brief Merge the exception specifications of two variable declarations.
+///
+/// This is called when there's a redeclaration of a VarDecl. The function
+/// checks if the redeclaration might have an exception specification and
+/// validates compatibility and merges the specs if necessary.
+void Sema::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {
+ // Shortcut if exceptions are disabled.
+ if (!getLangOptions().CXXExceptions)
+ return;
+
+ assert(Context.hasSameType(New->getType(), Old->getType()) &&
+ "Should only be called if types are otherwise the same.");
+
+ QualType NewType = New->getType();
+ QualType OldType = Old->getType();
+
+ // We're only interested in pointers and references to functions, as well
+ // as pointers to member functions.
+ if (const ReferenceType *R = NewType->getAs<ReferenceType>()) {
+ NewType = R->getPointeeType();
+ OldType = OldType->getAs<ReferenceType>()->getPointeeType();
+ } else if (const PointerType *P = NewType->getAs<PointerType>()) {
+ NewType = P->getPointeeType();
+ OldType = OldType->getAs<PointerType>()->getPointeeType();
+ } else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) {
+ NewType = M->getPointeeType();
+ OldType = OldType->getAs<MemberPointerType>()->getPointeeType();
+ }
+
+ if (!NewType->isFunctionProtoType())
+ return;
+
+ // There's lots of special cases for functions. For function pointers, system
+ // libraries are hopefully not as broken so that we don't need these
+ // workarounds.
+ if (CheckEquivalentExceptionSpec(
+ OldType->getAs<FunctionProtoType>(), Old->getLocation(),
+ NewType->getAs<FunctionProtoType>(), New->getLocation())) {
+ New->setInvalidDecl();
+ }
+}
+
/// CheckCXXDefaultArguments - Verify that the default arguments for a
/// function declaration are well-formed according to C++
/// [dcl.fct.default].
@@ -2978,53 +3020,101 @@
/// implicitly-declared special member functions.
class ImplicitExceptionSpecification {
ASTContext &Context;
- bool AllowsAllExceptions;
+ // We order exception specifications thus:
+ // noexcept is the most restrictive, but is only used in C++0x.
+ // throw() comes next.
+ // Then a throw(collected exceptions)
+ // Finally no specification.
+ // throw(...) is used instead if any called function uses it.
+ ExceptionSpecificationType ComputedEST;
llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
llvm::SmallVector<QualType, 4> Exceptions;
-
+
+ void ClearExceptions() {
+ ExceptionsSeen.clear();
+ Exceptions.clear();
+ }
+
public:
explicit ImplicitExceptionSpecification(ASTContext &Context)
- : Context(Context), AllowsAllExceptions(false) { }
-
- /// \brief Whether the special member function should have any
- /// exception specification at all.
- bool hasExceptionSpecification() const {
- return !AllowsAllExceptions;
+ : Context(Context), ComputedEST(EST_BasicNoexcept) {
+ if (!Context.getLangOptions().CPlusPlus0x)
+ ComputedEST = EST_DynamicNone;
}
-
- /// \brief Whether the special member function should have a
- /// throw(...) exception specification (a Microsoft extension).
- bool hasAnyExceptionSpecification() const {
- return false;
+
+ /// \brief Get the computed exception specification type.
+ ExceptionSpecificationType getExceptionSpecType() const {
+ assert(ComputedEST != EST_ComputedNoexcept &&
+ "noexcept(expr) should not be a possible result");
+ return ComputedEST;
}
-
+
/// \brief The number of exceptions in the exception specification.
unsigned size() const { return Exceptions.size(); }
-
+
/// \brief The set of exceptions in the exception specification.
const QualType *data() const { return Exceptions.data(); }
-
- /// \brief Note that
+
+ /// \brief Integrate another called method into the collected data.
void CalledDecl(CXXMethodDecl *Method) {
- // If we already know that we allow all exceptions, do nothing.
- if (AllowsAllExceptions || !Method)
+ // If we have an MSAny spec already, don't bother.
+ if (!Method || ComputedEST == EST_MSAny)
return;
-
+
const FunctionProtoType *Proto
= Method->getType()->getAs<FunctionProtoType>();
-
+
+ ExceptionSpecificationType EST = Proto->getExceptionSpecType();
+
// If this function can throw any exceptions, make a note of that.
- if (!Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec()) {
- AllowsAllExceptions = true;
- ExceptionsSeen.clear();
- Exceptions.clear();
+ if (EST == EST_MSAny || EST == EST_None) {
+ ClearExceptions();
+ ComputedEST = EST;
return;
}
-
+
+ // If this function has a basic noexcept, it doesn't affect the outcome.
+ if (EST == EST_BasicNoexcept)
+ return;
+
+ // If we have a throw-all spec at this point, ignore the function.
+ if (ComputedEST == EST_None)
+ return;
+
+ // If we're still at noexcept(true) and there's a nothrow() callee,
+ // change to that specification.
+ if (EST == EST_DynamicNone) {
+ if (ComputedEST == EST_BasicNoexcept)
+ ComputedEST = EST_DynamicNone;
+ return;
+ }
+
+ // Check out noexcept specs.
+ if (EST == EST_ComputedNoexcept) {
+ FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec();
+ 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;
+ }
+
+ assert(EST == EST_Dynamic && "EST case not considered earlier.");
+ assert(ComputedEST != EST_None &&
+ "Shouldn't collect exceptions when throw-all is guaranteed.");
+ ComputedEST = EST_Dynamic;
// Record the exceptions in this function's exception specification.
for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
EEnd = Proto->exception_end();
- E != EEnd; ++E)
+ E != EEnd; ++E)
if (ExceptionsSeen.insert(Context.getCanonicalType(*E)))
Exceptions.push_back(*E);
}
@@ -4672,7 +4762,7 @@
// exception-specification. [...]
ImplicitExceptionSpecification ExceptSpec(Context);
- // Direct base-class destructors.
+ // Direct base-class constructors.
for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
BEnd = ClassDecl->bases_end();
B != BEnd; ++B) {
@@ -4688,8 +4778,8 @@
ExceptSpec.CalledDecl(Constructor);
}
}
-
- // Virtual base-class destructors.
+
+ // Virtual base-class constructors.
for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(),
BEnd = ClassDecl->vbases_end();
B != BEnd; ++B) {
@@ -4702,8 +4792,8 @@
ExceptSpec.CalledDecl(Constructor);
}
}
-
- // Field destructors.
+
+ // Field constructors.
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
FEnd = ClassDecl->field_end();
F != FEnd; ++F) {
@@ -4720,9 +4810,7 @@
}
FunctionProtoType::ExtProtoInfo EPI;
- EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
- (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
- EST_None;
+ EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
EPI.NumExceptions = ExceptSpec.size();
EPI.Exceptions = ExceptSpec.data();
@@ -4997,16 +5085,14 @@
ExceptSpec.CalledDecl(
LookupDestructor(cast<CXXRecordDecl>(RecordTy->getDecl())));
}
-
+
// Create the actual destructor declaration.
FunctionProtoType::ExtProtoInfo EPI;
- EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
- (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
- EST_None;
+ EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
EPI.NumExceptions = ExceptSpec.size();
EPI.Exceptions = ExceptSpec.data();
QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI);
-
+
CanQualType ClassType
= Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
SourceLocation ClassLoc = ClassDecl->getLocation();
@@ -5014,9 +5100,9 @@
= Context.DeclarationNames.getCXXDestructorName(ClassType);
DeclarationNameInfo NameInfo(Name, ClassLoc);
CXXDestructorDecl *Destructor
- = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0,
- /*isInline=*/true,
- /*isImplicitlyDeclared=*/true);
+ = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0,
+ /*isInline=*/true,
+ /*isImplicitlyDeclared=*/true);
Destructor->setAccess(AS_public);
Destructor->setImplicit();
Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
@@ -5396,13 +5482,11 @@
ExceptSpec.CalledDecl(CopyAssign);
}
}
-
+
// An implicitly-declared copy assignment operator is an inline public
// member of its class.
FunctionProtoType::ExtProtoInfo EPI;
- EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
- (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
- EST_None;
+ EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
EPI.NumExceptions = ExceptSpec.size();
EPI.Exceptions = ExceptSpec.data();
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
@@ -5860,13 +5944,11 @@
ExceptSpec.CalledDecl(CopyConstructor);
}
}
-
+
// An implicitly-declared copy constructor is an inline public
// member of its class.
FunctionProtoType::ExtProtoInfo EPI;
- EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
- (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
- EST_None;
+ EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
EPI.NumExceptions = ExceptSpec.size();
EPI.Exceptions = ExceptSpec.data();
DeclarationName Name
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index dce4c6e..01ee712 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -105,7 +105,8 @@
New->getType()->getAs<FunctionProtoType>(),
New->getLocation(),
&MissingExceptionSpecification,
- &MissingEmptyExceptionSpecification))
+ &MissingEmptyExceptionSpecification,
+ /*AllowNoexceptAllMatchWithNoSpec=*/true))
return false;
// The failure was something other than an empty exception
@@ -129,8 +130,7 @@
Context.getSourceManager().isInSystemHeader(Old->getLocation())) &&
Old->isExternC()) {
FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
- EPI.ExceptionSpecType = EST_Dynamic;
- EPI.NumExceptions = 0;
+ EPI.ExceptionSpecType = EST_DynamicNone;
QualType NewType = Context.getFunctionType(NewProto->getResultType(),
NewProto->arg_type_begin(),
NewProto->getNumArgs(),
@@ -144,11 +144,14 @@
= Old->getType()->getAs<FunctionProtoType>();
FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
- EPI.ExceptionSpecType = OldProto->hasExceptionSpec() ?
- (OldProto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) :
- EST_None;
- EPI.NumExceptions = OldProto->getNumExceptions();
- EPI.Exceptions = OldProto->exception_begin();
+ EPI.ExceptionSpecType = OldProto->getExceptionSpecType();
+ if (EPI.ExceptionSpecType == EST_Dynamic) {
+ EPI.NumExceptions = OldProto->getNumExceptions();
+ EPI.Exceptions = OldProto->exception_begin();
+ } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
+ // FIXME: We can't just take the expression from the old prototype. It
+ // likely contains references to the old prototype's parameters.
+ }
// Update the type of the function with the appropriate exception
// specification.
@@ -178,20 +181,43 @@
// Warn about the lack of exception specification.
llvm::SmallString<128> ExceptionSpecString;
llvm::raw_svector_ostream OS(ExceptionSpecString);
- OS << "throw(";
- bool OnFirstException = true;
- for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
- EEnd = OldProto->exception_end();
- E != EEnd;
- ++E) {
- if (OnFirstException)
- OnFirstException = false;
- else
- OS << ", ";
-
- OS << E->getAsString(Context.PrintingPolicy);
+ switch (OldProto->getExceptionSpecType()) {
+ case EST_DynamicNone:
+ OS << "throw()";
+ break;
+
+ case EST_Dynamic: {
+ OS << "throw(";
+ bool OnFirstException = true;
+ for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
+ EEnd = OldProto->exception_end();
+ E != EEnd;
+ ++E) {
+ if (OnFirstException)
+ OnFirstException = false;
+ else
+ OS << ", ";
+
+ OS << E->getAsString(Context.PrintingPolicy);
+ }
+ OS << ")";
+ break;
}
- OS << ")";
+
+ case EST_BasicNoexcept:
+ OS << "noexcept";
+ break;
+
+ case EST_ComputedNoexcept:
+ OS << "noexcept(";
+ OldProto->getNoexceptExpr()->printPretty(OS, Context, 0,
+ Context.PrintingPolicy);
+ OS << ")";
+ break;
+
+ default:
+ assert(false && "This spec type is compatible with none.");
+ }
OS.flush();
SourceLocation FixItLoc;
@@ -236,18 +262,17 @@
Old, OldLoc, New, NewLoc);
}
-/// CheckEquivalentExceptionSpec - Check if the two types have equivalent
-/// exception specifications. Exception specifications are equivalent if
-/// they allow exactly the same set of exception types. It does not matter how
-/// that is achieved. See C++ [except.spec]p2.
-bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
+/// CheckEquivalentExceptionSpec - Check if the two types have compatible
+/// exception specifications. See C++ [except.spec]p3.
+bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
const PartialDiagnostic & NoteID,
- const FunctionProtoType *Old,
+ const FunctionProtoType *Old,
SourceLocation OldLoc,
- const FunctionProtoType *New,
+ const FunctionProtoType *New,
SourceLocation NewLoc,
bool *MissingExceptionSpecification,
- bool *MissingEmptyExceptionSpecification) {
+ bool*MissingEmptyExceptionSpecification,
+ bool AllowNoexceptAllMatchWithNoSpec) {
// Just completely ignore this under -fno-exceptions.
if (!getLangOptions().CXXExceptions)
return false;
@@ -258,29 +283,109 @@
if (MissingEmptyExceptionSpecification)
*MissingEmptyExceptionSpecification = false;
- bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec();
- bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec();
- if (getLangOptions().Microsoft) {
- // Treat throw(whatever) as throw(...) to be compatible with MS headers.
- if (New->hasExceptionSpec() && New->getNumExceptions() > 0)
- NewAny = true;
- if (Old->hasExceptionSpec() && Old->getNumExceptions() > 0)
- OldAny = true;
+ // C++0x [except.spec]p3: Two exception-specifications are compatible if:
+ // - both are non-throwing, regardless of their form,
+ // - both have the form noexcept(constant-expression) and the constant-
+ // expressions are equivalent,
+ // - one exception-specification is a noexcept-specification allowing all
+ // exceptions and the other is of the form throw(type-id-list), or
+ // - both are dynamic-exception-specifications that have the same set of
+ // adjusted types.
+ //
+ // C++0x [except.spec]p12: An exception-specifcation is non-throwing if it is
+ // of the form throw(), noexcept, or noexcept(constant-expression) where the
+ // constant-expression yields true.
+ //
+ // CWG 1073 Proposed resolution: Strike the third bullet above.
+ //
+ // C++0x [except.spec]p4: If any declaration of a function has an exception-
+ // specifier that is not a noexcept-specification allowing all exceptions,
+ // all declarations [...] of that function shall have a compatible
+ // exception-specification.
+ //
+ // That last point basically means that noexcept(false) matches no spec.
+ // It's considered when AllowNoexceptAllMatchWithNoSpec is true.
+
+ ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
+ ExceptionSpecificationType NewEST = New->getExceptionSpecType();
+
+ // Shortcut the case where both have no spec.
+ if (OldEST == EST_None && NewEST == EST_None)
+ return false;
+
+ FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec();
+ FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec();
+ 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) {
+ Diag(NewLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(OldLoc, NoteID);
+ return true;
}
- if (OldAny && NewAny)
+ if (getLangOptions().Microsoft) {
+ // Treat throw(whatever) as throw(...) to be compatible with MS headers.
+ if (OldEST == EST_Dynamic)
+ OldEST = EST_MSAny;
+ if (NewEST == EST_Dynamic)
+ NewEST = EST_MSAny;
+ }
+
+ // The MS extension throw(...) is compatible with itself.
+ if (OldEST == EST_MSAny && NewEST == EST_MSAny)
return false;
- if (OldAny || NewAny) {
+
+ // 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;
+ }
+
+ // Any non-throwing specifications are compatible.
+ bool OldNonThrowing = OldNR == FunctionProtoType::NR_Nothrow ||
+ OldEST == EST_DynamicNone;
+ bool NewNonThrowing = NewNR == FunctionProtoType::NR_Nothrow ||
+ NewEST == EST_DynamicNone;
+ if (OldNonThrowing && NewNonThrowing)
+ return false;
+
+ // At this point, the only remaining valid case is two matching dynamic
+ // specifications. We return here unless both specifications are dynamic.
+ if (OldEST != EST_Dynamic || NewEST != EST_Dynamic) {
if (MissingExceptionSpecification && Old->hasExceptionSpec() &&
!New->hasExceptionSpec()) {
// The old type has an exception specification of some sort, but
// the new type does not.
*MissingExceptionSpecification = true;
- if (MissingEmptyExceptionSpecification &&
- !Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0) {
- // The old type has a throw() exception specification and the
- // new type has no exception specification, and the caller asked
+ 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;
}
@@ -294,8 +399,11 @@
return true;
}
+ assert(OldEST == EST_Dynamic && NewEST == EST_Dynamic &&
+ "Exception compatibility logic error: non-dynamic spec slipped through.");
+
bool Success = true;
- // Both have a definite exception spec. Collect the first set, then compare
+ // Both have a dynamic exception spec. Collect the first set, then compare
// to the second.
llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
for (FunctionProtoType::exception_iterator I = Old->exception_begin(),
@@ -340,19 +448,66 @@
if (!SubLoc.isValid())
SubLoc = SuperLoc;
+ ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType();
+
// If superset contains everything, we're done.
- if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
+ if (SuperEST == EST_None || SuperEST == EST_MSAny)
return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+ // If there are dependent noexcept specs, assume everything is fine. Unlike
+ // with the equivalency check, this is safe in this case, because we don't
+ // want to merge declarations. Checks after instantiation will catch any
+ // omissions we make here.
+ // We also shortcut checking if a noexcept expression was bad.
+
+ FunctionProtoType::NoexceptResult SuperNR =Superset->getNoexceptSpec();
+ if (SuperNR == FunctionProtoType::NR_BadNoexcept ||
+ SuperNR == FunctionProtoType::NR_Dependent)
+ return false;
+
+ // Another case of the superset containing everything.
+ if (SuperNR == FunctionProtoType::NR_Throw)
+ return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+ ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
+
// It does not. If the subset contains everything, we've failed.
- if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
+ if (SubEST == EST_None || SubEST == EST_MSAny) {
Diag(SubLoc, DiagID);
if (NoteID.getDiagID() != 0)
Diag(SuperLoc, NoteID);
return true;
}
- // Neither contains everything. Do a proper comparison.
+ FunctionProtoType::NoexceptResult SubNR = Subset->getNoexceptSpec();
+ 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(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+ // Otherwise, if the superset contains nothing, we've failed.
+ if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) {
+ Diag(SubLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+
+ assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic &&
+ "Exception spec subset: non-dynamic case slipped through.");
+
+ // Neither contains everything or nothing. Do a proper comparison.
for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
// Take one type from the subset.
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 25f042c..a1cd43a 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -1501,10 +1501,12 @@
}
FunctionProtoType::ExtProtoInfo EPI;
- EPI.ExceptionSpecType = EST_Dynamic;
if (HasBadAllocExceptionSpec) {
+ EPI.ExceptionSpecType = EST_Dynamic;
EPI.NumExceptions = 1;
EPI.Exceptions = &BadAllocType;
+ } else {
+ EPI.ExceptionSpecType = EST_DynamicNone;
}
QualType FnType = Context.getFunctionType(Return, &Argument, 1, EPI);
@@ -2421,7 +2423,7 @@
FoundAssign = true;
const FunctionProtoType *CPT
= Operator->getType()->getAs<FunctionProtoType>();
- if (!CPT->hasEmptyExceptionSpec()) {
+ if (!CPT->isNothrow()) {
AllNoThrow = false;
break;
}
@@ -2461,9 +2463,9 @@
FoundConstructor = true;
const FunctionProtoType *CPT
= Constructor->getType()->getAs<FunctionProtoType>();
- // TODO: check whether evaluating default arguments can throw.
+ // FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
- if (!CPT->hasEmptyExceptionSpec() || CPT->getNumArgs() > 1) {
+ if (!CPT->isNothrow() || CPT->getNumArgs() > 1) {
AllNoThrow = false;
break;
}
@@ -2498,7 +2500,7 @@
= Constructor->getType()->getAs<FunctionProtoType>();
// TODO: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
- return CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0;
+ return CPT->isNothrow() && CPT->getNumArgs() == 0;
}
}
}
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e60882d..441149f 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2085,8 +2085,7 @@
const FunctionProtoType *Proto = Tmpl->getType()->getAs<FunctionProtoType>();
assert(Proto && "Function template without prototype?");
- if (Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec() ||
- Proto->getNoReturnAttr()) {
+ if (Proto->hasExceptionSpec() || Proto->getNoReturnAttr()) {
// The function has an exception specification or a "noreturn"
// attribute. Substitute into each of the exception types.
llvm::SmallVector<QualType, 4> Exceptions;
@@ -2100,7 +2099,7 @@
Unexpanded);
assert(!Unexpanded.empty() &&
"Pack expansion without parameter packs?");
-
+
bool Expand = false;
bool RetainExpansion = false;
llvm::Optional<unsigned> NumExpansions
@@ -2114,7 +2113,7 @@
RetainExpansion,
NumExpansions))
break;
-
+
if (!Expand) {
// We can't expand this pack expansion into separate arguments yet;
// just substitute into the pattern and create a new pack expansion
@@ -2130,7 +2129,7 @@
Exceptions.push_back(T);
continue;
}
-
+
// Substitute into the pack expansion pattern for each template
bool Invalid = false;
for (unsigned ArgIdx = 0; ArgIdx != *NumExpansions; ++ArgIdx) {
@@ -2143,13 +2142,13 @@
Invalid = true;
break;
}
-
+
Exceptions.push_back(T);
}
-
+
if (Invalid)
break;
-
+
continue;
}
@@ -2166,9 +2165,8 @@
// Rebuild the function type
FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();
- EPI.ExceptionSpecType = Proto->hasExceptionSpec() ?
- (Proto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) :
- EST_None;
+ // FIXME: Handle noexcept
+ EPI.ExceptionSpecType = Proto->getExceptionSpecType();
EPI.NumExceptions = Exceptions.size();
EPI.Exceptions = Exceptions.data();
EPI.ExtInfo = Proto->getExtInfo();
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 62bf8e6..f49a49c 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1901,7 +1901,22 @@
EPI.NumExceptions = Exceptions.size();
EPI.Exceptions = Exceptions.data();
} else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
- EPI.NoexceptExpr = FTI.NoexceptExpr;
+ // If an error occurred, there's no expression here.
+ if (Expr *NoexceptExpr = FTI.NoexceptExpr) {
+ assert((NoexceptExpr->isTypeDependent() ||
+ NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
+ Context.BoolTy) &&
+ "Parser should have made sure that the expression is boolean");
+ SourceLocation ErrLoc;
+ llvm::APSInt Dummy;
+ if (!NoexceptExpr->isValueDependent() &&
+ !NoexceptExpr->isIntegerConstantExpr(Dummy, Context, &ErrLoc,
+ /*evaluated*/false))
+ Diag(ErrLoc, diag::err_noexcept_needs_constant_expression)
+ << NoexceptExpr->getSourceRange();
+ else
+ EPI.NoexceptExpr = NoexceptExpr;
+ }
}
T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), EPI);
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 1283525..b34b62a 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -3117,15 +3117,18 @@
EPI.Variadic = Record[Idx++];
EPI.TypeQuals = Record[Idx++];
EPI.RefQualifier = static_cast<RefQualifierKind>(Record[Idx++]);
- bool HasExceptionSpec = Record[Idx++];
- bool HasAnyExceptionSpec = Record[Idx++];
- EPI.ExceptionSpecType = HasExceptionSpec ?
- (HasAnyExceptionSpec ? EST_DynamicAny : EST_Dynamic) : EST_None;
- EPI.NumExceptions = Record[Idx++];
- llvm::SmallVector<QualType, 2> Exceptions;
- for (unsigned I = 0; I != EPI.NumExceptions; ++I)
- Exceptions.push_back(GetType(Record[Idx++]));
- EPI.Exceptions = Exceptions.data();
+ ExceptionSpecificationType EST =
+ static_cast<ExceptionSpecificationType>(Record[Idx++]);
+ EPI.ExceptionSpecType = EST;
+ if (EST == EST_Dynamic) {
+ EPI.NumExceptions = Record[Idx++];
+ llvm::SmallVector<QualType, 2> Exceptions;
+ for (unsigned I = 0; I != EPI.NumExceptions; ++I)
+ Exceptions.push_back(GetType(Record[Idx++]));
+ EPI.Exceptions = Exceptions.data();
+ } else if (EST == EST_ComputedNoexcept) {
+ EPI.NoexceptExpr = ReadExpr(*Loc.F);
+ }
return Context->getFunctionType(ResultType, ParamTypes.data(), NumParams,
EPI);
}
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 5f83097..877650f 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -178,11 +178,14 @@
Record.push_back(T->isVariadic());
Record.push_back(T->getTypeQuals());
Record.push_back(static_cast<unsigned>(T->getRefQualifier()));
- Record.push_back(T->hasExceptionSpec());
- Record.push_back(T->hasAnyExceptionSpec());
- Record.push_back(T->getNumExceptions());
- for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I)
- Writer.AddTypeRef(T->getExceptionType(I), Record);
+ Record.push_back(T->getExceptionSpecType());
+ if (T->getExceptionSpecType() == EST_Dynamic) {
+ Record.push_back(T->getNumExceptions());
+ for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I)
+ Writer.AddTypeRef(T->getExceptionType(I), Record);
+ } else if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
+ Writer.AddStmt(T->getNoexceptExpr());
+ }
Code = TYPE_FUNCTION_PROTO;
}