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.)

llvm-svn: 295696
This commit is contained in:
Richard Smith 2017-02-21 06:30:38 +00:00
parent 16d9730b86
commit b4f9625a7b
7 changed files with 113 additions and 41 deletions

View File

@ -1479,6 +1479,7 @@ public:
unsigned NumExpansions);
using TemplateParmPosition::getDepth;
using TemplateParmPosition::setDepth;
using TemplateParmPosition::getPosition;
using TemplateParmPosition::setPosition;
using TemplateParmPosition::getIndex;

View File

@ -47,6 +47,10 @@ namespace clang {
/// argument list (first) to the outermost template argument list (last).
SmallVector<ArgList, 4> 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.
MultiLevelTemplateArgumentList() { }
@ -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<TemplateArgument&>(
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();

View File

@ -1442,6 +1442,8 @@ struct ConvertConstructorToDeductionGuideTransform {
CXXConstructorDecl *CD) {
SmallVector<TemplateArgument, 16> 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;
}

View File

@ -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<TemplateTypeParmDecl>(
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<TemplateTypeParmTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
return Result;

View File

@ -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();

View File

@ -176,3 +176,29 @@ namespace default_args_from_ctor {
template <class A> struct T { template<typename B> T(A = 0, B = 0) {} };
T t(0, 0);
}
namespace transform_params {
template<typename T, T N, template<T (*v)[N]> typename U, T (*X)[N]>
struct A { // expected-note 2{{candidate}}
template<typename V, V M, V (*Y)[M], template<V (*v)[M]> typename W>
A(U<X>, W<Y>); // expected-note {{[with V = int, M = 12, Y = &transform_params::n]}}
static constexpr T v = N;
};
int n[12];
template<int (*)[12]> 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<int, 12, Q, &transform_params::n>'}}
static_assert(a.v == 12);
// FIXME: This causes a crash right now (not class template deduction related).
#if 0
template<typename ...T> struct B {
template<T ...V> B(T (&...p)[V]);
};
B b({1, 2, 3}, {"foo", "bar"}, {'x', 'y', 'z', 'w'});
#endif
}

View File

@ -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<int N> struct A { // expected-note 2{{candidate}}
template<typename V = int, V M = 12, V (*Y)[M], template<V (*v)[M]> class W>
A(W<Y>); // expected-note {{[with V = int, M = 12, Y = &dependent_template_template_param_non_type_param_type::n]}}
};
int n[12];
template<int (*)[12]> 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}}
}