From 60618fa7f88d5162bb5b40988b6b38d4d75d6fc6 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sat, 12 Mar 2011 11:50:43 +0000 Subject: [PATCH] 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 --- include/clang/AST/Type.h | 91 +++++-- include/clang/Basic/DiagnosticSemaKinds.td | 2 + .../clang/Basic/ExceptionSpecificationType.h | 7 +- include/clang/Sema/Sema.h | 6 +- lib/AST/ASTContext.cpp | 16 +- lib/AST/ASTImporter.cpp | 21 +- lib/AST/DeclPrinter.cpp | 20 +- lib/AST/DumpXML.cpp | 5 +- lib/AST/Expr.cpp | 2 +- lib/AST/ExprCXX.cpp | 2 +- lib/AST/Type.cpp | 71 +++-- lib/AST/TypePrinter.cpp | 18 +- lib/Analysis/CFG.cpp | 2 +- lib/CodeGen/CGCall.cpp | 2 +- lib/CodeGen/CGException.cpp | 7 +- lib/CodeGen/CGExprCXX.cpp | 2 +- lib/Parse/ParseDeclCXX.cpp | 11 +- lib/Parse/ParseTentative.cpp | 10 + lib/Sema/SemaDecl.cpp | 10 +- lib/Sema/SemaDeclCXX.cpp | 184 +++++++++---- lib/Sema/SemaExceptionSpec.cpp | 249 ++++++++++++++---- lib/Sema/SemaExprCXX.cpp | 12 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 20 +- lib/Sema/SemaType.cpp | 17 +- lib/Serialization/ASTReader.cpp | 21 +- lib/Serialization/ASTWriter.cpp | 13 +- test/CXX/except/except.spec/p1.cpp | 29 +- .../except/except.spec/p2-dynamic-types.cpp | 34 +++ test/CXX/except/except.spec/p2-places.cpp | 63 +++++ test/CXX/except/except.spec/p3.cpp | 94 +++++++ test/CXX/except/except.spec/p5-pointers.cpp | 85 ++++++ test/CXX/except/except.spec/p5-virtual.cpp | 96 +++++++ test/SemaCXX/exception-spec.cpp | 193 -------------- 33 files changed, 999 insertions(+), 416 deletions(-) create mode 100644 test/CXX/except/except.spec/p2-dynamic-types.cpp create mode 100644 test/CXX/except/except.spec/p2-places.cpp create mode 100644 test/CXX/except/except.spec/p3.cpp create mode 100644 test/CXX/except/except.spec/p5-pointers.cpp create mode 100644 test/CXX/except/except.spec/p5-virtual.cpp delete mode 100644 test/SemaCXX/exception-spec.cpp diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 713316f146..6d5ca71ed4 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2430,19 +2430,17 @@ private: } FunctionProtoType(QualType result, const QualType *args, unsigned numArgs, - QualType canonical, const ExtProtoInfo &epi); + QualType canonical, const ExtProtoInfo &epi, + const ASTContext *Context); /// NumArgs - The number of arguments this function has, not counting '...'. unsigned NumArgs : 20; /// NumExceptions - The number of types in the exception spec, if any. - unsigned NumExceptions : 10; + unsigned NumExceptions : 9; - /// HasExceptionSpec - Whether this function has an exception spec at all. - unsigned HasExceptionSpec : 1; - - /// HasAnyExceptionSpec - Whether this function has a throw(...) spec. - unsigned HasAnyExceptionSpec : 1; + /// ExceptionSpecType - The type of exception specification this function has. + unsigned ExceptionSpecType : 3; /// ArgInfo - There is an variable size array after the class in memory that /// holds the argument types. @@ -2450,6 +2448,19 @@ private: /// Exceptions - There is another variable size array after ArgInfo that /// holds the exception types. + /// NoexceptExpr - Instead of Exceptions, there may be a single Expr* pointing + /// to the expression in the noexcept() specifier. + + /// Context - If there is a NoexceptExpr, we need to store a pointer to the + /// ASTContext as well. We do it right after NoexceptExpr. + + const ASTContext *getContext() const { + // Context sits after NoexceptExpr, one pointer past where the arguments end + if(getExceptionSpecType() == EST_ComputedNoexcept) + return *(reinterpret_cast(arg_type_end()) + 1); + return 0; + } + friend class ASTContext; // ASTContext creates these. public: @@ -2463,36 +2474,66 @@ public: ExtProtoInfo EPI; EPI.ExtInfo = getExtInfo(); EPI.Variadic = isVariadic(); - EPI.ExceptionSpecType = hasExceptionSpec() ? - (hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) : EST_None; + EPI.ExceptionSpecType = getExceptionSpecType(); EPI.TypeQuals = static_cast(getTypeQuals()); EPI.RefQualifier = getRefQualifier(); - EPI.NumExceptions = NumExceptions; - EPI.Exceptions = exception_begin(); + if (EPI.ExceptionSpecType == EST_Dynamic) { + EPI.NumExceptions = NumExceptions; + EPI.Exceptions = exception_begin(); + } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { + EPI.NoexceptExpr = getNoexceptExpr(); + } return EPI; } - bool hasExceptionSpec() const { return HasExceptionSpec; } - bool hasAnyExceptionSpec() const { return HasAnyExceptionSpec; } + /// \brief Get the kind of exception specification on this function. + ExceptionSpecificationType getExceptionSpecType() const { + return static_cast(ExceptionSpecType); + } + /// \brief Return whether this function has any kind of exception spec. + bool hasExceptionSpec() const { + return getExceptionSpecType() != EST_None; + } + /// \brief Return whether this function has a dynamic (throw) exception spec. + bool hasDynamicExceptionSpec() const { + return isDynamicExceptionSpec(getExceptionSpecType()); + } + /// \brief Return whther this function has a noexcept exception spec. + bool hasNoexceptExceptionSpec() const { + return isNoexceptExceptionSpec(getExceptionSpecType()); + } + /// \brief Result type of getNoexceptSpec(). + enum NoexceptResult { + NR_NoNoexcept, ///< There is no noexcept specifier. + NR_BadNoexcept, ///< The noexcept specifier has a bad expression. + NR_Dependent, ///< The noexcept specifier is dependent. + NR_Throw, ///< The noexcept specifier evaluates to false. + NR_Nothrow ///< The noexcept specifier evaluates to true. + }; + /// \brief Get the meaning of the noexcept spec on this function, if any. + NoexceptResult getNoexceptSpec() const; unsigned getNumExceptions() const { return NumExceptions; } QualType getExceptionType(unsigned i) const { assert(i < NumExceptions && "Invalid exception number!"); return exception_begin()[i]; } - bool hasEmptyExceptionSpec() const { - return hasExceptionSpec() && !hasAnyExceptionSpec() && - getNumExceptions() == 0; + Expr *getNoexceptExpr() const { + if (getExceptionSpecType() != EST_ComputedNoexcept) + return 0; + // NoexceptExpr sits where the arguments end. + return *reinterpret_cast(arg_type_end()); } - - /// \brief Returns true if this function type has a non-throwing - /// exception-specification. - bool hasNonThrowingExceptionSpec() const { - // FIXME: this needs to be updated for C++0x. - return hasEmptyExceptionSpec(); + bool isNothrow() const { + ExceptionSpecificationType EST = getExceptionSpecType(); + if (EST == EST_DynamicNone || EST == EST_BasicNoexcept) + return true; + if (EST != EST_ComputedNoexcept) + return false; + return getNoexceptSpec() == NR_Nothrow; } using FunctionType::isVariadic; - + /// \brief Determines whether this function prototype contains a /// parameter pack at the end. /// @@ -2521,6 +2562,8 @@ public: return arg_type_end(); } exception_iterator exception_end() const { + if (getExceptionSpecType() != EST_Dynamic) + return exception_begin(); return exception_begin() + NumExceptions; } @@ -2535,7 +2578,7 @@ public: void Profile(llvm::FoldingSetNodeID &ID); static void Profile(llvm::FoldingSetNodeID &ID, QualType Result, arg_type_iterator ArgTys, unsigned NumArgs, - const ExtProtoInfo &EPI); + const ExtProtoInfo &EPI, const ASTContext *Context); }; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bac7050ae0..3a6c43c269 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -560,6 +560,8 @@ def err_deep_exception_specs_differ : Error< "exception specifications of %select{return|argument}0 types differ">; def warn_missing_exception_specification : Warning< "%0 is missing exception specification '%1'">; +def err_noexcept_needs_constant_expression : Error< + "argument to noexcept specifier must be a constant expression">; // C++ access checking def err_class_redeclared_with_different_access : Error< diff --git a/include/clang/Basic/ExceptionSpecificationType.h b/include/clang/Basic/ExceptionSpecificationType.h index 48621c73d7..aecf6eb269 100644 --- a/include/clang/Basic/ExceptionSpecificationType.h +++ b/include/clang/Basic/ExceptionSpecificationType.h @@ -19,14 +19,15 @@ namespace clang { /// \brief The various types of exception specifications that exist in C++0x. enum ExceptionSpecificationType { EST_None, ///< no exception specification - EST_Dynamic, ///< throw() or throw(T1, T2) - EST_DynamicAny, ///< Microsoft throw(...) extension + EST_DynamicNone, ///< throw() + EST_Dynamic, ///< throw(T1, T2) + EST_MSAny, ///< Microsoft throw(...) extension EST_BasicNoexcept, ///< noexcept EST_ComputedNoexcept ///< noexcept(expression) }; inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) { - return ESpecType == EST_Dynamic || ESpecType == EST_DynamicAny; + return ESpecType >= EST_DynamicNone && ESpecType <= EST_MSAny; } inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c92bf5984d..0ba1f2e278 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -757,7 +757,8 @@ public: const FunctionProtoType *Old, SourceLocation OldLoc, const FunctionProtoType *New, SourceLocation NewLoc, bool *MissingExceptionSpecification = 0, - bool *MissingEmptyExceptionSpecification = 0); + bool *MissingEmptyExceptionSpecification = 0, + bool AllowNoexceptAllMatchWithNoSpec = false); bool CheckExceptionSpecSubset( const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, const FunctionProtoType *Superset, SourceLocation SuperLoc, @@ -1076,8 +1077,9 @@ public: bool MergeFunctionDecl(FunctionDecl *New, Decl *Old); bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old); void mergeObjCMethodDecls(ObjCMethodDecl *New, const ObjCMethodDecl *Old); - void MergeVarDeclTypes(VarDecl *New, VarDecl *Old); void MergeVarDecl(VarDecl *New, LookupResult &OldDecls); + void MergeVarDeclTypes(VarDecl *New, VarDecl *Old); + void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old); bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old); // AssignmentAction - This is used by all the assignment diagnostic functions diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fcfd230de0..c128ce598b 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1909,7 +1909,7 @@ ASTContext::getFunctionType(QualType ResultTy, // 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 @@ ASTContext::getFunctionType(QualType ResultTy, // 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 b00c78827f..895f442eac 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -521,16 +521,21 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, } 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 c6ae128e42..2781632384 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -412,22 +412,32 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { 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()) diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp index 9d828fcfb8..9a34722aa5 100644 --- a/lib/AST/DumpXML.cpp +++ b/lib/AST/DumpXML.cpp @@ -975,15 +975,16 @@ struct XMLDumper : public XMLDeclVisitor, 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 4793b114ef..1c7807e6d2 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1659,7 +1659,7 @@ static Expr::CanThrowResult CanCalleeThrow(const Decl *D, 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 966c1fe6a7..fd94dd108d 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -102,7 +102,7 @@ void CXXNewExpr::AllocateArgsArray(ASTContext &C, bool isArray, bool CXXNewExpr::shouldNullCheckAllocation() const { return getOperatorNew()->getType()-> - castAs()->hasNonThrowingExceptionSpec(); + castAs()->isNothrow(); } // CXXDeleteExpr diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index eea08f6242..eb5254e96e 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 @@ -1165,7 +1166,8 @@ llvm::StringRef FunctionType::getNameForCallConv(CallingConv CC) { 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 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, 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(this+1); @@ -1187,20 +1188,54 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, 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(argSlot + numArgs); + *noexSlot = epi.NoexceptExpr; + const ASTContext **contextSlot = const_cast( + reinterpret_cast(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(*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(getArgType(ArgIdx - 1))) @@ -1211,23 +1246,27 @@ bool FunctionProtoType::isTemplateVariadic() const { 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 254f46337e..d0267caabd 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -421,12 +421,12 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T, 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 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T, 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 ca7469f677..8d27c0cc49 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -1128,7 +1128,7 @@ static bool CanThrow(Expr *E) { const FunctionType *FT = Ty->getAs(); if (FT) { if (const FunctionProtoType *Proto = dyn_cast(FT)) - if (Proto->hasEmptyExceptionSpec()) + if (Proto->isNothrow()) return false; } return true; diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 158e3a3a2c..d72e6dc429 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -709,7 +709,7 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, FuncAttrs |= llvm::Attribute::NoUnwind; else if (const FunctionDecl *Fn = dyn_cast(TargetDecl)) { const FunctionProtoType *FPT = Fn->getType()->getAs(); - 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 0b248bd783..f05bc700b4 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -449,9 +449,8 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) { 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 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) { 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 0ca024059c..c9c8a6a35d 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -982,7 +982,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // 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 7b11e92d97..2cd2398157 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -2042,6 +2042,11 @@ Parser::MaybeParseExceptionSpecification(SourceRange &SpecificationRange, 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 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification( 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 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification( 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 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification( } 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 10993276c6..68b599bd37 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -1238,6 +1238,16 @@ Parser::TPResult Parser::TryParseFunctionDeclarator() { 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 c9678644cb..3b2a062162 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1552,8 +1552,8 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, 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 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) { 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 2bce585dd2..f6551054c1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -383,6 +383,48 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) { 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()) { + NewType = R->getPointeeType(); + OldType = OldType->getAs()->getPointeeType(); + } else if (const PointerType *P = NewType->getAs()) { + NewType = P->getPointeeType(); + OldType = OldType->getAs()->getPointeeType(); + } else if (const MemberPointerType *M = NewType->getAs()) { + NewType = M->getPointeeType(); + OldType = OldType->getAs()->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(), Old->getLocation(), + NewType->getAs(), 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 @@ namespace { /// 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 ExceptionsSeen; llvm::SmallVector 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(); - + + 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 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( // 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 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( 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 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( 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 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( } 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 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { ExceptSpec.CalledDecl( LookupDestructor(cast(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 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { = 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 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { 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 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( 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 dce4c6e220..01ee712254 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -105,7 +105,8 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { New->getType()->getAs(), New->getLocation(), &MissingExceptionSpecification, - &MissingEmptyExceptionSpecification)) + &MissingEmptyExceptionSpecification, + /*AllowNoexceptAllMatchWithNoSpec=*/true)) return false; // The failure was something other than an empty exception @@ -129,8 +130,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { 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 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { = Old->getType()->getAs(); 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 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { // 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; + } + + 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 << ")"; OS.flush(); SourceLocation FixItLoc; @@ -236,18 +262,17 @@ bool Sema::CheckEquivalentExceptionSpec( 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 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, 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 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, 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 OldTypes, NewTypes; for (FunctionProtoType::exception_iterator I = Old->exception_begin(), @@ -340,19 +448,66 @@ bool Sema::CheckExceptionSpecSubset( 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 25f042c473..a1cd43ac23 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1501,10 +1501,12 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, } 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 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T, FoundAssign = true; const FunctionProtoType *CPT = Operator->getType()->getAs(); - if (!CPT->hasEmptyExceptionSpec()) { + if (!CPT->isNothrow()) { AllNoThrow = false; break; } @@ -2461,9 +2463,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T, FoundConstructor = true; const FunctionProtoType *CPT = Constructor->getType()->getAs(); - // 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 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T, = Constructor->getType()->getAs(); // 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 e60882d6cb..441149f374 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2085,8 +2085,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, const FunctionProtoType *Proto = Tmpl->getType()->getAs(); 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 Exceptions; @@ -2100,7 +2099,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, Unexpanded); assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); - + bool Expand = false; bool RetainExpansion = false; llvm::Optional NumExpansions @@ -2114,7 +2113,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, 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 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, 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 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, Invalid = true; break; } - + Exceptions.push_back(T); } - + if (Invalid) break; - + continue; } @@ -2166,9 +2165,8 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, // 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 62bf8e63ae..f49a49c28f 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1901,7 +1901,22 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, 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 1283525654..b34b62ab6f 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3117,15 +3117,18 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { EPI.Variadic = Record[Idx++]; EPI.TypeQuals = Record[Idx++]; EPI.RefQualifier = static_cast(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 Exceptions; - for (unsigned I = 0; I != EPI.NumExceptions; ++I) - Exceptions.push_back(GetType(Record[Idx++])); - EPI.Exceptions = Exceptions.data(); + ExceptionSpecificationType EST = + static_cast(Record[Idx++]); + EPI.ExceptionSpecType = EST; + if (EST == EST_Dynamic) { + EPI.NumExceptions = Record[Idx++]; + llvm::SmallVector 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 5f830976a8..877650f9de 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -178,11 +178,14 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { Record.push_back(T->isVariadic()); Record.push_back(T->getTypeQuals()); Record.push_back(static_cast(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; } diff --git a/test/CXX/except/except.spec/p1.cpp b/test/CXX/except/except.spec/p1.cpp index b2cec97d78..0559285e77 100644 --- a/test/CXX/except/except.spec/p1.cpp +++ b/test/CXX/except/except.spec/p1.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s // Simple parser tests, dynamic specification. @@ -26,24 +26,35 @@ namespace dyn { namespace noex { - void f() noexcept { } - void g() noexcept (true) { } - void h() noexcept (false) { } - void i() noexcept (1 < 2) { } + void f1() noexcept { } + void f2() noexcept (true) { } + void f3() noexcept (false) { } + void f4() noexcept (1 < 2) { } - class Class { + class CA1 { void foo() noexcept { } void bar() noexcept (true) { } }; - void (*fptr)() noexcept; - void (*gptr)() noexcept (true); + void (*fptr1)() noexcept; + void (*fptr2)() noexcept (true); } -namespace bad { +namespace mix { void f() throw(int) noexcept { } // expected-error {{cannot have both}} void g() noexcept throw(int) { } // expected-error {{cannot have both}} } + +// Sema tests, noexcept specification + +namespace noex { + + struct A {}; + + void g1() noexcept(A()); // expected-error {{not contextually convertible}} + void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}} + +} diff --git a/test/CXX/except/except.spec/p2-dynamic-types.cpp b/test/CXX/except/except.spec/p2-dynamic-types.cpp new file mode 100644 index 0000000000..57f8c3251a --- /dev/null +++ b/test/CXX/except/except.spec/p2-dynamic-types.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s + +// Dynamic specifications: valid types. + +struct Incomplete; // expected-note 3 {{forward declaration}} + +// Exception spec must not have incomplete types, or pointers to them, except +// void. +void ic1() throw(void); // expected-error {{incomplete type 'void' is not allowed in exception specification}} +void ic2() throw(Incomplete); // expected-error {{incomplete type 'Incomplete' is not allowed in exception specification}} +void ic3() throw(void*); +void ic4() throw(Incomplete*); // expected-error {{pointer to incomplete type 'Incomplete' is not allowed in exception specification}} +void ic5() throw(Incomplete&); // expected-error {{reference to incomplete type 'Incomplete' is not allowed in exception specification}} + +// Don't suppress errors in template instantiation. +template struct TEx; // expected-note {{template is declared here}} + +void tf() throw(TEx); // expected-error {{implicit instantiation of undefined template}} + +// DR 437, class throws itself. +struct DR437 { + void f() throw(DR437); + void g() throw(DR437*); + void h() throw(DR437&); +}; + +// DR 437 within a nested class +struct DR437_out { + struct DR437_in { + void f() throw(DR437_out); + void g() throw(DR437_out*); + void h() throw(DR437_out&); + }; +}; diff --git a/test/CXX/except/except.spec/p2-places.cpp b/test/CXX/except/except.spec/p2-places.cpp new file mode 100644 index 0000000000..db1ee77463 --- /dev/null +++ b/test/CXX/except/except.spec/p2-places.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s + +// Tests where specs are allowed and where they aren't. + +namespace dyn { + + // Straight from the standard: + + // Plain function with spec + void f() throw(int); + + // Pointer to function with spec + void (*fp)() throw (int); + + // Function taking reference to function with spec + void g(void pfa() throw(int)); + + // Typedef for pointer to function with spec + typedef int (*pf)() throw(int); // expected-error {{specifications are not allowed in typedefs}} + + // Some more: + + // Function returning function with spec + void (*h())() throw(int); + + // Ultimate parser thrill: function with spec returning function with spec and + // taking pointer to function with spec. + // The actual function throws int, the return type double, the argument float. + void (*i() throw(int))(void (*)() throw(float)) throw(double); + + // Pointer to pointer to function taking function with spec + void (**k)(void pfa() throw(int)); // no-error + + // Pointer to pointer to function with spec + void (**j)() throw(int); // expected-error {{not allowed beyond a single}} + + // Pointer to function returning pointer to pointer to function with spec + void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}} + +} + +namespace noex { + + // These parallel those from above. + + void f() noexcept(false); + + void (*fp)() noexcept(false); + + void g(void pfa() noexcept(false)); + + typedef int (*pf)() noexcept(false); // expected-error {{specifications are not allowed in typedefs}} + + void (*h())() noexcept(false); + + void (*i() noexcept(false))(void (*)() noexcept(true)) noexcept(false); + + void (**k)(void pfa() noexcept(false)); // no-error + + void (**j)() noexcept(false); // expected-error {{not allowed beyond a single}} + + void (**(*h())())() noexcept(false); // expected-error {{not allowed beyond a single}} +} diff --git a/test/CXX/except/except.spec/p3.cpp b/test/CXX/except/except.spec/p3.cpp new file mode 100644 index 0000000000..e751f55ec3 --- /dev/null +++ b/test/CXX/except/except.spec/p3.cpp @@ -0,0 +1,94 @@ +// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s + +// Exception specification compatibility. +// We test function pointers, because functions have an extra rule in p4. + +// Same type is compatible +extern void (*r1)() throw(int); +extern void (*r1)() throw(int); + +// Typedefs don't matter. +typedef int INT; +extern void (*r2)() throw(int); +extern void (*r2)() throw(INT); + +// Order doesn't matter. +extern void (*r3)() throw(int, float); +extern void (*r3)() throw(float, int); + +// MS throw-any spec and no spec at all are compatible +extern void (*r4)(); +extern void (*r4)() throw(...); + +// throw(X) and no spec are not compatible +extern void (*r5)() throw(int); // expected-note {{previous declaration}} +extern void (*r5)(); // expected-error {{exception specification in declaration does not match}} + +// For functions, we accept this with a warning. +extern void f5() throw(int); // expected-note {{previous declaration}} +extern void f5(); // expected-warning {{missing exception specification}} + +// Different types are not compatible. +extern void (*r7)() throw(int); // expected-note {{previous declaration}} +extern void (*r7)() throw(float); // expected-error {{exception specification in declaration does not match}} + +// Top-level const doesn't matter. +extern void (*r8)() throw(int); +extern void (*r8)() throw(const int); + +// Multiple appearances don't matter. +extern void (*r9)() throw(int, int); +extern void (*r9)() throw(int, int); + + +// noexcept is compatible with itself +extern void (*r10)() noexcept; +extern void (*r10)() noexcept; + +// noexcept(true) is compatible with noexcept +extern void (*r11)() noexcept; +extern void (*r11)() noexcept(true); + +// noexcept(false) isn't +extern void (*r12)() noexcept; // expected-note {{previous declaration}} +extern void (*r12)() noexcept(false); // expected-error {{does not match}} + +// The form of the boolean expression doesn't matter. +extern void (*r13)() noexcept(1 < 2); +extern void (*r13)() noexcept(2 > 1); + +// noexcept(false) is incompatible with noexcept(true) +extern void (*r14)() noexcept(true); // expected-note {{previous declaration}} +extern void (*r14)() noexcept(false); // expected-error {{does not match}} + +// noexcept(false) is compatible with itself +extern void (*r15)() noexcept(false); +extern void (*r15)() noexcept(false); + +// noexcept(false) is compatible with MS throw(...) +extern void (*r16)() noexcept(false); +extern void (*r16)() throw(...); + +// noexcept(false) is *not* compatible with no spec +extern void (*r17)(); // expected-note {{previous declaration}} +extern void (*r17)() noexcept(false); // expected-error {{does not match}} + +// except for functions +void f17(); +void f17() noexcept(false); + +// noexcept(false) is compatible with dynamic specs that throw unless +// CWG 1073 resolution is accepted. Clang implements it. +//extern void (*r18)() throw(int); +//extern void (*r18)() noexcept(false); + +// noexcept(true) is compatible with dynamic specs that don't throw +extern void (*r19)() throw(); +extern void (*r19)() noexcept(true); + +// The other way round doesn't work. +extern void (*r20)() throw(); // expected-note {{previous declaration}} +extern void (*r20)() noexcept(false); // expected-error {{does not match}} + +extern void (*r21)() throw(int); // expected-note {{previous declaration}} +extern void (*r21)() noexcept(true); // expected-error {{does not match}} diff --git a/test/CXX/except/except.spec/p5-pointers.cpp b/test/CXX/except/except.spec/p5-pointers.cpp new file mode 100644 index 0000000000..171afff22b --- /dev/null +++ b/test/CXX/except/except.spec/p5-pointers.cpp @@ -0,0 +1,85 @@ +// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s + +// Assignment of function pointers. + +struct A +{ +}; + +struct B1 : A +{ +}; + +struct B2 : A +{ +}; + +struct D : B1, B2 +{ +}; + +struct P : private A +{ +}; + +// Some functions to play with below. +void s1() throw(); +void s2() throw(int); +void s3() throw(A); +void s4() throw(B1); +void s5() throw(D); +void s6(); +void s7() throw(int, float); +void (*s8())() throw(B1); // s8 returns a pointer to function with spec +void s9(void (*)() throw(B1)); // s9 takes pointer to function with spec + +void s10() noexcept; +void s11() noexcept(true); +void s12() noexcept(false); + +void fnptrs() +{ + // Assignment and initialization of function pointers. + void (*t1)() throw() = &s1; // valid + t1 = &s2; // expected-error {{not superset}} expected-error {{incompatible type}} + t1 = &s3; // expected-error {{not superset}} expected-error {{incompatible type}} + void (&t2)() throw() = s2; // expected-error {{not superset}} + void (*t3)() throw(int) = &s2; // valid + void (*t4)() throw(A) = &s1; // valid + t4 = &s3; // valid + t4 = &s4; // valid + t4 = &s5; // expected-error {{not superset}} expected-error {{incompatible type}} + void (*t5)() = &s1; // valid + t5 = &s2; // valid + t5 = &s6; // valid + t5 = &s7; // valid + t1 = t3; // expected-error {{not superset}} expected-error {{incompatible type}} + t3 = t1; // valid + void (*t6)() throw(B1); + t6 = t4; // expected-error {{not superset}} expected-error {{incompatible type}} + t4 = t6; // valid + t5 = t1; // valid + t1 = t5; // expected-error {{not superset}} expected-error {{incompatible type}} + + // return types and arguments must match exactly, no inheritance allowed + void (*(*t7)())() throw(B1) = &s8; // valid + void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}} + void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}} + void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}} + void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}} + void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}} +} + +// Member function stuff + +struct Str1 { void f() throw(int); }; // expected-note {{previous declaration}} +void Str1::f() // expected-warning {{missing exception specification}} +{ +} + +void mfnptr() +{ + void (Str1::*pfn1)() throw(int) = &Str1::f; // valid + void (Str1::*pfn2)() = &Str1::f; // valid + void (Str1::*pfn3)() throw() = &Str1::f; // expected-error {{not superset}} +} diff --git a/test/CXX/except/except.spec/p5-virtual.cpp b/test/CXX/except/except.spec/p5-virtual.cpp new file mode 100644 index 0000000000..ceea9f8f25 --- /dev/null +++ b/test/CXX/except/except.spec/p5-virtual.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s + +// Compatibility of virtual functions. + +struct A +{ +}; + +struct B1 : A +{ +}; + +struct B2 : A +{ +}; + +struct D : B1, B2 +{ +}; + +struct P : private A +{ +}; + +struct Base +{ + virtual void f1() throw(); + virtual void f2() throw(int, float); + + virtual void f3() throw(int, float); + virtual void f4() throw(A); + virtual void f5() throw(A, int, float); + virtual void f6(); + + virtual void f7() noexcept; + virtual void f8() noexcept; + virtual void f9() noexcept(false); + virtual void f10() noexcept(false); + + virtual void f11() throw(); + virtual void f12() noexcept; + virtual void f13() noexcept(false); + virtual void f14() throw(int); + + virtual void f15(); + virtual void f16(); + + virtual void g1() throw(); // expected-note {{overridden virtual function is here}} + virtual void g2() throw(int); // expected-note {{overridden virtual function is here}} + virtual void g3() throw(A); // expected-note {{overridden virtual function is here}} + virtual void g4() throw(B1); // expected-note {{overridden virtual function is here}} + virtual void g5() throw(A); // expected-note {{overridden virtual function is here}} + + virtual void g6() noexcept; // expected-note {{overridden virtual function is here}} + virtual void g7() noexcept; // expected-note {{overridden virtual function is here}} + + virtual void g8() noexcept; // expected-note {{overridden virtual function is here}} + virtual void g9() throw(); // expected-note {{overridden virtual function is here}} + virtual void g10() throw(int); // expected-note {{overridden virtual function is here}} +}; +struct Derived : Base +{ + virtual void f1() throw(); + virtual void f2() throw(float, int); + + virtual void f3() throw(float); + virtual void f4() throw(B1); + virtual void f5() throw(B1, B2, int); + virtual void f6() throw(B2, B2, int, float, char, double, bool); + + virtual void f7() noexcept; + virtual void f8() noexcept(true); + virtual void f9() noexcept(true); + virtual void f10() noexcept(false); + + virtual void f11() noexcept; + virtual void f12() throw(); + virtual void f13() throw(int); + virtual void f14() noexcept(true); + + virtual void f15() noexcept; + virtual void f16() throw(); + + virtual void g1() throw(int); // expected-error {{exception specification of overriding function is more lax}} + virtual void g2(); // expected-error {{exception specification of overriding function is more lax}} + virtual void g3() throw(D); // expected-error {{exception specification of overriding function is more lax}} + virtual void g4() throw(A); // expected-error {{exception specification of overriding function is more lax}} + virtual void g5() throw(P); // expected-error {{exception specification of overriding function is more lax}} + + virtual void g6() noexcept(false); // expected-error {{exception specification of overriding function is more lax}} + virtual void g7(); // expected-error {{exception specification of overriding function is more lax}} + + virtual void g8() throw(int); // expected-error {{exception specification of overriding function is more lax}} + virtual void g9() noexcept(false); // expected-error {{exception specification of overriding function is more lax}} + virtual void g10() noexcept(false); // expected-error {{exception specification of overriding function is more lax}} +}; diff --git a/test/SemaCXX/exception-spec.cpp b/test/SemaCXX/exception-spec.cpp deleted file mode 100644 index 80fd2c3d88..0000000000 --- a/test/SemaCXX/exception-spec.cpp +++ /dev/null @@ -1,193 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -fexceptions -fcxx-exceptions %s - -// Straight from the standard: -// Plain function with spec -void f() throw(int); -// Pointer to function with spec -void (*fp)() throw (int); -// Function taking reference to function with spec -void g(void pfa() throw(int)); -// Typedef for pointer to function with spec -typedef int (*pf)() throw(int); // expected-error {{specifications are not allowed in typedefs}} - -// Some more: -// Function returning function with spec -void (*h())() throw(int); -// Ultimate parser thrill: function with spec returning function with spec and -// taking pointer to function with spec. -// The actual function throws int, the return type double, the argument float. -void (*i() throw(int))(void (*)() throw(float)) throw(double); -// Pointer to pointer to function taking function with spec -void (**k)(void pfa() throw(int)); // no-error -// Pointer to pointer to function with spec -void (**j)() throw(int); // expected-error {{not allowed beyond a single}} -// Pointer to function returning pointer to pointer to function with spec -void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}} - -struct Incomplete; // expected-note 3 {{forward declaration}} - -// Exception spec must not have incomplete types, or pointers to them, except -// void. -void ic1() throw(void); // expected-error {{incomplete type 'void' is not allowed in exception specification}} -void ic2() throw(Incomplete); // expected-error {{incomplete type 'Incomplete' is not allowed in exception specification}} -void ic3() throw(void*); -void ic4() throw(Incomplete*); // expected-error {{pointer to incomplete type 'Incomplete' is not allowed in exception specification}} -void ic5() throw(Incomplete&); // expected-error {{reference to incomplete type 'Incomplete' is not allowed in exception specification}} - -// Redeclarations -typedef int INT; -void r1() throw(int); -void r1() throw(int); - -void r2() throw(int); -void r2() throw(INT); - -// throw-any spec and no spec at all are semantically equivalent -void r4() throw(int, float); -void r4() throw(float, int); - -void r5() throw(int); // expected-note {{previous declaration}} -void r5(); // expected-warning {{missing exception specification}} - -void r7() throw(int); // expected-note {{previous declaration}} -void r7() throw(float); // expected-error {{exception specification in declaration does not match}} - -// Top-level const doesn't matter. -void r8() throw(int); -void r8() throw(const int); - -// Multiple appearances don't matter. -void r9() throw(int, int); -void r9() throw(int, int); - -struct A -{ -}; - -struct B1 : A -{ -}; - -struct B2 : A -{ -}; - -struct D : B1, B2 -{ -}; - -struct P : private A -{ -}; - -struct Base -{ - virtual void f1() throw(); - virtual void f4() throw(int, float); - - virtual void f5() throw(int, float); - virtual void f6() throw(A); - virtual void f7() throw(A, int, float); - virtual void f8(); - - virtual void g1() throw(); // expected-note {{overridden virtual function is here}} - virtual void g2() throw(int); // expected-note {{overridden virtual function is here}} - virtual void g3() throw(A); // expected-note {{overridden virtual function is here}} - virtual void g4() throw(B1); // expected-note {{overridden virtual function is here}} - virtual void g5() throw(A); // expected-note {{overridden virtual function is here}} -}; -struct Derived : Base -{ - virtual void f1() throw(); - virtual void f4() throw(float, int); - - virtual void f5() throw(float); - virtual void f6() throw(B1); - virtual void f7() throw(B1, B2, int); - virtual void f8() throw(B2, B2, int, float, char, double, bool); - - virtual void g1() throw(int); // expected-error {{exception specification of overriding function is more lax}} - virtual void g2(); // expected-error {{exception specification of overriding function is more lax}} - virtual void g3() throw(D); // expected-error {{exception specification of overriding function is more lax}} - virtual void g4() throw(A); // expected-error {{exception specification of overriding function is more lax}} - virtual void g5() throw(P); // expected-error {{exception specification of overriding function is more lax}} -}; - -// Some functions to play with below. -void s1() throw(); -void s2() throw(int); -void s3() throw(A); -void s4() throw(B1); -void s5() throw(D); -void s6(); -void s7() throw(int, float); -void (*s8())() throw(B1); // s8 returns a pointer to function with spec -void s9(void (*)() throw(B1)); // s9 takes pointer to function with spec - -void fnptrs() -{ - // Assignment and initialization of function pointers. - void (*t1)() throw() = &s1; // valid - t1 = &s2; // expected-error {{not superset}} expected-error {{incompatible type}} - t1 = &s3; // expected-error {{not superset}} expected-error {{incompatible type}} - void (&t2)() throw() = s2; // expected-error {{not superset}} - void (*t3)() throw(int) = &s2; // valid - void (*t4)() throw(A) = &s1; // valid - t4 = &s3; // valid - t4 = &s4; // valid - t4 = &s5; // expected-error {{not superset}} expected-error {{incompatible type}} - void (*t5)() = &s1; // valid - t5 = &s2; // valid - t5 = &s6; // valid - t5 = &s7; // valid - t1 = t3; // expected-error {{not superset}} expected-error {{incompatible type}} - t3 = t1; // valid - void (*t6)() throw(B1); - t6 = t4; // expected-error {{not superset}} expected-error {{incompatible type}} - t4 = t6; // valid - t5 = t1; // valid - t1 = t5; // expected-error {{not superset}} expected-error {{incompatible type}} - - // return types and arguments must match exactly, no inheritance allowed - void (*(*t7)())() throw(B1) = &s8; // valid - void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}} - void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}} - void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}} - void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}} - void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}} -} - -// Member function stuff - -struct Str1 { void f() throw(int); }; // expected-note {{previous declaration}} -void Str1::f() // expected-warning {{missing exception specification}} -{ -} - -void mfnptr() -{ - void (Str1::*pfn1)() throw(int) = &Str1::f; // valid - void (Str1::*pfn2)() = &Str1::f; // valid - void (Str1::*pfn3)() throw() = &Str1::f; // expected-error {{not superset}} -} - -// Don't suppress errors in template instantiation. -template struct TEx; // expected-note {{template is declared here}} - -void tf() throw(TEx); // expected-error {{implicit instantiation of undefined template}} - -// DR 437, class throws itself. -struct DR437 { - void f() throw(DR437); - void g() throw(DR437*); - void h() throw(DR437&); -}; - -// DR 437 within a nested class -struct DR437_out { - struct DR437_in { - void f() throw(DR437_out); - void g() throw(DR437_out*); - void h() throw(DR437_out&); - }; -};