From: Douglas Gregor Date: Tue, 4 Jan 2011 23:35:54 +0000 (+0000) Subject: Improve our handling of non-type template parameters in partial X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=54c53cca105ed595e12fecf04e415c3712bda936;p=clang Improve our handling of non-type template parameters in partial specializations. We weren't dealing with any of the cases where the type of the non-type template argument differs from the type of the corresponding template parameter in the primary template. We would think that the template parameter in the partial specialization was not deducible (and warn about it, incorrectly), then fail to convert a deduced parameter to the type of the template parameter in the partial specialization (which may involve truncation, among other things). Fixes PR8905. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122851 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c11ab161a5..5235607468 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3011,7 +3011,7 @@ public: bool CheckTemplateArgument(NamedDecl *Param, const TemplateArgumentLoc &Arg, - TemplateDecl *Template, + NamedDecl *Template, SourceLocation TemplateLoc, SourceLocation RAngleLoc, llvm::SmallVectorImpl &Converted, @@ -3557,9 +3557,10 @@ public: /// \brief The point of instantiation within the source code. SourceLocation PointOfInstantiation; - /// \brief The template in which we are performing the instantiation, - /// for substitutions of prior template arguments. - TemplateDecl *Template; + /// \brief The template (or partial specialization) in which we are + /// performing the instantiation, for substitutions of prior template + /// arguments. + NamedDecl *Template; /// \brief The entity that is being instantiated. uintptr_t Entity; @@ -3745,14 +3746,14 @@ public: /// \brief Note that we are substituting prior template arguments into a /// non-type or template template parameter. InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - TemplateDecl *Template, + NamedDecl *Template, NonTypeTemplateParmDecl *Param, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs, SourceRange InstantiationRange); InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - TemplateDecl *Template, + NamedDecl *Template, TemplateTemplateParmDecl *Param, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 1835899e80..419f59d588 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2160,7 +2160,7 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template, /// template parameter. bool Sema::CheckTemplateArgument(NamedDecl *Param, const TemplateArgumentLoc &Arg, - TemplateDecl *Template, + NamedDecl *Template, SourceLocation TemplateLoc, SourceLocation RAngleLoc, llvm::SmallVectorImpl &Converted, @@ -3940,6 +3940,10 @@ static bool CheckNonTypeClassTemplatePartialSpecializationArgs(Sema &S, // We can have a pack expansion of any of the bullets below. if (PackExpansionExpr *Expansion = dyn_cast(ArgExpr)) ArgExpr = Expansion->getPattern(); + + // Strip off any implicit casts we added as part of type checking. + while (ImplicitCastExpr *ICE = dyn_cast(ArgExpr)) + ArgExpr = ICE->getSubExpr(); // C++ [temp.class.spec]p8: // A non-type argument is non-specialized if it is the name of a @@ -3950,7 +3954,7 @@ static bool CheckNonTypeClassTemplatePartialSpecializationArgs(Sema &S, // specialized non-type arguments, so skip any non-specialized // arguments. if (DeclRefExpr *DRE = dyn_cast(ArgExpr)) - if (llvm::isa(DRE->getDecl())) + if (isa(DRE->getDecl())) continue; // C++ [temp.class.spec]p9: diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 3c3f3d7889..09bfcf5c46 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1314,6 +1314,114 @@ static bool isSameTemplateArg(ASTContext &Context, return false; } +/// \brief Allocate a TemplateArgumentLoc where all locations have +/// been initialized to the given location. +/// +/// \param S The semantic analysis object. +/// +/// \param The template argument we are producing template argument +/// location information for. +/// +/// \param NTTPType For a declaration template argument, the type of +/// the non-type template parameter that corresponds to this template +/// argument. +/// +/// \param Loc The source location to use for the resulting template +/// argument. +static TemplateArgumentLoc +getTrivialTemplateArgumentLoc(Sema &S, + const TemplateArgument &Arg, + QualType NTTPType, + SourceLocation Loc) { + switch (Arg.getKind()) { + case TemplateArgument::Null: + llvm_unreachable("Can't get a NULL template argument here"); + break; + + case TemplateArgument::Type: + return TemplateArgumentLoc(Arg, + S.Context.getTrivialTypeSourceInfo(Arg.getAsType(), Loc)); + + case TemplateArgument::Declaration: { + Expr *E + = S.BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc) + .takeAs(); + return TemplateArgumentLoc(TemplateArgument(E), E); + } + + case TemplateArgument::Integral: { + Expr *E + = S.BuildExpressionFromIntegralTemplateArgument(Arg, Loc).takeAs(); + return TemplateArgumentLoc(TemplateArgument(E), E); + } + + case TemplateArgument::Template: + return TemplateArgumentLoc(Arg, SourceRange(), Loc); + + case TemplateArgument::Expression: + return TemplateArgumentLoc(Arg, Arg.getAsExpr()); + + case TemplateArgument::Pack: + return TemplateArgumentLoc(Arg, TemplateArgumentLocInfo()); + } + + return TemplateArgumentLoc(); +} + + +/// \brief Convert the given deduced template argument and add it to the set of +/// fully-converted template arguments. +static bool ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, + DeducedTemplateArgument Arg, + NamedDecl *Template, + QualType NTTPType, + TemplateDeductionInfo &Info, + bool InFunctionTemplate, + llvm::SmallVectorImpl &Output) { + if (Arg.getKind() == TemplateArgument::Pack) { + // This is a template argument pack, so check each of its arguments against + // the template parameter. + llvm::SmallVector PackedArgsBuilder; + for (TemplateArgument::pack_iterator PA = Arg.pack_begin(), + PAEnd = Arg.pack_end(); + PA != PAEnd; ++PA) { + DeducedTemplateArgument InnerArg(*PA); + InnerArg.setDeducedFromArrayBound(Arg.wasDeducedFromArrayBound()); + if (ConvertDeducedTemplateArgument(S, Param, InnerArg, Template, + NTTPType, Info, + InFunctionTemplate, PackedArgsBuilder)) + return true; + } + + // Create the resulting argument pack. + TemplateArgument *PackedArgs = 0; + if (!PackedArgsBuilder.empty()) { + PackedArgs = new (S.Context) TemplateArgument[PackedArgsBuilder.size()]; + std::copy(PackedArgsBuilder.begin(), PackedArgsBuilder.end(), PackedArgs); + } + Output.push_back(TemplateArgument(PackedArgs, PackedArgsBuilder.size())); + return false; + } + + // Convert the deduced template argument into a template + // argument that we can check, almost as if the user had written + // the template argument explicitly. + TemplateArgumentLoc ArgLoc = getTrivialTemplateArgumentLoc(S, Arg, NTTPType, + Info.getLocation()); + + // Check the template argument, converting it as necessary. + return S.CheckTemplateArgument(Param, ArgLoc, + Template, + Template->getLocation(), + Template->getSourceRange().getEnd(), + Output, + InFunctionTemplate + ? (Arg.wasDeducedFromArrayBound() + ? Sema::CTAK_DeducedFromArrayBound + : Sema::CTAK_Deduced) + : Sema::CTAK_Specified); +} + /// Complete template argument deduction for a class template partial /// specialization. static Sema::TemplateDeductionResult @@ -1333,12 +1441,32 @@ FinishTemplateArgumentDeduction(Sema &S, llvm::SmallVector Builder; TemplateParameterList *PartialParams = Partial->getTemplateParameters(); for (unsigned I = 0, N = PartialParams->size(); I != N; ++I) { + NamedDecl *Param = PartialParams->getParam(I); if (Deduced[I].isNull()) { - Info.Param = makeTemplateParameter(PartialParams->getParam(I)); + Info.Param = makeTemplateParameter(Param); return Sema::TDK_Incomplete; } - Builder.push_back(Deduced[I]); + // We have deduced this argument, so it still needs to be + // checked and converted. + + // First, for a non-type template parameter type that is + // initialized by a declaration, we need the type of the + // corresponding non-type template parameter. + QualType NTTPType; + if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(Param)) + NTTPType = NTTP->getType(); + + if (ConvertDeducedTemplateArgument(S, Param, Deduced[I], + Partial, NTTPType, Info, false, + Builder)) { + Info.Param = makeTemplateParameter(Param); + // FIXME: These template arguments are temporary. Free them! + Info.reset(TemplateArgumentList::CreateCopy(S.Context, Builder.data(), + Builder.size())); + return Sema::TDK_SubstitutionFailure; + } } // Form the template argument list from the deduced template arguments. @@ -1380,23 +1508,39 @@ FinishTemplateArgumentDeduction(Sema &S, llvm::SmallVector ConvertedInstArgs; if (S.CheckTemplateArgumentList(ClassTemplate, Partial->getLocation(), - InstArgs, false, ConvertedInstArgs)) + InstArgs, false, ConvertedInstArgs)) return Sema::TDK_SubstitutionFailure; - for (unsigned I = 0, E = ConvertedInstArgs.size(); I != E; ++I) { + TemplateParameterList *TemplateParams + = ClassTemplate->getTemplateParameters(); + for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { TemplateArgument InstArg = ConvertedInstArgs.data()[I]; - Decl *Param = const_cast( - ClassTemplate->getTemplateParameters()->getParam(I)); + Decl *Param = TemplateParams->getParam(I); - if (InstArg.getKind() == TemplateArgument::Expression) { + if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(Param)) { // When the argument is an expression, check the expression result // against the actual template parameter to get down to the canonical // template argument. - // FIXME: Variadic templates. - Expr *InstExpr = InstArg.getAsExpr(); - if (NonTypeTemplateParmDecl *NTTP - = dyn_cast(Param)) { + Expr *InstExpr = 0; + if (InstArg.getKind() == TemplateArgument::Expression) + InstExpr = InstArg.getAsExpr(); + else if (InstArg.getKind() == TemplateArgument::Integral) { + ExprResult InstExprFromArg + = S.BuildExpressionFromIntegralTemplateArgument(InstArg, + Info.getLocation()); + if (InstExprFromArg.isInvalid()) { + Info.Param = makeTemplateParameter(Param); + Info.FirstArg = InstArg; + return Sema::TDK_SubstitutionFailure; + } + + InstExpr = InstExprFromArg.get(); + } + + if (InstExpr) { + // FIXME: Variadic templates. if (S.CheckTemplateArgument(NTTP, NTTP->getType(), InstExpr, InstArg)) { Info.Param = makeTemplateParameter(Param); Info.FirstArg = Partial->getTemplateArgs()[I]; @@ -1615,109 +1759,6 @@ Sema::SubstituteExplicitTemplateArguments( return TDK_Success; } -/// \brief Allocate a TemplateArgumentLoc where all locations have -/// been initialized to the given location. -/// -/// \param S The semantic analysis object. -/// -/// \param The template argument we are producing template argument -/// location information for. -/// -/// \param NTTPType For a declaration template argument, the type of -/// the non-type template parameter that corresponds to this template -/// argument. -/// -/// \param Loc The source location to use for the resulting template -/// argument. -static TemplateArgumentLoc -getTrivialTemplateArgumentLoc(Sema &S, - const TemplateArgument &Arg, - QualType NTTPType, - SourceLocation Loc) { - switch (Arg.getKind()) { - case TemplateArgument::Null: - llvm_unreachable("Can't get a NULL template argument here"); - break; - - case TemplateArgument::Type: - return TemplateArgumentLoc(Arg, - S.Context.getTrivialTypeSourceInfo(Arg.getAsType(), Loc)); - - case TemplateArgument::Declaration: { - Expr *E - = S.BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc) - .takeAs(); - return TemplateArgumentLoc(TemplateArgument(E), E); - } - - case TemplateArgument::Integral: { - Expr *E - = S.BuildExpressionFromIntegralTemplateArgument(Arg, Loc).takeAs(); - return TemplateArgumentLoc(TemplateArgument(E), E); - } - - case TemplateArgument::Template: - return TemplateArgumentLoc(Arg, SourceRange(), Loc); - - case TemplateArgument::Expression: - return TemplateArgumentLoc(Arg, Arg.getAsExpr()); - - case TemplateArgument::Pack: - return TemplateArgumentLoc(Arg, TemplateArgumentLocInfo()); - } - - return TemplateArgumentLoc(); -} - -/// \brief Convert the given deduced template argument and add it to the set of -/// fully-converted template arguments. -static bool ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, - DeducedTemplateArgument Arg, - FunctionTemplateDecl *FunctionTemplate, - QualType NTTPType, - TemplateDeductionInfo &Info, - llvm::SmallVectorImpl &Output) { - if (Arg.getKind() == TemplateArgument::Pack) { - // This is a template argument pack, so check each of its arguments against - // the template parameter. - llvm::SmallVector PackedArgsBuilder; - for (TemplateArgument::pack_iterator PA = Arg.pack_begin(), - PAEnd = Arg.pack_end(); - PA != PAEnd; ++PA) { - DeducedTemplateArgument InnerArg(*PA); - InnerArg.setDeducedFromArrayBound(Arg.wasDeducedFromArrayBound()); - if (ConvertDeducedTemplateArgument(S, Param, InnerArg, FunctionTemplate, - NTTPType, Info, PackedArgsBuilder)) - return true; - } - - // Create the resulting argument pack. - TemplateArgument *PackedArgs = 0; - if (!PackedArgsBuilder.empty()) { - PackedArgs = new (S.Context) TemplateArgument[PackedArgsBuilder.size()]; - std::copy(PackedArgsBuilder.begin(), PackedArgsBuilder.end(), PackedArgs); - } - Output.push_back(TemplateArgument(PackedArgs, PackedArgsBuilder.size())); - return false; - } - - // Convert the deduced template argument into a template - // argument that we can check, almost as if the user had written - // the template argument explicitly. - TemplateArgumentLoc ArgLoc = getTrivialTemplateArgumentLoc(S, Arg, NTTPType, - Info.getLocation()); - - // Check the template argument, converting it as necessary. - return S.CheckTemplateArgument(Param, ArgLoc, - FunctionTemplate, - FunctionTemplate->getLocation(), - FunctionTemplate->getSourceRange().getEnd(), - Output, - Arg.wasDeducedFromArrayBound() - ? Sema::CTAK_DeducedFromArrayBound - : Sema::CTAK_Deduced); -} - /// \brief Finish template argument deduction for a function template, /// checking the deduced template arguments for completeness and forming /// the function template specialization. @@ -1791,7 +1832,7 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, if (ConvertDeducedTemplateArgument(*this, Param, Deduced[I], FunctionTemplate, NTTPType, Info, - Builder)) { + true, Builder)) { Info.Param = makeTemplateParameter(Param); // FIXME: These template arguments are temporary. Free them! Info.reset(TemplateArgumentList::CreateCopy(Context, Builder.data(), @@ -2953,6 +2994,10 @@ MarkUsedTemplateParameters(Sema &SemaRef, if (const PackExpansionExpr *Expansion = dyn_cast(E)) E = Expansion->getPattern(); + // Skip through any implicit casts we added while type-checking. + while (const ImplicitCastExpr *ICE = dyn_cast(E)) + E = ICE->getSubExpr(); + // FIXME: if !OnlyDeduced, we have to walk the whole subexpression to // find other occurrences of template parameters. const DeclRefExpr *DRE = dyn_cast(E); diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 16500d4207..5d724f3b70 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -263,7 +263,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(Sema &SemaRef, Sema::InstantiatingTemplate:: InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - TemplateDecl *Template, + NamedDecl *Template, NonTypeTemplateParmDecl *Param, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs, @@ -286,7 +286,7 @@ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, Sema::InstantiatingTemplate:: InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, - TemplateDecl *Template, + NamedDecl *Template, TemplateTemplateParmDecl *Param, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs, @@ -490,13 +490,19 @@ void Sema::PrintInstantiationStack() { std::string Name; if (!Parm->getName().empty()) Name = std::string(" '") + Parm->getName().str() + "'"; - + + TemplateParameterList *TemplateParams = 0; + if (TemplateDecl *Template = dyn_cast(Active->Template)) + TemplateParams = Template->getTemplateParameters(); + else + TemplateParams = + cast(Active->Template) + ->getTemplateParameters(); Diags.Report(Active->PointOfInstantiation, diag::note_prior_template_arg_substitution) << isa(Parm) << Name - << getTemplateArgumentBindingsText( - Active->Template->getTemplateParameters(), + << getTemplateArgumentBindingsText(TemplateParams, Active->TemplateArgs, Active->NumTemplateArgs) << Active->InstantiationRange; @@ -504,10 +510,17 @@ void Sema::PrintInstantiationStack() { } case ActiveTemplateInstantiation::DefaultTemplateArgumentChecking: { + TemplateParameterList *TemplateParams = 0; + if (TemplateDecl *Template = dyn_cast(Active->Template)) + TemplateParams = Template->getTemplateParameters(); + else + TemplateParams = + cast(Active->Template) + ->getTemplateParameters(); + Diags.Report(Active->PointOfInstantiation, diag::note_template_default_arg_checking) - << getTemplateArgumentBindingsText( - Active->Template->getTemplateParameters(), + << getTemplateArgumentBindingsText(TemplateParams, Active->TemplateArgs, Active->NumTemplateArgs) << Active->InstantiationRange; diff --git a/test/CXX/temp/temp.decls/temp.class.spec/p9.cpp b/test/CXX/temp/temp.decls/temp.class.spec/p9.cpp new file mode 100644 index 0000000000..2a3e914216 --- /dev/null +++ b/test/CXX/temp/temp.decls/temp.class.spec/p9.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// PR8905 +template +struct X { + static const bool value = 0; +}; + +template +struct X { + static const bool value = 1; +}; + +int check0[X<1, 2>::value == 0? 1 : -1]; +int check1[X<1, 1>::value == 1? 1 : -1]; + +template struct int_values { + static const unsigned value = 0; +}; + +template +struct int_values { + static const unsigned value = 1; +}; + +int check2[int_values<256, 12, 3>::value == 0? 1 : -1]; diff --git a/test/CXX/temp/temp.decls/temp.variadic/deduction.cpp b/test/CXX/temp/temp.decls/temp.variadic/deduction.cpp index f26b656be9..383e268054 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/deduction.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/deduction.cpp @@ -23,3 +23,28 @@ namespace DeductionForInstantiation { // FIXME: Extension of explicitly-specified arguments // template void f0(X<3, short&, int&, long&>); } + +namespace DeductionWithConversion { + template struct char_values { + static const unsigned value = 0; + }; + + template + struct char_values { + static const unsigned value = 1; + }; + + int check0[char_values<1, 12, 3>::value == 1? 1 : -1]; + + template struct int_values { + static const unsigned value = 0; + }; + + template + struct int_values { + static const unsigned value = 1; + }; + + int check1[int_values<256, 12, 3>::value == 0? 1 : -1]; + int check2[int_values<3, 12, 3>::value == 1? 1 : -1]; +}