From: Richard Smith Date: Tue, 21 Feb 2017 06:30:38 +0000 (+0000) Subject: PR32010: Fix template argument depth mixup when forming implicit constructor X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=735b586081cd2e830b82fe1edd19a173e3512a60;p=clang PR32010: Fix template argument depth mixup when forming implicit constructor template deduction guides for class template argument deduction. Ensure that we have a local instantiation scope for tracking the instantiated parameters. Additionally, unusually, we're substituting at depth 1 and leaving depth 0 alone; make sure that we don't reduce template parameter depth by 2 for inner parameters in the process. (This is probably also broken for alias templates in the case where they're expanded within a dependent context, but this patch doesn't fix that.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@295696 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index e83ec3cfcf..2879452f24 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -1479,6 +1479,7 @@ public: unsigned NumExpansions); using TemplateParmPosition::getDepth; + using TemplateParmPosition::setDepth; using TemplateParmPosition::getPosition; using TemplateParmPosition::setPosition; using TemplateParmPosition::getIndex; diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index 401bbbf1e5..644d55b93f 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -46,6 +46,10 @@ namespace clang { /// \brief The template argument lists, stored from the innermost template /// argument list (first) to the outermost template argument list (last). SmallVector TemplateArgumentLists; + + /// \brief The number of outer levels of template arguments that are not + /// being substituted. + unsigned NumRetainedOuterLevels = 0; public: /// \brief Construct an empty set of template argument lists. @@ -59,11 +63,19 @@ namespace clang { /// \brief Determine the number of levels in this template argument /// list. - unsigned getNumLevels() const { return TemplateArgumentLists.size(); } - + unsigned getNumLevels() const { + return TemplateArgumentLists.size() + NumRetainedOuterLevels; + } + + /// \brief Determine the number of substituted levels in this template + /// argument list. + unsigned getNumSubstitutedLevels() const { + return TemplateArgumentLists.size(); + } + /// \brief Retrieve the template argument at a given depth and index. const TemplateArgument &operator()(unsigned Depth, unsigned Index) const { - assert(Depth < TemplateArgumentLists.size()); + assert(NumRetainedOuterLevels <= Depth && Depth < getNumLevels()); assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].size()); return TemplateArgumentLists[getNumLevels() - Depth - 1][Index]; } @@ -73,7 +85,10 @@ namespace clang { /// /// There must exist a template argument list at the given depth. bool hasTemplateArgument(unsigned Depth, unsigned Index) const { - assert(Depth < TemplateArgumentLists.size()); + assert(Depth < getNumLevels()); + + if (Depth < NumRetainedOuterLevels) + return false; if (Index >= TemplateArgumentLists[getNumLevels() - Depth - 1].size()) return false; @@ -84,7 +99,7 @@ namespace clang { /// \brief Clear out a specific template argument. void setArgument(unsigned Depth, unsigned Index, TemplateArgument Arg) { - assert(Depth < TemplateArgumentLists.size()); + assert(NumRetainedOuterLevels <= Depth && Depth < getNumLevels()); assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].size()); const_cast( TemplateArgumentLists[getNumLevels() - Depth - 1][Index]) @@ -101,9 +116,18 @@ namespace clang { /// \brief Add a new outmost level to the multi-level template argument /// list. void addOuterTemplateArguments(ArgList Args) { + assert(!NumRetainedOuterLevels && + "substituted args outside retained args?"); TemplateArgumentLists.push_back(Args); } + /// \brief Add an outermost level that we are not substituting. We have no + /// arguments at this level, and do not remove it from the depth of inner + /// template parameters that we instantiate. + void addOuterRetainedLevel() { + ++NumRetainedOuterLevels; + } + /// \brief Retrieve the innermost template argument list. const ArgList &getInnermost() const { return TemplateArgumentLists.front(); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 4b4a47d63c..ad7b9e54f9 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1442,6 +1442,8 @@ struct ConvertConstructorToDeductionGuideTransform { CXXConstructorDecl *CD) { SmallVector SubstArgs; + LocalInstantiationScope Scope(SemaRef); + // C++ [over.match.class.deduct]p1: // -- For each constructor of the class template designated by the // template-name, a function template with the following properties: @@ -1463,7 +1465,7 @@ struct ConvertConstructorToDeductionGuideTransform { for (NamedDecl *Param : *InnerParams) { MultiLevelTemplateArgumentList Args; Args.addOuterTemplateArguments(SubstArgs); - Args.addOuterTemplateArguments(None); + Args.addOuterRetainedLevel(); NamedDecl *NewParam = transformTemplateParameter(Param, Args); if (!NewParam) return nullptr; @@ -1483,7 +1485,7 @@ struct ConvertConstructorToDeductionGuideTransform { MultiLevelTemplateArgumentList Args; if (FTD) { Args.addOuterTemplateArguments(SubstArgs); - Args.addOuterTemplateArguments(None); + Args.addOuterRetainedLevel(); } FunctionProtoTypeLoc FPTL = CD->getTypeSourceInfo()->getTypeLoc() @@ -1555,6 +1557,8 @@ private: if (InstantiatedDefaultArg) NewTTP->setDefaultArgument(InstantiatedDefaultArg); } + SemaRef.CurrentInstantiationScope->InstantiatedLocal(TemplateParam, + NewTTP); return NewTTP; } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 723a6c0e4d..f7535e6d24 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1121,6 +1121,23 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return E; TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition()); + + if (TemplateArgs.getNumLevels() != TemplateArgs.getNumSubstitutedLevels()) { + // We're performing a partial substitution, so the substituted argument + // could be dependent. As a result we can't create a SubstNonType*Expr + // node now, since that represents a fully-substituted argument. + // FIXME: We should have some AST representation for this. + if (Arg.getKind() == TemplateArgument::Pack) { + // FIXME: This won't work for alias templates. + assert(Arg.pack_size() == 1 && Arg.pack_begin()->isPackExpansion() && + "unexpected pack arguments in partial substitution"); + Arg = Arg.pack_begin()->getPackExpansionPattern(); + } + assert(Arg.getKind() == TemplateArgument::Expression && + "unexpected nontype template argument kind in partial substitution"); + return Arg.getAsExpr(); + } + if (NTTP->isParameterPack()) { assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack"); @@ -1429,12 +1446,9 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, NewTTPDecl = cast_or_null( TransformDecl(TL.getNameLoc(), OldTTPDecl)); - QualType Result - = getSema().Context.getTemplateTypeParmType(T->getDepth() - - TemplateArgs.getNumLevels(), - T->getIndex(), - T->isParameterPack(), - NewTTPDecl); + QualType Result = getSema().Context.getTemplateTypeParmType( + T->getDepth() - TemplateArgs.getNumSubstitutedLevels(), T->getIndex(), + T->isParameterPack(), NewTTPDecl); TemplateTypeParmTypeLoc NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); return Result; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 63c10c0c71..b285d4f381 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2074,13 +2074,10 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl( // TODO: don't always clone when decls are refcounted. assert(D->getTypeForDecl()->isTemplateTypeParmType()); - TemplateTypeParmDecl *Inst = - TemplateTypeParmDecl::Create(SemaRef.Context, Owner, - D->getLocStart(), D->getLocation(), - D->getDepth() - TemplateArgs.getNumLevels(), - D->getIndex(), D->getIdentifier(), - D->wasDeclaredWithTypename(), - D->isParameterPack()); + TemplateTypeParmDecl *Inst = TemplateTypeParmDecl::Create( + SemaRef.Context, Owner, D->getLocStart(), D->getLocation(), + D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getIndex(), + D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack()); Inst->setAccess(AS_public); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { @@ -2218,17 +2215,14 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( if (IsExpandedParameterPack) Param = NonTypeTemplateParmDecl::Create( SemaRef.Context, Owner, D->getInnerLocStart(), D->getLocation(), - D->getDepth() - TemplateArgs.getNumLevels(), D->getPosition(), - D->getIdentifier(), T, DI, ExpandedParameterPackTypes, + D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), + D->getPosition(), D->getIdentifier(), T, DI, ExpandedParameterPackTypes, ExpandedParameterPackTypesAsWritten); else - Param = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner, - D->getInnerLocStart(), - D->getLocation(), - D->getDepth() - TemplateArgs.getNumLevels(), - D->getPosition(), - D->getIdentifier(), T, - D->isParameterPack(), DI); + Param = NonTypeTemplateParmDecl::Create( + SemaRef.Context, Owner, D->getInnerLocStart(), D->getLocation(), + D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), + D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), DI); Param->setAccess(AS_public); if (Invalid) @@ -2349,19 +2343,15 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( // Build the template template parameter. TemplateTemplateParmDecl *Param; if (IsExpandedParameterPack) - Param = TemplateTemplateParmDecl::Create(SemaRef.Context, Owner, - D->getLocation(), - D->getDepth() - TemplateArgs.getNumLevels(), - D->getPosition(), - D->getIdentifier(), InstParams, - ExpandedParams); + Param = TemplateTemplateParmDecl::Create( + SemaRef.Context, Owner, D->getLocation(), + D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), + D->getPosition(), D->getIdentifier(), InstParams, ExpandedParams); else - Param = TemplateTemplateParmDecl::Create(SemaRef.Context, Owner, - D->getLocation(), - D->getDepth() - TemplateArgs.getNumLevels(), - D->getPosition(), - D->isParameterPack(), - D->getIdentifier(), InstParams); + Param = TemplateTemplateParmDecl::Create( + SemaRef.Context, Owner, D->getLocation(), + D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), + D->getPosition(), D->isParameterPack(), D->getIdentifier(), InstParams); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { NestedNameSpecifierLoc QualifierLoc = D->getDefaultArgument().getTemplateQualifierLoc(); diff --git a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp index 1205fd1cf3..4afbd2d7c1 100644 --- a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -176,3 +176,29 @@ namespace default_args_from_ctor { template struct T { template T(A = 0, B = 0) {} }; T t(0, 0); } + +namespace transform_params { + template typename U, T (*X)[N]> + struct A { // expected-note 2{{candidate}} + template typename W> + A(U, W); // expected-note {{[with V = int, M = 12, Y = &transform_params::n]}} + + static constexpr T v = N; + }; + + int n[12]; + template struct Q {}; + Q<&n> qn; + // FIXME: The class template argument deduction result here is correct, but + // we incorrectly fail to deduce arguments for the constructor! + A a(qn, qn); // expected-error {{no matching constructor for initialization of 'transform_params::A'}} + static_assert(a.v == 12); + + // FIXME: This causes a crash right now (not class template deduction related). +#if 0 + template struct B { + template B(T (&...p)[V]); + }; + B b({1, 2, 3}, {"foo", "bar"}, {'x', 'y', 'z', 'w'}); +#endif +} diff --git a/test/SemaTemplate/deduction.cpp b/test/SemaTemplate/deduction.cpp index 16e01a9346..a1180f0988 100644 --- a/test/SemaTemplate/deduction.cpp +++ b/test/SemaTemplate/deduction.cpp @@ -481,3 +481,16 @@ namespace check_extended_pack { int n; void h() { g<0>(Y<0, &n>()); } // expected-error {{no matching function}} } + +namespace dependent_template_template_param_non_type_param_type { + template struct A { // expected-note 2{{candidate}} + template class W> + A(W); // expected-note {{[with V = int, M = 12, Y = &dependent_template_template_param_non_type_param_type::n]}} + }; + + int n[12]; + template struct Q {}; + Q<&n> qn; + // FIXME: This should be accepted, but we somehow fail to deduce W. + A<0> a(qn); // expected-error {{no matching constructor for initialization}} +}