From e6975e9b0985ad7f7ff9187e38d95bfe9ac4181b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 17 Apr 2012 00:58:00 +0000 Subject: Implement DR1330 in C++11 mode, to support libstdc++4.7 which uses it. We have a new flavor of exception specification, EST_Uninstantiated. A function type with this exception specification carries a pointer to a FunctionDecl, and the exception specification for that FunctionDecl is instantiated (if needed) and used in the place of the function type's exception specification. When a function template declaration with a non-trivial exception specification is instantiated, the specialization's exception specification is set to this new 'uninstantiated' kind rather than being instantiated immediately. Expr::CanThrow has migrated onto Sema, so it can instantiate exception specs on-demand. Also, any odr-use of a function triggers the instantiation of its exception specification (the exception specification could be needed by IRGen). In passing, fix two places where a DeclRefExpr was created but the corresponding function was not actually marked odr-used. We used to get away with this, but don't any more. Also fix a bug where instantiating an exception specification which refers to function parameters resulted in a crash. We still have the same bug in default arguments, which I'll be looking into next. This, plus a tiny patch to fix libstdc++'s common_type, is enough for clang to parse (and, in very limited testing, support) all of libstdc++4.7's standard headers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154886 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 10 - include/clang/AST/Type.h | 18 +- include/clang/Basic/DiagnosticSemaKinds.td | 2 + include/clang/Basic/ExceptionSpecificationType.h | 18 +- include/clang/Sema/Sema.h | 29 +- lib/AST/ASTContext.cpp | 2 + lib/AST/Expr.cpp | 325 ------------------ lib/AST/Type.cpp | 9 + lib/Analysis/CFG.cpp | 3 +- lib/Sema/SemaDeclCXX.cpp | 76 +++-- lib/Sema/SemaExceptionSpec.cpp | 359 +++++++++++++++++++- lib/Sema/SemaExpr.cpp | 7 + lib/Sema/SemaExprCXX.cpp | 13 +- lib/Sema/SemaOverload.cpp | 2 + lib/Sema/SemaTemplateInstantiate.cpp | 32 ++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 362 ++++++++++++--------- lib/Sema/TreeTransform.h | 7 +- test/CXX/except/except.spec/p1.cpp | 4 +- test/CodeGenCXX/cxx11-exception-spec.cpp | 18 + test/SemaTemplate/instantiate-declref.cpp | 10 + .../instantiate-exception-spec-cxx11.cpp | 107 ++++++ 21 files changed, 872 insertions(+), 541 deletions(-) create mode 100644 test/CodeGenCXX/cxx11-exception-spec.cpp create mode 100644 test/SemaTemplate/instantiate-exception-spec-cxx11.cpp diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index a7822fab1c..b0b9b0fd6f 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -581,16 +581,6 @@ public: /// member expression. static QualType findBoundMemberType(const Expr *expr); - /// \brief Result type of CanThrow(). - enum CanThrowResult { - CT_Cannot, - CT_Dependent, - CT_Can - }; - /// \brief Test if this expression, if evaluated, might throw, according to - /// the rules of C++ [expr.unary.noexcept]. - CanThrowResult CanThrow(ASTContext &C) const; - /// IgnoreImpCasts - Skip past any implicit casts which might /// surround this expression. Only skips ImplicitCastExprs. Expr *IgnoreImpCasts() LLVM_READONLY; diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 7bd367c054..9fd4901672 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -79,6 +79,7 @@ namespace clang { class CXXRecordDecl; class EnumDecl; class FieldDecl; + class FunctionDecl; class ObjCInterfaceDecl; class ObjCProtocolDecl; class ObjCMethodDecl; @@ -2700,7 +2701,8 @@ public: ExtProtoInfo() : Variadic(false), HasTrailingReturn(false), TypeQuals(0), ExceptionSpecType(EST_None), RefQualifier(RQ_None), - NumExceptions(0), Exceptions(0), NoexceptExpr(0), ConsumedArguments(0) {} + NumExceptions(0), Exceptions(0), NoexceptExpr(0), ExceptionSpecDecl(0), + ConsumedArguments(0) {} FunctionType::ExtInfo ExtInfo; bool Variadic : 1; @@ -2711,6 +2713,7 @@ public: unsigned NumExceptions; const QualType *Exceptions; Expr *NoexceptExpr; + FunctionDecl *ExceptionSpecDecl; const bool *ConsumedArguments; }; @@ -2756,6 +2759,10 @@ private: // NoexceptExpr - Instead of Exceptions, there may be a single Expr* pointing // to the expression in the noexcept() specifier. + // ExceptionSpecDecl - Instead of Exceptions, there may be a single + // FunctionDecl* pointing to the function which should be used to resolve + // this function type's exception specification. + // ConsumedArgs - A variable size array, following Exceptions // and of length NumArgs, holding flags indicating which arguments // are consumed. This only appears if HasAnyConsumedArgs is true. @@ -2795,6 +2802,8 @@ public: EPI.Exceptions = exception_begin(); } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { EPI.NoexceptExpr = getNoexceptExpr(); + } else if (EPI.ExceptionSpecType == EST_Uninstantiated) { + EPI.ExceptionSpecDecl = getExceptionSpecDecl(); } if (hasAnyConsumedArgs()) EPI.ConsumedArguments = getConsumedArgsBuffer(); @@ -2838,9 +2847,14 @@ public: // NoexceptExpr sits where the arguments end. return *reinterpret_cast(arg_type_end()); } + FunctionDecl *getExceptionSpecDecl() const { + if (getExceptionSpecType() != EST_Uninstantiated) + return 0; + return *reinterpret_cast(arg_type_end()); + } bool isNothrow(ASTContext &Ctx) const { ExceptionSpecificationType EST = getExceptionSpecType(); - assert(EST != EST_Delayed); + assert(EST != EST_Delayed && EST != EST_Uninstantiated); if (EST == EST_DynamicNone || EST == EST_BasicNoexcept) return true; if (EST != EST_ComputedNoexcept) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index baaddcfeb2..779af403a6 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2583,6 +2583,8 @@ def note_template_enum_def_here : Note< "in instantiation of enumeration %q0 requested here">; def note_template_type_alias_instantiation_here : Note< "in instantiation of template type alias %0 requested here">; +def note_template_exception_spec_instantiation_here : Note< + "in instantiation of exception specification for %0 requested here">; def note_default_arg_instantiation_here : Note< "in instantiation of default argument for '%0' required here">; diff --git a/include/clang/Basic/ExceptionSpecificationType.h b/include/clang/Basic/ExceptionSpecificationType.h index 98cfd297ad..e911bde191 100644 --- a/include/clang/Basic/ExceptionSpecificationType.h +++ b/include/clang/Basic/ExceptionSpecificationType.h @@ -16,7 +16,7 @@ namespace clang { -/// \brief The various types of exception specifications that exist in C++0x. +/// \brief The various types of exception specifications that exist in C++11. enum ExceptionSpecificationType { EST_None, ///< no exception specification EST_DynamicNone, ///< throw() @@ -24,7 +24,8 @@ enum ExceptionSpecificationType { EST_MSAny, ///< Microsoft throw(...) extension EST_BasicNoexcept, ///< noexcept EST_ComputedNoexcept, ///< noexcept(expression) - EST_Delayed ///< not known yet + EST_Delayed, ///< not known yet + EST_Uninstantiated ///< not instantiated yet }; inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) { @@ -35,6 +36,19 @@ inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) { return ESpecType == EST_BasicNoexcept || ESpecType == EST_ComputedNoexcept; } +/// \brief Possible results from evaluation of a noexcept expression. +enum CanThrowResult { + CT_Cannot, + CT_Dependent, + CT_Can +}; + +inline CanThrowResult mergeCanThrow(CanThrowResult CT1, CanThrowResult CT2) { + // CanThrowResult constants are ordered so that the maximum is the correct + // merge result. + return CT1 > CT2 ? CT1 : CT2; +} + } // end namespace clang #endif // LLVM_CLANG_BASIC_EXCEPTIONSPECIFICATIONTYPE_H diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 7de59e0f5d..c845ee5549 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -907,6 +907,9 @@ public: DeclarationNameInfo GetNameForDeclarator(Declarator &D); DeclarationNameInfo GetNameFromUnqualifiedId(const UnqualifiedId &Name); static QualType GetTypeFromParser(ParsedType Ty, TypeSourceInfo **TInfo = 0); + CanThrowResult canThrow(const Expr *E); + const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc, + const FunctionProtoType *FPT); bool CheckSpecifiedExceptionType(QualType T, const SourceRange &Range); bool CheckDistantExceptionSpec(QualType T); bool CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New); @@ -3050,7 +3053,7 @@ public: /// implicitly-declared special member functions. class ImplicitExceptionSpecification { // Pointer to allow copying - ASTContext *Context; + Sema *Self; // We order exception specifications thus: // noexcept is the most restrictive, but is only used in C++0x. // throw() comes next. @@ -3074,9 +3077,9 @@ public: } public: - explicit ImplicitExceptionSpecification(ASTContext &Context) - : Context(&Context), ComputedEST(EST_BasicNoexcept) { - if (!Context.getLangOpts().CPlusPlus0x) + explicit ImplicitExceptionSpecification(Sema &Self) + : Self(&Self), ComputedEST(EST_BasicNoexcept) { + if (!Self.Context.getLangOpts().CPlusPlus0x) ComputedEST = EST_DynamicNone; } @@ -3094,7 +3097,7 @@ public: const QualType *data() const { return Exceptions.data(); } /// \brief Integrate another called method into the collected data. - void CalledDecl(CXXMethodDecl *Method); + void CalledDecl(SourceLocation CallLoc, CXXMethodDecl *Method); /// \brief Integrate an invoked expression into the collected data. void CalledExpr(Expr *E); @@ -5194,7 +5197,11 @@ public: /// We are checking the validity of a default template argument that /// has been used when naming a template-id. - DefaultTemplateArgumentChecking + DefaultTemplateArgumentChecking, + + /// We are instantiating the exception specification for a function + /// template which was deferred until it was needed. + ExceptionSpecInstantiation } Kind; /// \brief The point of instantiation within the source code. @@ -5242,6 +5249,7 @@ public: switch (X.Kind) { case TemplateInstantiation: + case ExceptionSpecInstantiation: return true; case PriorTemplateArgumentSubstitution: @@ -5359,6 +5367,13 @@ public: Decl *Entity, SourceRange InstantiationRange = SourceRange()); + struct ExceptionSpecification {}; + /// \brief Note that we are instantiating an exception specification + /// of a function template. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + FunctionDecl *Entity, ExceptionSpecification, + SourceRange InstantiationRange = SourceRange()); + /// \brief Note that we are instantiating a default argument in a /// template-id. InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, @@ -5658,6 +5673,8 @@ public: TemplateArgumentListInfo &Result, const MultiLevelTemplateArgumentList &TemplateArgs); + void InstantiateExceptionSpec(SourceLocation PointOfInstantiation, + FunctionDecl *Function); void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive = false, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 887beac4b0..65ddc059fa 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2194,6 +2194,8 @@ ASTContext::getFunctionType(QualType ResultTy, Size += EPI.NumExceptions * sizeof(QualType); else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { Size += sizeof(Expr*); + } else if (EPI.ExceptionSpecType == EST_Uninstantiated) { + Size += sizeof(FunctionDecl*); } if (EPI.ConsumedArguments) Size += NumArgs * sizeof(bool); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 9556b1acb9..fcde5429b3 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1996,331 +1996,6 @@ QualType Expr::findBoundMemberType(const Expr *expr) { return QualType(); } -static Expr::CanThrowResult MergeCanThrow(Expr::CanThrowResult CT1, - Expr::CanThrowResult CT2) { - // CanThrowResult constants are ordered so that the maximum is the correct - // merge result. - return CT1 > CT2 ? CT1 : CT2; -} - -static Expr::CanThrowResult CanSubExprsThrow(ASTContext &C, const Expr *CE) { - Expr *E = const_cast(CE); - Expr::CanThrowResult R = Expr::CT_Cannot; - for (Expr::child_range I = E->children(); I && R != Expr::CT_Can; ++I) { - R = MergeCanThrow(R, cast(*I)->CanThrow(C)); - } - return R; -} - -static Expr::CanThrowResult CanCalleeThrow(ASTContext &Ctx, const Expr *E, - const Decl *D, - bool NullThrows = true) { - if (!D) - return NullThrows ? Expr::CT_Can : Expr::CT_Cannot; - - // See if we can get a function type from the decl somehow. - const ValueDecl *VD = dyn_cast(D); - if (!VD) // If we have no clue what we're calling, assume the worst. - return Expr::CT_Can; - - // As an extension, we assume that __attribute__((nothrow)) functions don't - // throw. - if (isa(D) && D->hasAttr()) - return Expr::CT_Cannot; - - QualType T = VD->getType(); - const FunctionProtoType *FT; - if ((FT = T->getAs())) { - } else if (const PointerType *PT = T->getAs()) - FT = PT->getPointeeType()->getAs(); - else if (const ReferenceType *RT = T->getAs()) - FT = RT->getPointeeType()->getAs(); - else if (const MemberPointerType *MT = T->getAs()) - FT = MT->getPointeeType()->getAs(); - else if (const BlockPointerType *BT = T->getAs()) - FT = BT->getPointeeType()->getAs(); - - if (!FT) - return Expr::CT_Can; - - if (FT->getExceptionSpecType() == EST_Delayed) { - assert(isa(D) && - "only constructor exception specs can be unknown"); - Ctx.getDiagnostics().Report(E->getLocStart(), - diag::err_exception_spec_unknown) - << E->getSourceRange(); - return Expr::CT_Can; - } - - return FT->isNothrow(Ctx) ? Expr::CT_Cannot : Expr::CT_Can; -} - -static Expr::CanThrowResult CanDynamicCastThrow(const CXXDynamicCastExpr *DC) { - if (DC->isTypeDependent()) - return Expr::CT_Dependent; - - if (!DC->getTypeAsWritten()->isReferenceType()) - return Expr::CT_Cannot; - - if (DC->getSubExpr()->isTypeDependent()) - return Expr::CT_Dependent; - - return DC->getCastKind() == clang::CK_Dynamic? Expr::CT_Can : Expr::CT_Cannot; -} - -static Expr::CanThrowResult CanTypeidThrow(ASTContext &C, - const CXXTypeidExpr *DC) { - if (DC->isTypeOperand()) - return Expr::CT_Cannot; - - Expr *Op = DC->getExprOperand(); - if (Op->isTypeDependent()) - return Expr::CT_Dependent; - - const RecordType *RT = Op->getType()->getAs(); - if (!RT) - return Expr::CT_Cannot; - - if (!cast(RT->getDecl())->isPolymorphic()) - return Expr::CT_Cannot; - - if (Op->Classify(C).isPRValue()) - return Expr::CT_Cannot; - - return Expr::CT_Can; -} - -Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { - // C++ [expr.unary.noexcept]p3: - // [Can throw] if in a potentially-evaluated context the expression would - // contain: - switch (getStmtClass()) { - case CXXThrowExprClass: - // - a potentially evaluated throw-expression - return CT_Can; - - case CXXDynamicCastExprClass: { - // - a potentially evaluated dynamic_cast expression dynamic_cast(v), - // where T is a reference type, that requires a run-time check - CanThrowResult CT = CanDynamicCastThrow(cast(this)); - if (CT == CT_Can) - return CT; - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - case CXXTypeidExprClass: - // - a potentially evaluated typeid expression applied to a glvalue - // expression whose type is a polymorphic class type - return CanTypeidThrow(C, cast(this)); - - // - a potentially evaluated call to a function, member function, function - // pointer, or member function pointer that does not have a non-throwing - // exception-specification - case CallExprClass: - case CXXMemberCallExprClass: - case CXXOperatorCallExprClass: - case UserDefinedLiteralClass: { - const CallExpr *CE = cast(this); - CanThrowResult CT; - if (isTypeDependent()) - CT = CT_Dependent; - else if (isa(CE->getCallee()->IgnoreParens())) - CT = CT_Cannot; - else - CT = CanCalleeThrow(C, this, CE->getCalleeDecl()); - if (CT == CT_Can) - return CT; - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - case CXXConstructExprClass: - case CXXTemporaryObjectExprClass: { - CanThrowResult CT = CanCalleeThrow(C, this, - cast(this)->getConstructor()); - if (CT == CT_Can) - return CT; - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - case LambdaExprClass: { - const LambdaExpr *Lambda = cast(this); - CanThrowResult CT = Expr::CT_Cannot; - for (LambdaExpr::capture_init_iterator Cap = Lambda->capture_init_begin(), - CapEnd = Lambda->capture_init_end(); - Cap != CapEnd; ++Cap) - CT = MergeCanThrow(CT, (*Cap)->CanThrow(C)); - return CT; - } - - case CXXNewExprClass: { - CanThrowResult CT; - if (isTypeDependent()) - CT = CT_Dependent; - else - CT = CanCalleeThrow(C, this, cast(this)->getOperatorNew()); - if (CT == CT_Can) - return CT; - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - case CXXDeleteExprClass: { - CanThrowResult CT; - QualType DTy = cast(this)->getDestroyedType(); - if (DTy.isNull() || DTy->isDependentType()) { - CT = CT_Dependent; - } else { - CT = CanCalleeThrow(C, this, - cast(this)->getOperatorDelete()); - if (const RecordType *RT = DTy->getAs()) { - const CXXRecordDecl *RD = cast(RT->getDecl()); - CT = MergeCanThrow(CT, CanCalleeThrow(C, this, RD->getDestructor())); - } - if (CT == CT_Can) - return CT; - } - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - case CXXBindTemporaryExprClass: { - // The bound temporary has to be destroyed again, which might throw. - CanThrowResult CT = CanCalleeThrow(C, this, - cast(this)->getTemporary()->getDestructor()); - if (CT == CT_Can) - return CT; - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - // ObjC message sends are like function calls, but never have exception - // specs. - case ObjCMessageExprClass: - case ObjCPropertyRefExprClass: - case ObjCSubscriptRefExprClass: - return CT_Can; - - // All the ObjC literals that are implemented as calls are - // potentially throwing unless we decide to close off that - // possibility. - case ObjCArrayLiteralClass: - case ObjCDictionaryLiteralClass: - case ObjCNumericLiteralClass: - return CT_Can; - - // Many other things have subexpressions, so we have to test those. - // Some are simple: - case ConditionalOperatorClass: - case CompoundLiteralExprClass: - case CXXConstCastExprClass: - case CXXDefaultArgExprClass: - case CXXReinterpretCastExprClass: - case DesignatedInitExprClass: - case ExprWithCleanupsClass: - case ExtVectorElementExprClass: - case InitListExprClass: - case MemberExprClass: - case ObjCIsaExprClass: - case ObjCIvarRefExprClass: - case ParenExprClass: - case ParenListExprClass: - case ShuffleVectorExprClass: - case VAArgExprClass: - return CanSubExprsThrow(C, this); - - // Some might be dependent for other reasons. - case ArraySubscriptExprClass: - case BinaryOperatorClass: - case CompoundAssignOperatorClass: - case CStyleCastExprClass: - case CXXStaticCastExprClass: - case CXXFunctionalCastExprClass: - case ImplicitCastExprClass: - case MaterializeTemporaryExprClass: - case UnaryOperatorClass: { - CanThrowResult CT = isTypeDependent() ? CT_Dependent : CT_Cannot; - return MergeCanThrow(CT, CanSubExprsThrow(C, this)); - } - - // FIXME: We should handle StmtExpr, but that opens a MASSIVE can of worms. - case StmtExprClass: - return CT_Can; - - case ChooseExprClass: - if (isTypeDependent() || isValueDependent()) - return CT_Dependent; - return cast(this)->getChosenSubExpr(C)->CanThrow(C); - - case GenericSelectionExprClass: - if (cast(this)->isResultDependent()) - return CT_Dependent; - return cast(this)->getResultExpr()->CanThrow(C); - - // Some expressions are always dependent. - case CXXDependentScopeMemberExprClass: - case CXXUnresolvedConstructExprClass: - case DependentScopeDeclRefExprClass: - return CT_Dependent; - - case AtomicExprClass: - case AsTypeExprClass: - case BinaryConditionalOperatorClass: - case BlockExprClass: - case CUDAKernelCallExprClass: - case DeclRefExprClass: - case ObjCBridgedCastExprClass: - case ObjCIndirectCopyRestoreExprClass: - case ObjCProtocolExprClass: - case ObjCSelectorExprClass: - case OffsetOfExprClass: - case PackExpansionExprClass: - case PseudoObjectExprClass: - case SubstNonTypeTemplateParmExprClass: - case SubstNonTypeTemplateParmPackExprClass: - case UnaryExprOrTypeTraitExprClass: - case UnresolvedLookupExprClass: - case UnresolvedMemberExprClass: - // FIXME: Can any of the above throw? If so, when? - return CT_Cannot; - - case AddrLabelExprClass: - case ArrayTypeTraitExprClass: - case BinaryTypeTraitExprClass: - case TypeTraitExprClass: - case CXXBoolLiteralExprClass: - case CXXNoexceptExprClass: - case CXXNullPtrLiteralExprClass: - case CXXPseudoDestructorExprClass: - case CXXScalarValueInitExprClass: - case CXXThisExprClass: - case CXXUuidofExprClass: - case CharacterLiteralClass: - case ExpressionTraitExprClass: - case FloatingLiteralClass: - case GNUNullExprClass: - case ImaginaryLiteralClass: - case ImplicitValueInitExprClass: - case IntegerLiteralClass: - case ObjCEncodeExprClass: - case ObjCStringLiteralClass: - case ObjCBoolLiteralExprClass: - case OpaqueValueExprClass: - case PredefinedExprClass: - case SizeOfPackExprClass: - case StringLiteralClass: - case UnaryTypeTraitExprClass: - // These expressions can never throw. - return CT_Cannot; - -#define STMT(CLASS, PARENT) case CLASS##Class: -#define STMT_RANGE(Base, First, Last) -#define LAST_STMT_RANGE(BASE, FIRST, LAST) -#define EXPR(CLASS, PARENT) -#define ABSTRACT_STMT(STMT) -#include "clang/AST/StmtNodes.inc" - case NoStmtClass: - llvm_unreachable("Invalid class for expression"); - } - llvm_unreachable("Bogus StmtClass"); -} - Expr* Expr::IgnoreParens() { Expr* E = this; while (true) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index c82aeaadd4..10c1adc875 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1546,6 +1546,13 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, else if (epi.NoexceptExpr->isInstantiationDependent()) setInstantiationDependent(); } + } else if (getExceptionSpecType() == EST_Uninstantiated) { + // Store the function decl from which we will resolve our + // exception specification. + FunctionDecl **slot = reinterpret_cast(argSlot + numArgs); + *slot = epi.ExceptionSpecDecl; + // This exception specification doesn't make the type dependent, because + // it's not instantiated as part of instantiating the type. } if (epi.ConsumedArguments) { @@ -1629,6 +1636,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr()); } else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){ epi.NoexceptExpr->Profile(ID, Context, false); + } else if (epi.ExceptionSpecType == EST_Uninstantiated) { + ID.AddPointer(epi.ExceptionSpecDecl->getCanonicalDecl()); } if (epi.ConsumedArguments) { for (unsigned i = 0; i != NumArgs; ++i) diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index f4d9a354a5..2f1f1cb4e4 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -1284,7 +1284,8 @@ static bool CanThrow(Expr *E, ASTContext &Ctx) { const FunctionType *FT = Ty->getAs(); if (FT) { if (const FunctionProtoType *Proto = dyn_cast(FT)) - if (Proto->isNothrow(Ctx)) + if (Proto->getExceptionSpecType() != EST_Uninstantiated && + Proto->isNothrow(Ctx)) return false; } return true; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 8fe28e9555..f0f52c7546 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -125,14 +125,17 @@ namespace { } } -void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) { - assert(Context && "ImplicitExceptionSpecification without an ASTContext"); +void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, + CXXMethodDecl *Method) { // If we have an MSAny or unknown spec already, don't bother. if (!Method || ComputedEST == EST_MSAny || ComputedEST == EST_Delayed) return; const FunctionProtoType *Proto = Method->getType()->getAs(); + Proto = Self->ResolveExceptionSpec(CallLoc, Proto); + if (!Proto) + return; ExceptionSpecificationType EST = Proto->getExceptionSpecType(); @@ -164,7 +167,8 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) { // Check out noexcept specs. if (EST == EST_ComputedNoexcept) { - FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec(*Context); + FunctionProtoType::NoexceptResult NR = + Proto->getNoexceptSpec(Self->Context); assert(NR != FunctionProtoType::NR_NoNoexcept && "Must have noexcept result for EST_ComputedNoexcept."); assert(NR != FunctionProtoType::NR_Dependent && @@ -188,7 +192,7 @@ void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) { for (FunctionProtoType::exception_iterator E = Proto->exception_begin(), EEnd = Proto->exception_end(); E != EEnd; ++E) - if (ExceptionsSeen.insert(Context->getCanonicalType(*E))) + if (ExceptionsSeen.insert(Self->Context.getCanonicalType(*E))) Exceptions.push_back(*E); } @@ -217,7 +221,7 @@ void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) { // implicit definition. For now, we assume that any non-nothrow expression can // throw any exception. - if (E->CanThrow(*Context)) + if (Self->canThrow(E)) ComputedEST = EST_None; } @@ -3922,7 +3926,7 @@ void Sema::CheckExplicitlyDefaultedCopyConstructor(CXXConstructorDecl *CD) { HadError = true; } - ImplicitExceptionSpecification Spec(Context); + ImplicitExceptionSpecification Spec(*this); bool Const; llvm::tie(Spec, Const) = ComputeDefaultedCopyCtorExceptionSpecAndConst(CD->getParent()); @@ -4031,7 +4035,7 @@ void Sema::CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *MD) { HadError = true; } - ImplicitExceptionSpecification Spec(Context); + ImplicitExceptionSpecification Spec(*this); bool Const; llvm::tie(Spec, Const) = ComputeDefaultedCopyCtorExceptionSpecAndConst(MD->getParent()); @@ -6814,7 +6818,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] - ImplicitExceptionSpecification ExceptSpec(Context); + ImplicitExceptionSpecification ExceptSpec(*this); if (ClassDecl->isInvalidDecl()) return ExceptSpec; @@ -6831,7 +6835,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. if (Constructor) - ExceptSpec.CalledDecl(Constructor); + ExceptSpec.CalledDecl(B->getLocStart(), Constructor); } } @@ -6845,7 +6849,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. if (Constructor) - ExceptSpec.CalledDecl(Constructor); + ExceptSpec.CalledDecl(B->getLocStart(), Constructor); } } @@ -6868,7 +6872,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // might just be ill-formed because this function attempts to refer to // a deleted function here. if (Constructor) - ExceptSpec.CalledDecl(Constructor); + ExceptSpec.CalledDecl(F->getLocation(), Constructor); } } @@ -6990,6 +6994,7 @@ void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { const FunctionProtoType *CtorTy = CtorDecl->getType()->castAs(); if (CtorTy->getExceptionSpecType() == EST_Delayed) { + // FIXME: Don't do this unless the exception spec is needed. ImplicitExceptionSpecification Spec = ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl); FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); @@ -7190,7 +7195,7 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have // an exception-specification. - ImplicitExceptionSpecification ExceptSpec(Context); + ImplicitExceptionSpecification ExceptSpec(*this); if (ClassDecl->isInvalidDecl()) return ExceptSpec; @@ -7202,7 +7207,7 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { continue; if (const RecordType *BaseType = B->getType()->getAs()) - ExceptSpec.CalledDecl( + ExceptSpec.CalledDecl(B->getLocStart(), LookupDestructor(cast(BaseType->getDecl()))); } @@ -7211,7 +7216,7 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { BEnd = ClassDecl->vbases_end(); B != BEnd; ++B) { if (const RecordType *BaseType = B->getType()->getAs()) - ExceptSpec.CalledDecl( + ExceptSpec.CalledDecl(B->getLocStart(), LookupDestructor(cast(BaseType->getDecl()))); } @@ -7221,7 +7226,7 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { F != FEnd; ++F) { if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) - ExceptSpec.CalledDecl( + ExceptSpec.CalledDecl(F->getLocation(), LookupDestructor(cast(RecordTy->getDecl()))); } @@ -7546,7 +7551,7 @@ std::pair Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( CXXRecordDecl *ClassDecl) { if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(Context), false); + return std::make_pair(ImplicitExceptionSpecification(*this), false); // C++ [class.copy]p10: // If the class definition does not explicitly declare a copy @@ -7619,7 +7624,7 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // Based on a similar decision made for constness in C++0x, we're erring on // the side of assuming such calls to be made regardless of whether they // actually happen. - ImplicitExceptionSpecification ExceptSpec(Context); + ImplicitExceptionSpecification ExceptSpec(*this); unsigned ArgQuals = HasConstCopyAssignment ? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); @@ -7631,7 +7636,7 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( = cast(Base->getType()->getAs()->getDecl()); if (CXXMethodDecl *CopyAssign = LookupCopyingAssignment(BaseClassDecl, ArgQuals, false, 0)) - ExceptSpec.CalledDecl(CopyAssign); + ExceptSpec.CalledDecl(Base->getLocStart(), CopyAssign); } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), @@ -7641,7 +7646,7 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( = cast(Base->getType()->getAs()->getDecl()); if (CXXMethodDecl *CopyAssign = LookupCopyingAssignment(BaseClassDecl, ArgQuals, false, 0)) - ExceptSpec.CalledDecl(CopyAssign); + ExceptSpec.CalledDecl(Base->getLocStart(), CopyAssign); } for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), @@ -7652,7 +7657,7 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { if (CXXMethodDecl *CopyAssign = LookupCopyingAssignment(FieldClassDecl, ArgQuals, false, 0)) - ExceptSpec.CalledDecl(CopyAssign); + ExceptSpec.CalledDecl(Field->getLocation(), CopyAssign); } } @@ -7665,7 +7670,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { // for determining the argument type of the operator. Note also that // operators taking an object instead of a reference are allowed. - ImplicitExceptionSpecification Spec(Context); + ImplicitExceptionSpecification Spec(*this); bool Const; llvm::tie(Spec, Const) = ComputeDefaultedCopyAssignmentExceptionSpecAndConst(ClassDecl); @@ -8032,7 +8037,7 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, Sema::ImplicitExceptionSpecification Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { - ImplicitExceptionSpecification ExceptSpec(Context); + ImplicitExceptionSpecification ExceptSpec(*this); if (ClassDecl->isInvalidDecl()) return ExceptSpec; @@ -8059,7 +8064,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { = cast(Base->getType()->getAs()->getDecl()); if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl, false, 0)) - ExceptSpec.CalledDecl(MoveAssign); + ExceptSpec.CalledDecl(Base->getLocStart(), MoveAssign); } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), @@ -8069,7 +8074,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { = cast(Base->getType()->getAs()->getDecl()); if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(BaseClassDecl, false, 0)) - ExceptSpec.CalledDecl(MoveAssign); + ExceptSpec.CalledDecl(Base->getLocStart(), MoveAssign); } for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), @@ -8080,7 +8085,7 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXRecordDecl *ClassDecl) { if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { if (CXXMethodDecl *MoveAssign = LookupMovingAssignment(FieldClassDecl, false, 0)) - ExceptSpec.CalledDecl(MoveAssign); + ExceptSpec.CalledDecl(Field->getLocation(), MoveAssign); } } @@ -8578,7 +8583,7 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, std::pair Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { if (ClassDecl->isInvalidDecl()) - return std::make_pair(ImplicitExceptionSpecification(Context), false); + return std::make_pair(ImplicitExceptionSpecification(*this), false); // C++ [class.copy]p5: // The implicitly-declared copy constructor for a class X will @@ -8639,7 +8644,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] - ImplicitExceptionSpecification ExceptSpec(Context); + ImplicitExceptionSpecification ExceptSpec(*this); unsigned Quals = HasConstCopyConstructor? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); @@ -8653,7 +8658,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { = cast(Base->getType()->getAs()->getDecl()); if (CXXConstructorDecl *CopyConstructor = LookupCopyingConstructor(BaseClassDecl, Quals)) - ExceptSpec.CalledDecl(CopyConstructor); + ExceptSpec.CalledDecl(Base->getLocStart(), CopyConstructor); } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), BaseEnd = ClassDecl->vbases_end(); @@ -8663,7 +8668,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { = cast(Base->getType()->getAs()->getDecl()); if (CXXConstructorDecl *CopyConstructor = LookupCopyingConstructor(BaseClassDecl, Quals)) - ExceptSpec.CalledDecl(CopyConstructor); + ExceptSpec.CalledDecl(Base->getLocStart(), CopyConstructor); } for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); @@ -8673,7 +8678,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { if (CXXConstructorDecl *CopyConstructor = LookupCopyingConstructor(FieldClassDecl, Quals)) - ExceptSpec.CalledDecl(CopyConstructor); + ExceptSpec.CalledDecl(Field->getLocation(), CopyConstructor); } } @@ -8686,7 +8691,7 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // If the class definition does not explicitly declare a copy // constructor, one is declared implicitly. - ImplicitExceptionSpecification Spec(Context); + ImplicitExceptionSpecification Spec(*this); bool Const; llvm::tie(Spec, Const) = ComputeDefaultedCopyCtorExceptionSpecAndConst(ClassDecl); @@ -8784,7 +8789,7 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] - ImplicitExceptionSpecification ExceptSpec(Context); + ImplicitExceptionSpecification ExceptSpec(*this); if (ClassDecl->isInvalidDecl()) return ExceptSpec; @@ -8801,7 +8806,7 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. if (Constructor) - ExceptSpec.CalledDecl(Constructor); + ExceptSpec.CalledDecl(B->getLocStart(), Constructor); } } @@ -8815,7 +8820,7 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // If this is a deleted function, add it anyway. This might be conformant // with the standard. This might not. I'm not sure. It might not matter. if (Constructor) - ExceptSpec.CalledDecl(Constructor); + ExceptSpec.CalledDecl(B->getLocStart(), Constructor); } } @@ -8833,7 +8838,7 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // might just be ill-formed because this function attempts to refer to // a deleted function here. if (Constructor) - ExceptSpec.CalledDecl(Constructor); + ExceptSpec.CalledDecl(F->getLocation(), Constructor); } } @@ -11116,6 +11121,7 @@ bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) { FindCXXThisExpr Finder(*this); switch (Proto->getExceptionSpecType()) { + case EST_Uninstantiated: case EST_BasicNoexcept: case EST_Delayed: case EST_DynamicNone: diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 42221f8df4..14b24341d0 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -96,6 +96,26 @@ bool Sema::CheckDistantExceptionSpec(QualType T) { return FnT->hasExceptionSpec(); } +const FunctionProtoType * +Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { + // FIXME: If FD is a special member, we should delay computing its exception + // specification until this point. + if (FPT->getExceptionSpecType() != EST_Uninstantiated) + return FPT; + + FunctionDecl *SourceDecl = FPT->getExceptionSpecDecl(); + const FunctionProtoType *SourceFPT = + SourceDecl->getType()->castAs(); + + if (SourceFPT->getExceptionSpecType() != EST_Uninstantiated) + return SourceFPT; + + // Instantiate the exception specification now. + InstantiateExceptionSpec(Loc, SourceDecl); + + return SourceDecl->getType()->castAs(); +} + bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { OverloadedOperatorKind OO = New->getDeclName().getCXXOverloadedOperator(); bool IsOperatorNew = OO == OO_New || OO == OO_Array_New; @@ -104,7 +124,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { unsigned DiagID = diag::err_mismatched_exception_spec; if (getLangOpts().MicrosoftExt) DiagID = diag::warn_mismatched_exception_spec; - + if (!CheckEquivalentExceptionSpec(PDiag(DiagID), PDiag(diag::note_previous_declaration), Old->getType()->getAs(), @@ -295,6 +315,13 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, if (MissingEmptyExceptionSpecification) *MissingEmptyExceptionSpecification = false; + Old = ResolveExceptionSpec(NewLoc, Old); + if (!Old) + return false; + New = ResolveExceptionSpec(NewLoc, New); + if (!New) + return false; + // 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- @@ -318,6 +345,7 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, ExceptionSpecificationType NewEST = New->getExceptionSpecType(); assert(OldEST != EST_Delayed && NewEST != EST_Delayed && + OldEST != EST_Uninstantiated && NewEST != EST_Uninstantiated && "Shouldn't see unknown exception specifications here"); // Shortcut the case where both have no spec. @@ -483,6 +511,14 @@ bool Sema::CheckExceptionSpecSubset( if (!SubLoc.isValid()) SubLoc = SuperLoc; + // Resolve the exception specifications, if needed. + Superset = ResolveExceptionSpec(SuperLoc, Superset); + if (!Superset) + return false; + Subset = ResolveExceptionSpec(SubLoc, Subset); + if (!Subset) + return false; + ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType(); // If superset contains everything, we're done. @@ -507,6 +543,7 @@ bool Sema::CheckExceptionSpecSubset( ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); assert(SuperEST != EST_Delayed && SubEST != EST_Delayed && + SuperEST != EST_Uninstantiated && SubEST != EST_Uninstantiated && "Shouldn't see unknown exception specifications here"); // It does not. If the subset contains everything, we've failed. @@ -726,4 +763,324 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, New->getLocation()); } +static CanThrowResult canSubExprsThrow(Sema &S, const Expr *CE) { + Expr *E = const_cast(CE); + CanThrowResult R = CT_Cannot; + for (Expr::child_range I = E->children(); I && R != CT_Can; ++I) + R = mergeCanThrow(R, S.canThrow(cast(*I))); + return R; +} + +static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, + const Decl *D, + bool NullThrows = true) { + if (!D) + return NullThrows ? CT_Can : CT_Cannot; + + // See if we can get a function type from the decl somehow. + const ValueDecl *VD = dyn_cast(D); + if (!VD) // If we have no clue what we're calling, assume the worst. + return CT_Can; + + // As an extension, we assume that __attribute__((nothrow)) functions don't + // throw. + if (isa(D) && D->hasAttr()) + return CT_Cannot; + + QualType T = VD->getType(); + const FunctionProtoType *FT; + if ((FT = T->getAs())) { + } else if (const PointerType *PT = T->getAs()) + FT = PT->getPointeeType()->getAs(); + else if (const ReferenceType *RT = T->getAs()) + FT = RT->getPointeeType()->getAs(); + else if (const MemberPointerType *MT = T->getAs()) + FT = MT->getPointeeType()->getAs(); + else if (const BlockPointerType *BT = T->getAs()) + FT = BT->getPointeeType()->getAs(); + + if (!FT) + return CT_Can; + + FT = S.ResolveExceptionSpec(E->getLocStart(), FT); + if (!FT) + return CT_Can; + + if (FT->getExceptionSpecType() == EST_Delayed) { + // FIXME: Try to resolve a delayed exception spec in ResolveExceptionSpec. + assert(isa(D) && + "only constructor exception specs can be unknown"); + S.Diag(E->getLocStart(), diag::err_exception_spec_unknown) + << E->getSourceRange(); + return CT_Can; + } + + return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can; +} + +static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) { + if (DC->isTypeDependent()) + return CT_Dependent; + + if (!DC->getTypeAsWritten()->isReferenceType()) + return CT_Cannot; + + if (DC->getSubExpr()->isTypeDependent()) + return CT_Dependent; + + return DC->getCastKind() == clang::CK_Dynamic? CT_Can : CT_Cannot; +} + +static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) { + if (DC->isTypeOperand()) + return CT_Cannot; + + Expr *Op = DC->getExprOperand(); + if (Op->isTypeDependent()) + return CT_Dependent; + + const RecordType *RT = Op->getType()->getAs(); + if (!RT) + return CT_Cannot; + + if (!cast(RT->getDecl())->isPolymorphic()) + return CT_Cannot; + + if (Op->Classify(S.Context).isPRValue()) + return CT_Cannot; + + return CT_Can; +} + +CanThrowResult Sema::canThrow(const Expr *E) { + // C++ [expr.unary.noexcept]p3: + // [Can throw] if in a potentially-evaluated context the expression would + // contain: + switch (E->getStmtClass()) { + case Expr::CXXThrowExprClass: + // - a potentially evaluated throw-expression + return CT_Can; + + case Expr::CXXDynamicCastExprClass: { + // - a potentially evaluated dynamic_cast expression dynamic_cast(v), + // where T is a reference type, that requires a run-time check + CanThrowResult CT = canDynamicCastThrow(cast(E)); + if (CT == CT_Can) + return CT; + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + case Expr::CXXTypeidExprClass: + // - a potentially evaluated typeid expression applied to a glvalue + // expression whose type is a polymorphic class type + return canTypeidThrow(*this, cast(E)); + + // - a potentially evaluated call to a function, member function, function + // pointer, or member function pointer that does not have a non-throwing + // exception-specification + case Expr::CallExprClass: + case Expr::CXXMemberCallExprClass: + case Expr::CXXOperatorCallExprClass: + case Expr::UserDefinedLiteralClass: { + const CallExpr *CE = cast(E); + CanThrowResult CT; + if (E->isTypeDependent()) + CT = CT_Dependent; + else if (isa(CE->getCallee()->IgnoreParens())) + CT = CT_Cannot; + else + CT = canCalleeThrow(*this, E, CE->getCalleeDecl()); + if (CT == CT_Can) + return CT; + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + case Expr::CXXConstructExprClass: + case Expr::CXXTemporaryObjectExprClass: { + CanThrowResult CT = canCalleeThrow(*this, E, + cast(E)->getConstructor()); + if (CT == CT_Can) + return CT; + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + case Expr::LambdaExprClass: { + const LambdaExpr *Lambda = cast(E); + CanThrowResult CT = CT_Cannot; + for (LambdaExpr::capture_init_iterator Cap = Lambda->capture_init_begin(), + CapEnd = Lambda->capture_init_end(); + Cap != CapEnd; ++Cap) + CT = mergeCanThrow(CT, canThrow(*Cap)); + return CT; + } + + case Expr::CXXNewExprClass: { + CanThrowResult CT; + if (E->isTypeDependent()) + CT = CT_Dependent; + else + CT = canCalleeThrow(*this, E, cast(E)->getOperatorNew()); + if (CT == CT_Can) + return CT; + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + case Expr::CXXDeleteExprClass: { + CanThrowResult CT; + QualType DTy = cast(E)->getDestroyedType(); + if (DTy.isNull() || DTy->isDependentType()) { + CT = CT_Dependent; + } else { + CT = canCalleeThrow(*this, E, + cast(E)->getOperatorDelete()); + if (const RecordType *RT = DTy->getAs()) { + const CXXRecordDecl *RD = cast(RT->getDecl()); + CT = mergeCanThrow(CT, canCalleeThrow(*this, E, RD->getDestructor())); + } + if (CT == CT_Can) + return CT; + } + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + case Expr::CXXBindTemporaryExprClass: { + // The bound temporary has to be destroyed again, which might throw. + CanThrowResult CT = canCalleeThrow(*this, E, + cast(E)->getTemporary()->getDestructor()); + if (CT == CT_Can) + return CT; + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + // ObjC message sends are like function calls, but never have exception + // specs. + case Expr::ObjCMessageExprClass: + case Expr::ObjCPropertyRefExprClass: + case Expr::ObjCSubscriptRefExprClass: + return CT_Can; + + // All the ObjC literals that are implemented as calls are + // potentially throwing unless we decide to close off that + // possibility. + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: + case Expr::ObjCNumericLiteralClass: + return CT_Can; + + // Many other things have subexpressions, so we have to test those. + // Some are simple: + case Expr::ConditionalOperatorClass: + case Expr::CompoundLiteralExprClass: + case Expr::CXXConstCastExprClass: + case Expr::CXXDefaultArgExprClass: + case Expr::CXXReinterpretCastExprClass: + case Expr::DesignatedInitExprClass: + case Expr::ExprWithCleanupsClass: + case Expr::ExtVectorElementExprClass: + case Expr::InitListExprClass: + case Expr::MemberExprClass: + case Expr::ObjCIsaExprClass: + case Expr::ObjCIvarRefExprClass: + case Expr::ParenExprClass: + case Expr::ParenListExprClass: + case Expr::ShuffleVectorExprClass: + case Expr::VAArgExprClass: + return canSubExprsThrow(*this, E); + + // Some might be dependent for other reasons. + case Expr::ArraySubscriptExprClass: + case Expr::BinaryOperatorClass: + case Expr::CompoundAssignOperatorClass: + case Expr::CStyleCastExprClass: + case Expr::CXXStaticCastExprClass: + case Expr::CXXFunctionalCastExprClass: + case Expr::ImplicitCastExprClass: + case Expr::MaterializeTemporaryExprClass: + case Expr::UnaryOperatorClass: { + CanThrowResult CT = E->isTypeDependent() ? CT_Dependent : CT_Cannot; + return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + } + + // FIXME: We should handle StmtExpr, but that opens a MASSIVE can of worms. + case Expr::StmtExprClass: + return CT_Can; + + case Expr::ChooseExprClass: + if (E->isTypeDependent() || E->isValueDependent()) + return CT_Dependent; + return canThrow(cast(E)->getChosenSubExpr(Context)); + + case Expr::GenericSelectionExprClass: + if (cast(E)->isResultDependent()) + return CT_Dependent; + return canThrow(cast(E)->getResultExpr()); + + // Some expressions are always dependent. + case Expr::CXXDependentScopeMemberExprClass: + case Expr::CXXUnresolvedConstructExprClass: + case Expr::DependentScopeDeclRefExprClass: + return CT_Dependent; + + case Expr::AsTypeExprClass: + case Expr::BinaryConditionalOperatorClass: + case Expr::BlockExprClass: + case Expr::CUDAKernelCallExprClass: + case Expr::DeclRefExprClass: + case Expr::ObjCBridgedCastExprClass: + case Expr::ObjCIndirectCopyRestoreExprClass: + case Expr::ObjCProtocolExprClass: + case Expr::ObjCSelectorExprClass: + case Expr::OffsetOfExprClass: + case Expr::PackExpansionExprClass: + case Expr::PseudoObjectExprClass: + case Expr::SubstNonTypeTemplateParmExprClass: + case Expr::SubstNonTypeTemplateParmPackExprClass: + case Expr::UnaryExprOrTypeTraitExprClass: + case Expr::UnresolvedLookupExprClass: + case Expr::UnresolvedMemberExprClass: + // FIXME: Can any of the above throw? If so, when? + return CT_Cannot; + + case Expr::AddrLabelExprClass: + case Expr::ArrayTypeTraitExprClass: + case Expr::AtomicExprClass: + case Expr::BinaryTypeTraitExprClass: + case Expr::TypeTraitExprClass: + case Expr::CXXBoolLiteralExprClass: + case Expr::CXXNoexceptExprClass: + case Expr::CXXNullPtrLiteralExprClass: + case Expr::CXXPseudoDestructorExprClass: + case Expr::CXXScalarValueInitExprClass: + case Expr::CXXThisExprClass: + case Expr::CXXUuidofExprClass: + case Expr::CharacterLiteralClass: + case Expr::ExpressionTraitExprClass: + case Expr::FloatingLiteralClass: + case Expr::GNUNullExprClass: + case Expr::ImaginaryLiteralClass: + case Expr::ImplicitValueInitExprClass: + case Expr::IntegerLiteralClass: + case Expr::ObjCEncodeExprClass: + case Expr::ObjCStringLiteralClass: + case Expr::ObjCBoolLiteralExprClass: + case Expr::OpaqueValueExprClass: + case Expr::PredefinedExprClass: + case Expr::SizeOfPackExprClass: + case Expr::StringLiteralClass: + case Expr::UnaryTypeTraitExprClass: + // These expressions can never throw. + return CT_Cannot; + +#define STMT(CLASS, PARENT) case Expr::CLASS##Class: +#define STMT_RANGE(Base, First, Last) +#define LAST_STMT_RANGE(BASE, FIRST, LAST) +#define EXPR(CLASS, PARENT) +#define ABSTRACT_STMT(STMT) +#include "clang/AST/StmtNodes.inc" + case Expr::NoStmtClass: + llvm_unreachable("Invalid class for expression"); + } + llvm_unreachable("Bogus StmtClass"); +} + } // end namespace clang diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 3e368a3b10..fd54499c9e 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9773,6 +9773,13 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { // FIXME: Is this really right? if (CurContext == Func) return; + // Instantiate the exception specification for any function which is + // used: CodeGen will need it. + if (Func->getTemplateInstantiationPattern() && + Func->getType()->castAs()->getExceptionSpecType() + == EST_Uninstantiated) + InstantiateExceptionSpec(Loc, Func); + // Implicit instantiation of function templates and member functions of // class templates. if (Func->isImplicitlyInstantiable()) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index e4b40e624c..af86cb2c43 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -3135,6 +3135,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, FoundAssign = true; const FunctionProtoType *CPT = Operator->getType()->getAs(); + CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); + if (!CPT) + return false; if (CPT->getExceptionSpecType() == EST_Delayed) return false; if (!CPT->isNothrow(Self.Context)) @@ -3174,6 +3177,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, FoundConstructor = true; const FunctionProtoType *CPT = Constructor->getType()->getAs(); + CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); + if (!CPT) + return false; if (CPT->getExceptionSpecType() == EST_Delayed) return false; // FIXME: check whether evaluating default arguments can throw. @@ -3209,6 +3215,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, if (Constructor->isDefaultConstructor()) { const FunctionProtoType *CPT = Constructor->getType()->getAs(); + CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); + if (!CPT) + return false; if (CPT->getExceptionSpecType() == EST_Delayed) return false; // TODO: check whether evaluating default arguments can throw. @@ -5203,9 +5212,9 @@ ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl, ExprResult Sema::BuildCXXNoexceptExpr(SourceLocation KeyLoc, Expr *Operand, SourceLocation RParen) { + CanThrowResult CanThrow = canThrow(Operand); return Owned(new (Context) CXXNoexceptExpr(Context.BoolTy, Operand, - Operand->CanThrow(Context), - KeyLoc, RParen)); + CanThrow, KeyLoc, RParen)); } ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 284c8dec1c..50230f068d 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -11150,6 +11150,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found, VK_LValue, Found.getDecl(), TemplateArgs); + MarkDeclRefReferenced(DRE); DRE->setHadMultipleCandidates(ULE->getNumDecls() > 1); return DRE; } @@ -11178,6 +11179,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found, VK_LValue, Found.getDecl(), TemplateArgs); + MarkDeclRefReferenced(DRE); DRE->setHadMultipleCandidates(MemExpr->getNumDecls() > 1); return DRE; } else { diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 60de75b917..128dc2f7ff 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -153,6 +153,7 @@ Sema::getTemplateInstantiationArgs(NamedDecl *D, bool Sema::ActiveTemplateInstantiation::isInstantiationRecord() const { switch (Kind) { case TemplateInstantiation: + case ExceptionSpecInstantiation: case DefaultTemplateArgumentInstantiation: case DefaultFunctionArgumentInstantiation: return true; @@ -190,6 +191,29 @@ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, } } +Sema::InstantiatingTemplate:: +InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + FunctionDecl *Entity, ExceptionSpecification, + SourceRange InstantiationRange) + : SemaRef(SemaRef), + SavedInNonInstantiationSFINAEContext( + SemaRef.InNonInstantiationSFINAEContext) +{ + Invalid = CheckInstantiationDepth(PointOfInstantiation, + InstantiationRange); + if (!Invalid) { + ActiveTemplateInstantiation Inst; + Inst.Kind = ActiveTemplateInstantiation::ExceptionSpecInstantiation; + Inst.PointOfInstantiation = PointOfInstantiation; + Inst.Entity = reinterpret_cast(Entity); + Inst.TemplateArgs = 0; + Inst.NumTemplateArgs = 0; + Inst.InstantiationRange = InstantiationRange; + SemaRef.InNonInstantiationSFINAEContext = false; + SemaRef.ActiveTemplateInstantiations.push_back(Inst); + } +} + Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template, @@ -592,6 +616,13 @@ void Sema::PrintInstantiationStack() { << Active->InstantiationRange; break; } + + case ActiveTemplateInstantiation::ExceptionSpecInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_exception_spec_instantiation_here) + << cast((Decl *)Active->Entity) + << Active->InstantiationRange; + break; } } } @@ -609,6 +640,7 @@ llvm::Optional Sema::isSFINAEContext() const { switch(Active->Kind) { case ActiveTemplateInstantiation::DefaultFunctionArgumentInstantiation: case ActiveTemplateInstantiation::TemplateInstantiation: + case ActiveTemplateInstantiation::ExceptionSpecInstantiation: // This is a template instantiation, so there is no SFINAE. return llvm::Optional(); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 8d382d1bac..39c27fc497 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2215,6 +2215,194 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, return NewTInfo; } +/// Introduce the instantiated function parameters into the local +/// instantiation scope, and set the parameter names to those used +/// in the template. +static void addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function, + const FunctionDecl *PatternDecl, + LocalInstantiationScope &Scope, + const MultiLevelTemplateArgumentList &TemplateArgs) { + unsigned FParamIdx = 0; + for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) { + const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I); + if (!PatternParam->isParameterPack()) { + // Simple case: not a parameter pack. + assert(FParamIdx < Function->getNumParams()); + ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); + FunctionParam->setDeclName(PatternParam->getDeclName()); + Scope.InstantiatedLocal(PatternParam, FunctionParam); + ++FParamIdx; + continue; + } + + // Expand the parameter pack. + Scope.MakeInstantiatedLocalArgPack(PatternParam); + unsigned NumArgumentsInExpansion + = S.getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs); + for (unsigned Arg = 0; Arg < NumArgumentsInExpansion; ++Arg) { + ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); + FunctionParam->setDeclName(PatternParam->getDeclName()); + Scope.InstantiatedLocalPackArg(PatternParam, FunctionParam); + ++FParamIdx; + } + } +} + +static void InstantiateExceptionSpec(Sema &SemaRef, FunctionDecl *New, + const FunctionProtoType *Proto, + const MultiLevelTemplateArgumentList &TemplateArgs) { + // C++11 [expr.prim.general]p3: + // If a declaration declares a member function or member function + // template of a class X, the expression this is a prvalue of type + // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq + // and the end of the function-definition, member-declarator, or + // declarator. + CXXRecordDecl *ThisContext = 0; + unsigned ThisTypeQuals = 0; + if (CXXMethodDecl *Method = dyn_cast(New)) { + ThisContext = Method->getParent(); + ThisTypeQuals = Method->getTypeQualifiers(); + } + Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals, + SemaRef.getLangOpts().CPlusPlus0x); + + // The function has an exception specification or a "noreturn" + // attribute. Substitute into each of the exception types. + SmallVector Exceptions; + for (unsigned I = 0, N = Proto->getNumExceptions(); I != N; ++I) { + // FIXME: Poor location information! + if (const PackExpansionType *PackExpansion + = Proto->getExceptionType(I)->getAs()) { + // We have a pack expansion. Instantiate it. + SmallVector Unexpanded; + SemaRef.collectUnexpandedParameterPacks(PackExpansion->getPattern(), + Unexpanded); + assert(!Unexpanded.empty() && + "Pack expansion without parameter packs?"); + + bool Expand = false; + bool RetainExpansion = false; + llvm::Optional NumExpansions + = PackExpansion->getNumExpansions(); + if (SemaRef.CheckParameterPacksForExpansion(New->getLocation(), + SourceRange(), + Unexpanded, + TemplateArgs, + Expand, + 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 + // type. + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, -1); + QualType T = SemaRef.SubstType(PackExpansion->getPattern(), + TemplateArgs, + New->getLocation(), New->getDeclName()); + if (T.isNull()) + break; + + T = SemaRef.Context.getPackExpansionType(T, NumExpansions); + Exceptions.push_back(T); + continue; + } + + // Substitute into the pack expansion pattern for each template + bool Invalid = false; + for (unsigned ArgIdx = 0; ArgIdx != *NumExpansions; ++ArgIdx) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, ArgIdx); + + QualType T = SemaRef.SubstType(PackExpansion->getPattern(), + TemplateArgs, + New->getLocation(), New->getDeclName()); + if (T.isNull()) { + Invalid = true; + break; + } + + Exceptions.push_back(T); + } + + if (Invalid) + break; + + continue; + } + + QualType T + = SemaRef.SubstType(Proto->getExceptionType(I), TemplateArgs, + New->getLocation(), New->getDeclName()); + if (T.isNull() || + SemaRef.CheckSpecifiedExceptionType(T, New->getLocation())) + continue; + + Exceptions.push_back(T); + } + Expr *NoexceptExpr = 0; + if (Expr *OldNoexceptExpr = Proto->getNoexceptExpr()) { + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); + ExprResult E = SemaRef.SubstExpr(OldNoexceptExpr, TemplateArgs); + if (E.isUsable()) + E = SemaRef.CheckBooleanCondition(E.get(), E.get()->getLocStart()); + + if (E.isUsable()) { + NoexceptExpr = E.take(); + if (!NoexceptExpr->isTypeDependent() && + !NoexceptExpr->isValueDependent()) + NoexceptExpr = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr, + 0, SemaRef.PDiag(diag::err_noexcept_needs_constant_expression), + /*AllowFold*/ false).take(); + } + } + + // Rebuild the function type + const FunctionProtoType *NewProto + = New->getType()->getAs(); + assert(NewProto && "Template instantiation without function prototype?"); + + FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo(); + EPI.ExceptionSpecType = Proto->getExceptionSpecType(); + EPI.NumExceptions = Exceptions.size(); + EPI.Exceptions = Exceptions.data(); + EPI.NoexceptExpr = NoexceptExpr; + + New->setType(SemaRef.Context.getFunctionType(NewProto->getResultType(), + NewProto->arg_type_begin(), + NewProto->getNumArgs(), + EPI)); +} + +void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, + FunctionDecl *Decl) { + FunctionDecl *Tmpl = Decl->getTemplateInstantiationPattern(); + assert(Tmpl && "can't instantiate non-template"); + + if (Decl->getType()->castAs()->getExceptionSpecType() + != EST_Uninstantiated) + return; + + InstantiatingTemplate Inst(*this, PointOfInstantiation, Decl, + InstantiatingTemplate::ExceptionSpecification()); + if (Inst) + return; + + // Enter the scope of this instantiation. We don't use + // PushDeclContext because we don't have a scope. + Sema::ContextRAII savedContext(*this, Decl); + LocalInstantiationScope Scope(*this); + + MultiLevelTemplateArgumentList TemplateArgs = + getTemplateInstantiationArgs(Decl, 0, /*RelativeToPrimary*/true); + + addInstantiatedParametersToScope(*this, Decl, Tmpl, Scope, TemplateArgs); + + const FunctionProtoType *Proto = Tmpl->getType()->castAs(); + ::InstantiateExceptionSpec(*this, Decl, Proto, TemplateArgs); +} + /// \brief Initializes the common fields of an instantiation function /// declaration (New) from the corresponding fields of its template (Tmpl). /// @@ -2252,135 +2440,32 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, assert(Proto && "Function template without prototype?"); if (Proto->hasExceptionSpec() || Proto->getNoReturnAttr()) { - // C++11 [expr.prim.general]p3: - // If a declaration declares a member function or member function - // template of a class X, the expression this is a prvalue of type - // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq - // and the end of the function-definition, member-declarator, or - // declarator. - CXXRecordDecl *ThisContext = 0; - unsigned ThisTypeQuals = 0; - if (CXXMethodDecl *Method = dyn_cast(New)) { - ThisContext = Method->getParent(); - ThisTypeQuals = Method->getTypeQualifiers(); - } - Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals, - SemaRef.getLangOpts().CPlusPlus0x); - - - // The function has an exception specification or a "noreturn" - // attribute. Substitute into each of the exception types. - SmallVector Exceptions; - for (unsigned I = 0, N = Proto->getNumExceptions(); I != N; ++I) { - // FIXME: Poor location information! - if (const PackExpansionType *PackExpansion - = Proto->getExceptionType(I)->getAs()) { - // We have a pack expansion. Instantiate it. - SmallVector Unexpanded; - SemaRef.collectUnexpandedParameterPacks(PackExpansion->getPattern(), - Unexpanded); - assert(!Unexpanded.empty() && - "Pack expansion without parameter packs?"); - - bool Expand = false; - bool RetainExpansion = false; - llvm::Optional NumExpansions - = PackExpansion->getNumExpansions(); - if (SemaRef.CheckParameterPacksForExpansion(New->getLocation(), - SourceRange(), - Unexpanded, - TemplateArgs, - Expand, - 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 - // type. - Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, -1); - QualType T = SemaRef.SubstType(PackExpansion->getPattern(), - TemplateArgs, - New->getLocation(), New->getDeclName()); - if (T.isNull()) - break; - - T = SemaRef.Context.getPackExpansionType(T, NumExpansions); - Exceptions.push_back(T); - continue; - } - - // Substitute into the pack expansion pattern for each template - bool Invalid = false; - for (unsigned ArgIdx = 0; ArgIdx != *NumExpansions; ++ArgIdx) { - Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, ArgIdx); - - QualType T = SemaRef.SubstType(PackExpansion->getPattern(), - TemplateArgs, - New->getLocation(), New->getDeclName()); - if (T.isNull()) { - Invalid = true; - break; - } - - Exceptions.push_back(T); - } - - if (Invalid) - break; - - continue; - } - - QualType T - = SemaRef.SubstType(Proto->getExceptionType(I), TemplateArgs, - New->getLocation(), New->getDeclName()); - if (T.isNull() || - SemaRef.CheckSpecifiedExceptionType(T, New->getLocation())) - continue; - - Exceptions.push_back(T); - } - Expr *NoexceptExpr = 0; - if (Expr *OldNoexceptExpr = Proto->getNoexceptExpr()) { - EnterExpressionEvaluationContext Unevaluated(SemaRef, - Sema::ConstantEvaluated); - ExprResult E = SemaRef.SubstExpr(OldNoexceptExpr, TemplateArgs); - if (E.isUsable()) - E = SemaRef.CheckBooleanCondition(E.get(), E.get()->getLocStart()); - - if (E.isUsable()) { - NoexceptExpr = E.take(); - if (!NoexceptExpr->isTypeDependent() && - !NoexceptExpr->isValueDependent()) - NoexceptExpr = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr, - 0, SemaRef.PDiag(diag::err_noexcept_needs_constant_expression), - /*AllowFold*/ false).take(); - } - } - - // Rebuild the function type - FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo(); - EPI.ExceptionSpecType = Proto->getExceptionSpecType(); - EPI.NumExceptions = Exceptions.size(); - EPI.Exceptions = Exceptions.data(); - EPI.NoexceptExpr = NoexceptExpr; - EPI.ExtInfo = Proto->getExtInfo(); - const FunctionProtoType *NewProto - = New->getType()->getAs(); - assert(NewProto && "Template instantiation without function prototype?"); - New->setType(SemaRef.Context.getFunctionType(NewProto->getResultType(), - NewProto->arg_type_begin(), - NewProto->getNumArgs(), - EPI)); + // DR1330: In C++11, defer instantiation of a non-trivial + // exception specification. + if (SemaRef.getLangOpts().CPlusPlus0x && + EPI.ExceptionSpecType != EST_None && + EPI.ExceptionSpecType != EST_DynamicNone && + EPI.ExceptionSpecType != EST_BasicNoexcept) { + // Mark the function has having an uninstantiated exception specification. + const FunctionProtoType *NewProto + = New->getType()->getAs(); + assert(NewProto && "Template instantiation without function prototype?"); + EPI = NewProto->getExtProtoInfo(); + EPI.ExceptionSpecType = EST_Uninstantiated; + EPI.ExceptionSpecDecl = New; + New->setType(SemaRef.Context.getFunctionType(NewProto->getResultType(), + NewProto->arg_type_begin(), + NewProto->getNumArgs(), + EPI)); + } else { + ::InstantiateExceptionSpec(SemaRef, New, Proto, TemplateArgs); + } } - const FunctionDecl* Definition = Tmpl; - // Get the definition. Leaves the variable unchanged if undefined. + const FunctionDecl *Definition = Tmpl; Tmpl->isDefined(Definition); SemaRef.InstantiateAttrs(TemplateArgs, Definition, New, @@ -2538,33 +2623,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(Function, 0, false, PatternDecl); - // Introduce the instantiated function parameters into the local - // instantiation scope, and set the parameter names to those used - // in the template. - unsigned FParamIdx = 0; - for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) { - const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I); - if (!PatternParam->isParameterPack()) { - // Simple case: not a parameter pack. - assert(FParamIdx < Function->getNumParams()); - ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); - FunctionParam->setDeclName(PatternParam->getDeclName()); - Scope.InstantiatedLocal(PatternParam, FunctionParam); - ++FParamIdx; - continue; - } - - // Expand the parameter pack. - Scope.MakeInstantiatedLocalArgPack(PatternParam); - unsigned NumArgumentsInExpansion - = getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs); - for (unsigned Arg = 0; Arg < NumArgumentsInExpansion; ++Arg) { - ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); - FunctionParam->setDeclName(PatternParam->getDeclName()); - Scope.InstantiatedLocalPackArg(PatternParam, FunctionParam); - ++FParamIdx; - } - } + addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope, + TemplateArgs); if (PatternDecl->isDefaulted()) { ActOnFinishFunctionBody(Function, 0, /*IsInstantiation=*/true); diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 5511fc00f5..a66378e517 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -4181,10 +4181,7 @@ TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, unsigned ThisTypeQuals) { // Transform the parameters and return type. // - // We instantiate in source order, with the return type first followed by - // the parameters, because users tend to expect this (even if they shouldn't - // rely on it!). - // + // We are required to instantiate the params and return type in source order. // When the function has a trailing return type, we instantiate the // parameters before the return type, since the return type can then refer // to the parameters themselves (via decltype, sizeof, etc.). @@ -4230,6 +4227,8 @@ TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, return QualType(); } + // FIXME: Need to transform the exception-specification too. + QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || ResultType != T->getResultType() || diff --git a/test/CXX/except/except.spec/p1.cpp b/test/CXX/except/except.spec/p1.cpp index c68ec56a24..e184ec4ffa 100644 --- a/test/CXX/except/except.spec/p1.cpp +++ b/test/CXX/except/except.spec/p1.cpp @@ -65,7 +65,7 @@ namespace noexcept_unevaluated { } template - void g(T x) noexcept((sizeof(T) == sizeof(int)) || f(x)) { } + void g(T x) noexcept((sizeof(T) == sizeof(int)) || noexcept(f(x))) { } void h() { g(1); @@ -77,5 +77,5 @@ namespace PR11084 { static int f() noexcept(1/X) { return 10; } // expected-error{{argument to noexcept specifier must be a constant expression}} expected-note{{division by zero}} }; - void g() { A<0>::f(); } // expected-note{{in instantiation of template class 'PR11084::A<0>' requested here}} + void g() { A<0>::f(); } // expected-note{{in instantiation of exception specification for 'f' requested here}} } diff --git a/test/CodeGenCXX/cxx11-exception-spec.cpp b/test/CodeGenCXX/cxx11-exception-spec.cpp new file mode 100644 index 0000000000..461af78eae --- /dev/null +++ b/test/CodeGenCXX/cxx11-exception-spec.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++11 -verify -emit-llvm %s -o - | FileCheck %s + +template void f() noexcept(sizeof(T) == 4); + +void g() { + // CHECK: declare void @_Z1fIiEvv() nounwind + f(); + // CHECK: declare void @_Z1fIA2_iEvv() + f(); + // CHECK: declare void @_Z1fIfEvv() nounwind + void (*f1)() = &f; + // CHECK: declare void @_Z1fIdEvv() + void (*f2)() = &f; + // CHECK: declare void @_Z1fIA4_cEvv() nounwind + (void)&f; + // CHECK: declare void @_Z1fIcEvv() + (void)&f; +} diff --git a/test/SemaTemplate/instantiate-declref.cpp b/test/SemaTemplate/instantiate-declref.cpp index ced56dfc6a..7d4a2ff6a3 100644 --- a/test/SemaTemplate/instantiate-declref.cpp +++ b/test/SemaTemplate/instantiate-declref.cpp @@ -105,3 +105,13 @@ namespace test1 { } template void f(int const &); // expected-note {{requested here}} } + +namespace test2 { + template void f() { + T::error; // expected-error {{no member}} + } + void g() { + // This counts as an odr-use, so should trigger the instantiation of f. + (void)&f; // expected-note {{here}} + } +} diff --git a/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp b/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp new file mode 100644 index 0000000000..763b9ed41b --- /dev/null +++ b/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -ftemplate-depth 16 %s + +// DR1330: an exception specification for a function template is only +// instantiated when it is needed. + +template void f1(T*) throw(T); // expected-error{{incomplete type 'Incomplete' is not allowed in exception specification}} +struct Incomplete; // expected-note{{forward}} + +void test_f1(Incomplete *incomplete_p, int *int_p) { + f1(int_p); + f1(incomplete_p); // expected-note{{instantiation of exception spec}} +} + +template struct A { + template struct B { + static void f() noexcept(A().n); + }; + + constexpr A() : n(true) {} + bool n; +}; + +static_assert(noexcept(A::B::f()), ""); + +template struct S { + static void recurse() noexcept(noexcept(S::recurse())); // \ + // expected-error {{no member named 'recurse'}} \ + // expected-note 9{{instantiation of exception spec}} +}; +decltype(S<0>::recurse()) *pVoid1 = 0; // ok, exception spec not needed +decltype(&S<0>::recurse) pFn = 0; // ok, exception spec not needed + +template<> struct S<10> {}; +void (*pFn2)() noexcept = &S<0>::recurse; // expected-note {{instantiation of exception spec}} + + +template T go(T a) noexcept(noexcept(go(a))); // \ +// expected-error 16{{call to function 'go' that is neither visible}} \ +// expected-note 16{{'go' should be declared prior to the call site}} \ +// expected-error {{recursive template instantiation exceeded maximum depth of 16}} \ +// expected-error {{use of undeclared identifier 'go'}} \ + +void f() { + int k = go(0); // \ + // expected-note {{in instantiation of exception specification for 'go' requested here}} +} + + +namespace dr1330_example { + template struct A { + void f(...) throw (typename T::X); // expected-error {{'int'}} + void f(int); + }; + + int main() { + A().f(42); + } + + int test2() { + struct S { + template + static int f() noexcept(noexcept(A().f("boo!"))) { return 0; } // \ + // expected-note {{instantiation of exception spec}} + typedef decltype(f()) X; + }; + S().f(); // ok + S().f(); // expected-note {{instantiation of exception spec}} + } +} + +namespace core_19754_example { + template T declval() noexcept; + + template()))> + struct is_movable { static const bool value = true; }; + + template + struct wrap { + T val; + void irrelevant(wrap &p) noexcept(is_movable::value); + }; + + template + struct base { + base() {} + base(const typename T::type1 &); + base(const typename T::type2 &); + }; + + template + struct type1 { + wrap base; + }; + + template + struct type2 { + wrap base; + }; + + struct types { + typedef base base; + typedef type1 type1; + typedef type2 type2; + }; + + base val = base(); +} -- cgit v1.2.3