Distinguish between template parameter substitutions that are forming

specializations and those that are done as part of rewrites.

Do not create Subst* nodes in the latter. We previously had a hybrid of
these two behaviors where we would only create some Subst* nodes but not
others during deduction guide rewrites.

No functional change intended, but the resulting ASTs are more
principled.
This commit is contained in:
Richard Smith 2020-06-22 19:30:36 -07:00
parent 19df9e2959
commit 9f9373f86d
4 changed files with 106 additions and 10 deletions

View File

@ -42,6 +42,17 @@ class TypedefNameDecl;
class TypeSourceInfo;
class VarDecl;
/// The kind of template substitution being performed.
enum class TemplateSubstitutionKind : char {
/// We are substituting template parameters for template arguments in order
/// to form a template specialization.
Specialization,
/// We are substituting template parameters for (typically) other template
/// parameters in order to rewrite a declaration as a different declaration
/// (for example, when forming a deduction guide from a constructor).
Rewrite,
};
/// Data structure that captures multiple levels of template argument
/// lists for use in template instantiation.
///
@ -73,6 +84,9 @@ class VarDecl;
/// being substituted.
unsigned NumRetainedOuterLevels = 0;
/// The kind of substitution described by this argument list.
TemplateSubstitutionKind Kind = TemplateSubstitutionKind::Specialization;
public:
/// Construct an empty set of template argument lists.
MultiLevelTemplateArgumentList() = default;
@ -83,6 +97,18 @@ class VarDecl;
addOuterTemplateArguments(&TemplateArgs);
}
void setKind(TemplateSubstitutionKind K) { Kind = K; }
/// Determine the kind of template substitution being performed.
TemplateSubstitutionKind getKind() const { return Kind; }
/// Determine whether we are rewriting template parameters rather than
/// substituting for them. If so, we should not leave references to the
/// original template parameters behind.
bool isRewrite() const {
return Kind == TemplateSubstitutionKind::Rewrite;
}
/// Determine the number of levels in this template argument
/// list.
unsigned getNumLevels() const {

View File

@ -2038,6 +2038,7 @@ struct ConvertConstructorToDeductionGuideTransform {
// a list of substituted template arguments as we go.
for (NamedDecl *Param : *InnerParams) {
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
Args.addOuterTemplateArguments(SubstArgs);
Args.addOuterRetainedLevel();
NamedDecl *NewParam = transformTemplateParameter(Param, Args);
@ -2057,6 +2058,7 @@ struct ConvertConstructorToDeductionGuideTransform {
// substitute references to the old parameters into references to the
// new ones.
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
if (FTD) {
Args.addOuterTemplateArguments(SubstArgs);
Args.addOuterRetainedLevel();

View File

@ -1362,6 +1362,19 @@ TemplateName TemplateInstantiator::TransformTemplateName(
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
if (TemplateArgs.isRewrite()) {
// We're rewriting the template parameter as a reference to another
// template parameter.
if (Arg.getKind() == TemplateArgument::Pack) {
assert(Arg.pack_size() == 1 && Arg.pack_begin()->isPackExpansion() &&
"unexpected pack arguments in template rewrite");
Arg = Arg.pack_begin()->getPackExpansionPattern();
}
assert(Arg.getKind() == TemplateArgument::Template &&
"unexpected nontype template argument kind in template rewrite");
return Arg.getAsTemplate();
}
if (TTP->isParameterPack()) {
assert(Arg.getKind() == TemplateArgument::Pack &&
"Missing argument pack");
@ -1458,19 +1471,18 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *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 (TemplateArgs.isRewrite()) {
// We're rewriting the template parameter as a reference to another
// template parameter.
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");
"unexpected pack arguments in template rewrite");
Arg = Arg.pack_begin()->getPackExpansionPattern();
}
assert(Arg.getKind() == TemplateArgument::Expression &&
"unexpected nontype template argument kind in partial substitution");
"unexpected nontype template argument kind in template rewrite");
// FIXME: This can lead to the same subexpression appearing multiple times
// in a complete expression.
return Arg.getAsExpr();
}
@ -1782,6 +1794,24 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
TemplateArgument Arg = TemplateArgs(T->getDepth(), T->getIndex());
if (TemplateArgs.isRewrite()) {
// We're rewriting the template parameter as a reference to another
// template parameter.
if (Arg.getKind() == TemplateArgument::Pack) {
assert(Arg.pack_size() == 1 && Arg.pack_begin()->isPackExpansion() &&
"unexpected pack arguments in template rewrite");
Arg = Arg.pack_begin()->getPackExpansionPattern();
}
assert(Arg.getKind() == TemplateArgument::Type &&
"unexpected nontype template argument kind in template rewrite");
QualType NewT = Arg.getAsType();
assert(isa<TemplateTypeParmType>(NewT) &&
"type parm not rewritten to type parm");
auto NewTL = TLB.push<TemplateTypeParmTypeLoc>(NewT);
NewTL.setNameLoc(TL.getNameLoc());
return NewT;
}
if (T->isParameterPack()) {
assert(Arg.getKind() == TemplateArgument::Pack &&
"Missing argument pack");

View File

@ -66,7 +66,7 @@ using BT = B<char, 'x'>;
// CHECK: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 T
// CHECK: |-NonTypeTemplateParmDecl {{.*}} 'T' depth 0 index 1 V
// CHECK: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 2 U
// CHECK: |-NonTypeTemplateParmDecl {{.*}} 'type-parameter-0-2':'type-parameter-0-2' depth 0 index 3 W
// CHECK: |-NonTypeTemplateParmDecl {{.*}} 'type-parameter-0-2' depth 0 index 3 W
// CHECK: |-CXXDeductionGuideDecl {{.*}} 'auto (X<W, V>) -> B<T, V>'
// CHECK: | `-ParmVarDecl {{.*}} 'X<W, V>'
// CHECK: `-CXXDeductionGuideDecl {{.*}} 'auto (X<nullptr, 'x'>) -> B<char, 'x'>'
@ -79,6 +79,44 @@ using BT = B<char, 'x'>;
// CHECK: |-InjectedClassNameType {{.*}} 'B<T, V>' dependent
// CHECK: `-TemplateSpecializationType {{.*}} 'X<W, V>' dependent X
// CHECK: |-TemplateArgument expr
// CHECK: | `-DeclRefExpr {{.*}} 'type-parameter-0-2':'type-parameter-0-2' NonTypeTemplateParm {{.*}} 'W' 'type-parameter-0-2':'type-parameter-0-2'
// CHECK: | `-DeclRefExpr {{.*}} 'type-parameter-0-2' NonTypeTemplateParm {{.*}} 'W' 'type-parameter-0-2'
// CHECK: `-TemplateArgument expr
// CHECK: `-DeclRefExpr {{.*}} 'T' NonTypeTemplateParm {{.*}} 'V' 'T'
template<template<typename X, X> typename> struct Y {};
template<typename A> struct C {
template<template<typename X, X> typename T, typename U, U V = 0> C(A, Y<T>, U);
};
C c(1, Y<B>{}, 2);
using CT = decltype(c);
using CT = C<int>;
// CHECK: Dumping <deduction guide for C>:
// CHECK: FunctionTemplateDecl
// CHECK: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 A
// CHECK: |-TemplateTemplateParmDecl {{.*}} depth 0 index 1 T
// CHECK: | |-TemplateTypeParmDecl {{.*}} typename depth 1 index 0 X
// CHECK: | `-NonTypeTemplateParmDecl {{.*}} 'X' depth 1 index 1
// CHECK: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 2 U
// CHECK: |-NonTypeTemplateParmDecl {{.*}} 'type-parameter-0-2' depth 0 index 3 V
// CHECK: | `-TemplateArgument expr
// CHECK: | `-IntegerLiteral {{.*}} 'int' 0
// CHECK: |-CXXDeductionGuideDecl {{.*}} 'auto (A, Y<>, type-parameter-0-2) -> C<A>'
// CHECK: | |-ParmVarDecl {{.*}} 'A'
// CHECK: | |-ParmVarDecl {{.*}} 'Y<>'
// CHECK: | `-ParmVarDecl {{.*}} 'type-parameter-0-2'
// CHECK: `-CXXDeductionGuideDecl {{.*}} 'auto (int, Y<B>, int) -> C<int>'
// CHECK: |-TemplateArgument type 'int'
// CHECK: |-TemplateArgument template B
// CHECK: |-TemplateArgument type 'int'
// CHECK: |-TemplateArgument integral 0
// CHECK: |-ParmVarDecl {{.*}} 'int':'int'
// CHECK: |-ParmVarDecl {{.*}} 'Y<B>':'Y<B>'
// CHECK: `-ParmVarDecl {{.*}} 'int':'int'
// CHECK: FunctionProtoType {{.*}} 'auto (A, Y<>, type-parameter-0-2) -> C<A>' dependent trailing_return cdecl
// CHECK: |-InjectedClassNameType {{.*}} 'C<A>' dependent
// CHECK: |-TemplateTypeParmType {{.*}} 'A' dependent depth 0 index 0
// CHECK: | `-TemplateTypeParm {{.*}} 'A'
// CHECK: |-TemplateSpecializationType {{.*}} 'Y<>' dependent Y
// CHECK: | `-TemplateArgument template
// CHECK: `-TemplateTypeParmType {{.*}} 'type-parameter-0-2' dependent depth 0 index 2