diff --git a/clang/include/clang/AST/DataRecursiveASTVisitor.h b/clang/include/clang/AST/DataRecursiveASTVisitor.h index 6d9ac94a43e8..f7c811ee8d61 100644 --- a/clang/include/clang/AST/DataRecursiveASTVisitor.h +++ b/clang/include/clang/AST/DataRecursiveASTVisitor.h @@ -878,6 +878,9 @@ DEF_TRAVERSE_TYPE(FunctionProtoType, { for (const auto &E : T->exceptions()) { TRY_TO(TraverseType(E)); } + + if (Expr *NE = T->getNoexceptExpr()) + TRY_TO(TraverseStmt(NE)); }) DEF_TRAVERSE_TYPE(UnresolvedUsingType, {}) @@ -1086,6 +1089,9 @@ DEF_TRAVERSE_TYPELOC(FunctionProtoType, { for (const auto &E : T->exceptions()) { TRY_TO(TraverseType(E)); } + + if (Expr *NE = T->getNoexceptExpr()) + TRY_TO(TraverseStmt(NE)); }) DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {}) @@ -2125,21 +2131,29 @@ bool RecursiveASTVisitor::TraverseLambdaExpr(LambdaExpr *S) { TRY_TO(TraverseLambdaCapture(S, C)); } - if (S->hasExplicitParameters() || S->hasExplicitResultType()) { - TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); - if (S->hasExplicitParameters() && S->hasExplicitResultType()) { - // Visit the whole type. - TRY_TO(TraverseTypeLoc(TL)); - } else if (FunctionProtoTypeLoc Proto = TL.getAs()) { - if (S->hasExplicitParameters()) { - // Visit parameters. - for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I) { - TRY_TO(TraverseDecl(Proto.getParam(I))); - } - } else { - TRY_TO(TraverseTypeLoc(Proto.getReturnLoc())); + TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + FunctionProtoTypeLoc Proto = TL.castAs(); + + if (S->hasExplicitParameters() && S->hasExplicitResultType()) { + // Visit the whole type. + TRY_TO(TraverseTypeLoc(TL)); + } else { + if (S->hasExplicitParameters()) { + // Visit parameters. + for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I) { + TRY_TO(TraverseDecl(Proto.getParam(I))); } + } else if (S->hasExplicitResultType()) { + TRY_TO(TraverseTypeLoc(Proto.getReturnLoc())); } + + auto *T = Proto.getTypePtr(); + for (const auto &E : T->exceptions()) { + TRY_TO(TraverseType(E)); + } + + if (Expr *NE = T->getNoexceptExpr()) + TRY_TO(TraverseStmt(NE)); } TRY_TO(TraverseLambdaBody(S)); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2b2adc810afd..afe4b0cd2be5 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2678,20 +2678,23 @@ private: } protected: - CastExpr(StmtClass SC, QualType ty, ExprValueKind VK, - const CastKind kind, Expr *op, unsigned BasePathSize) : - Expr(SC, ty, VK, OK_Ordinary, - // Cast expressions are type-dependent if the type is - // dependent (C++ [temp.dep.expr]p3). - ty->isDependentType(), - // Cast expressions are value-dependent if the type is - // dependent or if the subexpression is value-dependent. - ty->isDependentType() || (op && op->isValueDependent()), - (ty->isInstantiationDependentType() || - (op && op->isInstantiationDependent())), - (ty->containsUnexpandedParameterPack() || - (op && op->containsUnexpandedParameterPack()))), - Op(op) { + CastExpr(StmtClass SC, QualType ty, ExprValueKind VK, const CastKind kind, + Expr *op, unsigned BasePathSize) + : Expr(SC, ty, VK, OK_Ordinary, + // Cast expressions are type-dependent if the type is + // dependent (C++ [temp.dep.expr]p3). + ty->isDependentType(), + // Cast expressions are value-dependent if the type is + // dependent or if the subexpression is value-dependent. + ty->isDependentType() || (op && op->isValueDependent()), + (ty->isInstantiationDependentType() || + (op && op->isInstantiationDependent())), + // An implicit cast expression doesn't (lexically) contain an + // unexpanded pack, even if its target type does. + ((SC != ImplicitCastExprClass && + ty->containsUnexpandedParameterPack()) || + (op && op->containsUnexpandedParameterPack()))), + Op(op) { assert(kind != CK_Invalid && "creating cast with invalid cast kind"); CastExprBits.Kind = kind; setBasePathSize(BasePathSize); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 2f26801f9401..6ae788d6c6cb 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -943,6 +943,9 @@ DEF_TRAVERSE_TYPE(FunctionProtoType, { for (const auto &E : T->exceptions()) { TRY_TO(TraverseType(E)); } + + if (Expr *NE = T->getNoexceptExpr()) + TRY_TO(TraverseStmt(NE)); }) DEF_TRAVERSE_TYPE(UnresolvedUsingType, {}) @@ -1151,6 +1154,9 @@ DEF_TRAVERSE_TYPELOC(FunctionProtoType, { for (const auto &E : T->exceptions()) { TRY_TO(TraverseType(E)); } + + if (Expr *NE = T->getNoexceptExpr()) + TRY_TO(TraverseStmt(NE)); }) DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {}) @@ -2147,21 +2153,29 @@ bool RecursiveASTVisitor::TraverseLambdaExpr(LambdaExpr *S) { TRY_TO(TraverseLambdaCapture(S, C)); } - if (S->hasExplicitParameters() || S->hasExplicitResultType()) { - TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); - if (S->hasExplicitParameters() && S->hasExplicitResultType()) { - // Visit the whole type. - TRY_TO(TraverseTypeLoc(TL)); - } else if (FunctionProtoTypeLoc Proto = TL.getAs()) { - if (S->hasExplicitParameters()) { - // Visit parameters. - for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I) { - TRY_TO(TraverseDecl(Proto.getParam(I))); - } - } else { - TRY_TO(TraverseTypeLoc(Proto.getReturnLoc())); + TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + FunctionProtoTypeLoc Proto = TL.castAs(); + + if (S->hasExplicitParameters() && S->hasExplicitResultType()) { + // Visit the whole type. + TRY_TO(TraverseTypeLoc(TL)); + } else { + if (S->hasExplicitParameters()) { + // Visit parameters. + for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I) { + TRY_TO(TraverseDecl(Proto.getParam(I))); } + } else if (S->hasExplicitResultType()) { + TRY_TO(TraverseTypeLoc(Proto.getReturnLoc())); } + + auto *T = Proto.getTypePtr(); + for (const auto &E : T->exceptions()) { + TRY_TO(TraverseType(E)); + } + + if (Expr *NE = T->getNoexceptExpr()) + TRY_TO(TraverseStmt(NE)); } TRY_TO(TraverseLambdaBody(S)); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 139158648313..2a6ac2db30d3 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3012,6 +3012,8 @@ public: bool hasNoexceptExceptionSpec() const { return isNoexceptExceptionSpec(getExceptionSpecType()); } + /// \brief Return whether this function has a dependent exception spec. + bool hasDependentExceptionSpec() const; /// \brief Result type of getNoexceptSpec(). enum NoexceptResult { NR_NoNoexcept, ///< There is no noexcept specifier. @@ -5247,8 +5249,8 @@ template const T *Type::castAs() const { ArrayType_cannot_be_used_with_getAs at; (void) at; - assert(isa(CanonicalType)); if (const T *ty = dyn_cast(this)) return ty; + assert(isa(CanonicalType)); return cast(getUnqualifiedDesugaredType()); } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 372365452ecf..7c8535c92e86 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4036,7 +4036,8 @@ public: /// \brief Check the given exception-specification and update the /// exception specification information with the results. - void checkExceptionSpecification(ExceptionSpecificationType EST, + void checkExceptionSpecification(bool IsTopLevel, + ExceptionSpecificationType EST, ArrayRef DynamicExceptions, ArrayRef DynamicExceptionRanges, Expr *NoexceptExpr, @@ -6675,6 +6676,8 @@ public: DeclarationName Entity, CXXRecordDecl *ThisContext, unsigned ThisTypeQuals); + void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, + const MultiLevelTemplateArgumentList &Args); ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D, const MultiLevelTemplateArgumentList &TemplateArgs, int indexAdjustment, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index bfd726c165e5..ad8a1ed6708f 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1623,9 +1623,9 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, QualType *exnSlot = argSlot + NumParams; unsigned I = 0; for (QualType ExceptionType : epi.ExceptionSpec.Exceptions) { - if (ExceptionType->isDependentType()) - setDependent(); - else if (ExceptionType->isInstantiationDependentType()) + // Note that a dependent exception specification does *not* make + // a type dependent; it's not even part of the C++ type system. + if (ExceptionType->isInstantiationDependentType()) setInstantiationDependent(); if (ExceptionType->containsUnexpandedParameterPack()) @@ -1639,11 +1639,12 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, *noexSlot = epi.ExceptionSpec.NoexceptExpr; if (epi.ExceptionSpec.NoexceptExpr) { - if (epi.ExceptionSpec.NoexceptExpr->isValueDependent() - || epi.ExceptionSpec.NoexceptExpr->isTypeDependent()) - setDependent(); - else if (epi.ExceptionSpec.NoexceptExpr->isInstantiationDependent()) + if (epi.ExceptionSpec.NoexceptExpr->isValueDependent() || + epi.ExceptionSpec.NoexceptExpr->isInstantiationDependent()) setInstantiationDependent(); + + if (epi.ExceptionSpec.NoexceptExpr->containsUnexpandedParameterPack()) + setContainsUnexpandedParameterPack(); } } else if (getExceptionSpecType() == EST_Uninstantiated) { // Store the function decl from which we will resolve our @@ -1669,6 +1670,18 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, } } +bool FunctionProtoType::hasDependentExceptionSpec() const { + if (Expr *NE = getNoexceptExpr()) + return NE->isValueDependent(); + for (QualType ET : exceptions()) + // A pack expansion with a non-dependent pattern is still dependent, + // because we don't know whether the pattern is in the exception spec + // or not (that depends on whether the pack has 0 expansions). + if (ET->isDependentType() || ET->getAs()) + return true; + return false; +} + FunctionProtoType::NoexceptResult FunctionProtoType::getNoexceptSpec(const ASTContext &ctx) const { ExceptionSpecificationType est = getExceptionSpecType(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ed15012a1602..d1f337f5b998 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -13168,13 +13168,12 @@ bool Sema::checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method) { return false; } -void -Sema::checkExceptionSpecification(ExceptionSpecificationType EST, - ArrayRef DynamicExceptions, - ArrayRef DynamicExceptionRanges, - Expr *NoexceptExpr, - SmallVectorImpl &Exceptions, - FunctionProtoType::ExceptionSpecInfo &ESI) { +void Sema::checkExceptionSpecification( + bool IsTopLevel, ExceptionSpecificationType EST, + ArrayRef DynamicExceptions, + ArrayRef DynamicExceptionRanges, Expr *NoexceptExpr, + SmallVectorImpl &Exceptions, + FunctionProtoType::ExceptionSpecInfo &ESI) { Exceptions.clear(); ESI.Type = EST; if (EST == EST_Dynamic) { @@ -13183,13 +13182,15 @@ Sema::checkExceptionSpecification(ExceptionSpecificationType EST, // FIXME: Preserve type source info. QualType ET = GetTypeFromParser(DynamicExceptions[ei]); - SmallVector Unexpanded; - collectUnexpandedParameterPacks(ET, Unexpanded); - if (!Unexpanded.empty()) { - DiagnoseUnexpandedParameterPacks(DynamicExceptionRanges[ei].getBegin(), - UPPC_ExceptionType, - Unexpanded); - continue; + if (IsTopLevel) { + SmallVector Unexpanded; + collectUnexpandedParameterPacks(ET, Unexpanded); + if (!Unexpanded.empty()) { + DiagnoseUnexpandedParameterPacks( + DynamicExceptionRanges[ei].getBegin(), UPPC_ExceptionType, + Unexpanded); + continue; + } } // Check that the type is valid for an exception spec, and @@ -13208,7 +13209,8 @@ Sema::checkExceptionSpecification(ExceptionSpecificationType EST, NoexceptExpr->getType()->getCanonicalTypeUnqualified() == Context.BoolTy) && "Parser should have made sure that the expression is boolean"); - if (NoexceptExpr && DiagnoseUnexpandedParameterPack(NoexceptExpr)) { + if (IsTopLevel && NoexceptExpr && + DiagnoseUnexpandedParameterPack(NoexceptExpr)) { ESI.Type = EST_BasicNoexcept; return; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index e4963b13d641..4e4de5a3e7f0 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -720,10 +720,11 @@ static bool CheckSpecForTypesEquivalent(Sema &S, /// assignment and override compatibility check. We do not check the parameters /// of parameter function pointers recursively, as no sane programmer would /// even be able to write such a function type. -bool Sema::CheckParamExceptionSpec(const PartialDiagnostic & NoteID, - const FunctionProtoType *Target, SourceLocation TargetLoc, - const FunctionProtoType *Source, SourceLocation SourceLoc) -{ +bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &NoteID, + const FunctionProtoType *Target, + SourceLocation TargetLoc, + const FunctionProtoType *Source, + SourceLocation SourceLoc) { if (CheckSpecForTypesEquivalent( *this, PDiag(diag::err_deep_exception_specs_differ) << 0, PDiag(), Target->getReturnType(), TargetLoc, Source->getReturnType(), @@ -744,23 +745,30 @@ bool Sema::CheckParamExceptionSpec(const PartialDiagnostic & NoteID, return false; } -bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) -{ +bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) { // First we check for applicability. // Target type must be a function, function pointer or function reference. const FunctionProtoType *ToFunc = GetUnderlyingFunction(ToType); - if (!ToFunc) + if (!ToFunc || ToFunc->hasDependentExceptionSpec()) return false; // SourceType must be a function or function pointer. const FunctionProtoType *FromFunc = GetUnderlyingFunction(From->getType()); - if (!FromFunc) + if (!FromFunc || FromFunc->hasDependentExceptionSpec()) return false; // Now we've got the correct types on both sides, check their compatibility. // This means that the source of the conversion can only throw a subset of // the exceptions of the target, and any exception specs on arguments or // return types must be equivalent. + // + // FIXME: If there is a nested dependent exception specification, we should + // not be checking it here. This is fine: + // template void f() { + // void (*p)(void (*) throw(T)); + // void (*q)(void (*) throw(int)) = p; + // } + // ... because it might be instantiated with T=int. return CheckExceptionSpecSubset(PDiag(diag::err_incompatible_exception_specs), PDiag(), ToFunc, From->getSourceRange().getBegin(), diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 9c1ffb0c6547..a9aae009401f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -791,11 +791,17 @@ namespace { ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E); QualType TransformFunctionProtoType(TypeLocBuilder &TLB, - FunctionProtoTypeLoc TL); + FunctionProtoTypeLoc TL) { + // Call the base version; it will forward to our overridden version below. + return inherited::TransformFunctionProtoType(TLB, TL); + } + + template QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, - unsigned ThisTypeQuals); + unsigned ThisTypeQuals, + Fn TransformExceptionSpec); ParmVarDecl *TransformFunctionTypeParam(ParmVarDecl *OldParm, int indexAdjustment, @@ -1327,21 +1333,16 @@ ExprResult TemplateInstantiator::TransformCXXDefaultArgExpr( E->getParam()); } -QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB, - FunctionProtoTypeLoc TL) { - // We need a local instantiation scope for this function prototype. - LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); - return inherited::TransformFunctionProtoType(TLB, TL); -} - +template QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, - unsigned ThisTypeQuals) { + unsigned ThisTypeQuals, + Fn TransformExceptionSpec) { // We need a local instantiation scope for this function prototype. LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); - return inherited::TransformFunctionProtoType(TLB, TL, ThisContext, - ThisTypeQuals); + return inherited::TransformFunctionProtoType( + TLB, TL, ThisContext, ThisTypeQuals, TransformExceptionSpec); } ParmVarDecl * @@ -1576,7 +1577,8 @@ static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) { /// A form of SubstType intended specifically for instantiating the /// type of a FunctionDecl. Its purpose is solely to force the -/// instantiation of default-argument expressions. +/// instantiation of default-argument expressions and to avoid +/// instantiating an exception-specification. TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T, const MultiLevelTemplateArgumentList &Args, SourceLocation Loc, @@ -1599,9 +1601,17 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T, QualType Result; - if (FunctionProtoTypeLoc Proto = TL.getAs()) { - Result = Instantiator.TransformFunctionProtoType(TLB, Proto, ThisContext, - ThisTypeQuals); + if (FunctionProtoTypeLoc Proto = + TL.IgnoreParens().getAs()) { + // Instantiate the type, other than its exception specification. The + // exception specification is instantiated in InitFunctionInstantiation + // once we've built the FunctionDecl. + // FIXME: Set the exception specification to EST_Uninstantiated here, + // instead of rebuilding the function type again later. + Result = Instantiator.TransformFunctionProtoType( + TLB, Proto, ThisContext, ThisTypeQuals, + [](FunctionProtoType::ExceptionSpecInfo &ESI, + bool &Changed) { return false; }); } else { Result = Instantiator.TransformType(TLB, TL); } @@ -1611,6 +1621,26 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T, return TLB.getTypeSourceInfo(Context, Result); } +void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, + const MultiLevelTemplateArgumentList &Args) { + FunctionProtoType::ExceptionSpecInfo ESI = + Proto->getExtProtoInfo().ExceptionSpec; + assert(ESI.Type != EST_Uninstantiated); + + TemplateInstantiator Instantiator(*this, Args, New->getLocation(), + New->getDeclName()); + + SmallVector ExceptionStorage; + bool Changed = false; + if (Instantiator.TransformExceptionSpec( + New->getTypeSourceInfo()->getTypeLoc().getLocEnd(), ESI, + ExceptionStorage, Changed)) + // On error, recover by dropping the exception specification. + ESI.Type = EST_None; + + UpdateExceptionSpec(New, ESI); +} + ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, const MultiLevelTemplateArgumentList &TemplateArgs, int indexAdjustment, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ec8c08ddf535..cc2460c617ad 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3056,7 +3056,7 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, /// 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, +static bool addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function, const FunctionDecl *PatternDecl, LocalInstantiationScope &Scope, const MultiLevelTemplateArgumentList &TemplateArgs) { @@ -3067,15 +3067,22 @@ static void addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function, // Simple case: not a parameter pack. assert(FParamIdx < Function->getNumParams()); ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); + FunctionParam->setDeclName(PatternParam->getDeclName()); // If the parameter's type is not dependent, update it to match the type // in the pattern. They can differ in top-level cv-qualifiers, and we want // the pattern's type here. If the type is dependent, they can't differ, - // per core issue 1668. + // per core issue 1668. Substitute into the type from the pattern, in case + // it's instantiation-dependent. // FIXME: Updating the type to work around this is at best fragile. - if (!PatternDecl->getType()->isDependentType()) - FunctionParam->setType(PatternParam->getType()); + if (!PatternDecl->getType()->isDependentType()) { + QualType T = S.SubstType(PatternParam->getType(), TemplateArgs, + FunctionParam->getLocation(), + FunctionParam->getDeclName()); + if (T.isNull()) + return true; + FunctionParam->setType(T); + } - FunctionParam->setDeclName(PatternParam->getDeclName()); Scope.InstantiatedLocal(PatternParam, FunctionParam); ++FParamIdx; continue; @@ -3087,136 +3094,27 @@ static void addInstantiatedParametersToScope(Sema &S, FunctionDecl *Function, = S.getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs); assert(NumArgumentsInExpansion && "should only be called when all template arguments are known"); + QualType PatternType = + PatternParam->getType()->castAs()->getPattern(); for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) { ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); - if (!PatternDecl->getType()->isDependentType()) - FunctionParam->setType(PatternParam->getType()); - FunctionParam->setDeclName(PatternParam->getDeclName()); + if (!PatternDecl->getType()->isDependentType()) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, Arg); + QualType T = S.SubstType(PatternType, TemplateArgs, + FunctionParam->getLocation(), + FunctionParam->getDeclName()); + if (T.isNull()) + return true; + FunctionParam->setType(T); + } + Scope.InstantiatedLocalPackArg(PatternParam, FunctionParam); ++FParamIdx; } } -} -static void InstantiateExceptionSpec(Sema &SemaRef, FunctionDecl *New, - const FunctionProtoType *Proto, - const MultiLevelTemplateArgumentList &TemplateArgs) { - assert(Proto->getExceptionSpecType() != EST_Uninstantiated); - - // 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 = nullptr; - unsigned ThisTypeQuals = 0; - if (CXXMethodDecl *Method = dyn_cast(New)) { - ThisContext = Method->getParent(); - ThisTypeQuals = Method->getTypeQualifiers(); - } - Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, ThisTypeQuals, - SemaRef.getLangOpts().CPlusPlus11); - - // 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; - 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 = nullptr; - 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.get(); - if (!NoexceptExpr->isTypeDependent() && - !NoexceptExpr->isValueDependent()) - NoexceptExpr - = SemaRef.VerifyIntegerConstantExpression(NoexceptExpr, - nullptr, diag::err_noexcept_needs_constant_expression, - /*AllowFold*/ false).get(); - } - } - - FunctionProtoType::ExceptionSpecInfo ESI; - ESI.Type = Proto->getExceptionSpecType(); - ESI.Exceptions = Exceptions; - ESI.NoexceptExpr = NoexceptExpr; - - SemaRef.UpdateExceptionSpec(New, ESI); + return false; } void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, @@ -3243,11 +3141,14 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation, getTemplateInstantiationArgs(Decl, nullptr, /*RelativeToPrimary*/true); FunctionDecl *Template = Proto->getExceptionSpecTemplate(); - addInstantiatedParametersToScope(*this, Decl, Template, Scope, TemplateArgs); + if (addInstantiatedParametersToScope(*this, Decl, Template, Scope, + TemplateArgs)) { + UpdateExceptionSpec(Decl, EST_None); + return; + } - ::InstantiateExceptionSpec(*this, Decl, - Template->getType()->castAs(), - TemplateArgs); + SubstExceptionSpec(Decl, Template->getType()->castAs(), + TemplateArgs); } /// \brief Initializes the common fields of an instantiation function @@ -3316,7 +3217,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, New->setType(SemaRef.Context.getFunctionType( NewProto->getReturnType(), NewProto->getParamTypes(), EPI)); } else { - ::InstantiateExceptionSpec(SemaRef, New, Proto, TemplateArgs); + SemaRef.SubstExceptionSpec(New, Proto, TemplateArgs); } } @@ -3506,8 +3407,9 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(Function, nullptr, false, PatternDecl); - addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope, - TemplateArgs); + if (addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope, + TemplateArgs)) + return; // If this is a constructor, instantiate the member initializers. if (const CXXConstructorDecl *Ctor = diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3ce9489fdd28..f395e10caa04 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2989,7 +2989,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, NoexceptExpr = FTI.NoexceptExpr; } - S.checkExceptionSpecification(FTI.getExceptionSpecType(), + S.checkExceptionSpecification(D.isFunctionDeclarationContext(), + FTI.getExceptionSpecType(), DynamicExceptions, DynamicExceptionRanges, NoexceptExpr, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 28890f59ce2f..75783f29e647 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -563,10 +563,17 @@ public: QualType Transform##CLASS##Type(TypeLocBuilder &TLB, CLASS##TypeLoc T); #include "clang/AST/TypeLocNodes.def" + template QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, - unsigned ThisTypeQuals); + unsigned ThisTypeQuals, + Fn TransformExceptionSpec); + + bool TransformExceptionSpec(SourceLocation Loc, + FunctionProtoType::ExceptionSpecInfo &ESI, + SmallVectorImpl &Exceptions, + bool &Changed); StmtResult TransformSEHHandler(Stmt *Handler); @@ -4550,15 +4557,19 @@ template QualType TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL) { - return getDerived().TransformFunctionProtoType(TLB, TL, nullptr, 0); + SmallVector ExceptionStorage; + return getDerived().TransformFunctionProtoType( + TLB, TL, nullptr, 0, + [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) { + return TransformExceptionSpec(TL.getBeginLoc(), ESI, ExceptionStorage, + Changed); + }); } -template -QualType -TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, - FunctionProtoTypeLoc TL, - CXXRecordDecl *ThisContext, - unsigned ThisTypeQuals) { +template template +QualType TreeTransform::TransformFunctionProtoType( + TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, + unsigned ThisTypeQuals, Fn TransformExceptionSpec) { // Transform the parameters and return type. // // We are required to instantiate the params and return type in source order. @@ -4603,15 +4614,21 @@ TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, return QualType(); } - // FIXME: Need to transform the exception-specification too. + FunctionProtoType::ExtProtoInfo EPI = T->getExtProtoInfo(); + + bool EPIChanged = false; + if (TransformExceptionSpec(EPI.ExceptionSpec, EPIChanged)) + return QualType(); + + // FIXME: Need to transform ConsumedParameters for variadic template + // expansion. QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || ResultType != T->getReturnType() || T->getNumParams() != ParamTypes.size() || !std::equal(T->param_type_begin(), T->param_type_end(), - ParamTypes.begin())) { - Result = getDerived().RebuildFunctionProtoType(ResultType, ParamTypes, - T->getExtProtoInfo()); + ParamTypes.begin()) || EPIChanged) { + Result = getDerived().RebuildFunctionProtoType(ResultType, ParamTypes, EPI); if (Result.isNull()) return QualType(); } @@ -4627,6 +4644,107 @@ TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, return Result; } +template +bool TreeTransform::TransformExceptionSpec( + SourceLocation Loc, FunctionProtoType::ExceptionSpecInfo &ESI, + SmallVectorImpl &Exceptions, bool &Changed) { + assert(ESI.Type != EST_Uninstantiated && ESI.Type != EST_Unevaluated); + + // Instantiate a dynamic noexcept expression, if any. + if (ESI.Type == EST_ComputedNoexcept) { + EnterExpressionEvaluationContext Unevaluated(getSema(), + Sema::ConstantEvaluated); + ExprResult NoexceptExpr = getDerived().TransformExpr(ESI.NoexceptExpr); + if (NoexceptExpr.isInvalid()) + return true; + + NoexceptExpr = getSema().CheckBooleanCondition( + NoexceptExpr.get(), NoexceptExpr.get()->getLocStart()); + if (NoexceptExpr.isInvalid()) + return true; + + if (!NoexceptExpr.get()->isValueDependent()) { + NoexceptExpr = getSema().VerifyIntegerConstantExpression( + NoexceptExpr.get(), nullptr, + diag::err_noexcept_needs_constant_expression, + /*AllowFold*/false); + if (NoexceptExpr.isInvalid()) + return true; + } + + if (ESI.NoexceptExpr != NoexceptExpr.get()) + Changed = true; + ESI.NoexceptExpr = NoexceptExpr.get(); + } + + if (ESI.Type != EST_Dynamic) + return false; + + // Instantiate a dynamic exception specification's type. + for (QualType T : ESI.Exceptions) { + if (const PackExpansionType *PackExpansion = + T->getAs()) { + Changed = true; + + // We have a pack expansion. Instantiate it. + SmallVector Unexpanded; + SemaRef.collectUnexpandedParameterPacks(PackExpansion->getPattern(), + Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + + // Determine whether the set of unexpanded parameter packs can and + // should + // be expanded. + bool Expand = false; + bool RetainExpansion = false; + Optional NumExpansions = PackExpansion->getNumExpansions(); + // FIXME: Track the location of the ellipsis (and track source location + // information for the types in the exception specification in general). + if (getDerived().TryExpandParameterPacks( + Loc, SourceRange(), Unexpanded, Expand, + RetainExpansion, NumExpansions)) + return true; + + 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(getSema(), -1); + QualType U = getDerived().TransformType(PackExpansion->getPattern()); + if (U.isNull()) + return true; + + U = SemaRef.Context.getPackExpansionType(U, NumExpansions); + Exceptions.push_back(U); + continue; + } + + // Substitute into the pack expansion pattern for each slice of the + // pack. + for (unsigned ArgIdx = 0; ArgIdx != *NumExpansions; ++ArgIdx) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), ArgIdx); + + QualType U = getDerived().TransformType(PackExpansion->getPattern()); + if (U.isNull() || SemaRef.CheckSpecifiedExceptionType(U, Loc)) + return true; + + Exceptions.push_back(U); + } + } else { + QualType U = getDerived().TransformType(T); + if (U.isNull() || SemaRef.CheckSpecifiedExceptionType(U, Loc)) + return true; + if (T != U) + Changed = true; + + Exceptions.push_back(U); + } + } + + ESI.Exceptions = Exceptions; + return false; +} + template QualType TreeTransform::TransformFunctionNoProtoType( TypeLocBuilder &TLB, @@ -9005,9 +9123,13 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { // transformed parameters. TypeLocBuilder NewCallOpTLBuilder; - QualType NewCallOpType = TransformFunctionProtoType(NewCallOpTLBuilder, - OldCallOpFPTL, - nullptr, 0); + SmallVector ExceptionStorage; + QualType NewCallOpType = TransformFunctionProtoType( + NewCallOpTLBuilder, OldCallOpFPTL, nullptr, 0, + [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) { + return TransformExceptionSpec(OldCallOpFPTL.getBeginLoc(), ESI, + ExceptionStorage, Changed); + }); NewCallOpTSI = NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType); } diff --git a/clang/test/CXX/except/except.spec/p1.cpp b/clang/test/CXX/except/except.spec/p1.cpp index a32f37d55208..fa53c9f5d23f 100644 --- a/clang/test/CXX/except/except.spec/p1.cpp +++ b/clang/test/CXX/except/except.spec/p1.cpp @@ -77,5 +77,12 @@ 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 exception specification for 'f' requested here}} + template void f() { + int (*p)() noexcept(1/X); // 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 exception specification for 'f'}} + f<0>(); // expected-note{{in instantiation of function template specialization}} + } } diff --git a/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp b/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp index a376f0e5fd5b..f62ef61758a4 100644 --- a/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp +++ b/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp @@ -58,6 +58,13 @@ namespace dr1330_example { S().f(); // ok S().f(); // expected-note {{instantiation of exception spec}} } + + template + struct U { + void f() noexcept(T::error); + void (g)() noexcept(T::error); + }; + U uint; // ok } namespace core_19754_example { @@ -137,3 +144,37 @@ namespace PR12763 { }; void X::g() {} // expected-note {{in instantiation of}} } + +namespace Variadic { + template void check() { static_assert(B, ""); } + template void check() { static_assert(B, ""); check(); } + + template void consume(T...); + + template void f(void (*...p)() throw (T)) { + void (*q[])() = { p... }; + consume((p(),0)...); + } + template void g(void (*...p)() noexcept (B)) { + consume((p(),0)...); + check(); + } + template void i() { + consume([]() throw(T) {} ...); + consume([]() noexcept(sizeof(T) == 4) {} ...); + } + template void j() { + consume([](void (*p)() noexcept(B)) { + void (*q)() noexcept = p; // expected-error {{not superset of source}} + } ...); + } + + void z() { + f(nullptr, nullptr, nullptr); + g(nullptr, nullptr, nullptr); + i(); + j(); + j(); // expected-note {{in instantiation of}} + } + +} diff --git a/clang/test/SemaTemplate/instantiate-exception-spec.cpp b/clang/test/SemaTemplate/instantiate-exception-spec.cpp index 993ee8dfae1f..d3411722283c 100644 --- a/clang/test/SemaTemplate/instantiate-exception-spec.cpp +++ b/clang/test/SemaTemplate/instantiate-exception-spec.cpp @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -verify %s -DERRORS +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -emit-llvm-only %s +#ifdef ERRORS template void f1(T*) throw(T); // expected-error{{incomplete type 'Incomplete' is not allowed in exception specification}} struct Incomplete; // expected-note{{forward}} @@ -7,3 +9,20 @@ void test_f1(Incomplete *incomplete_p, int *int_p) { f1(int_p); f1(incomplete_p); // expected-note{{instantiation of}} } +#endif + +template void f(void (*p)() throw(T)) { +#ifdef ERRORS + void (*q)() throw(char) = p; // expected-error {{target exception spec}} + + extern void (*p2)() throw(T); + void (*q2)() throw(char) = p2; // expected-error {{target exception spec}} + + extern void (*p3)() throw(char); + void (*q3)() throw(T) = p3; // expected-error {{target exception spec}} + + void (*q4)() throw(T) = p2; // ok +#endif + p(); +} +void g() { f(0); } // expected-note {{instantiation of}}