Distinguish between template parameter substitutions that are forming
authorRichard Smith <richard@metafoo.co.uk>
Tue, 23 Jun 2020 02:30:36 +0000 (19:30 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Tue, 23 Jun 2020 02:34:52 +0000 (19:34 -0700)
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.

clang/include/clang/Sema/Template.h
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/test/SemaTemplate/deduction-guide.cpp

index 8166b6c..741de48 100644 (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 {
index 073b4e8..00003e8 100644 (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();
index bfda59d..8197e7d 100644 (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");
index 3ac37ac..2a7d438 100644 (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