Ensure that we substitute into the declaration of a template parameter pack
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 3 Feb 2016 20:40:30 +0000 (20:40 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 3 Feb 2016 20:40:30 +0000 (20:40 +0000)
(that is not a pack expansion) during template argument deduction, even if we
deduced that the pack would be empty.

llvm-svn: 259688

clang/lib/Sema/SemaTemplateDeduction.cpp
clang/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp [new file with mode: 0644]

index 2dc4fdb..0b090c8 100644 (file)
@@ -2119,8 +2119,25 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param,
       PackedArgsBuilder.push_back(Output.pop_back_val());
     }
 
-    // FIXME: If the pack is empty and this is a template template parameter,
-    // we still need to substitute into the parameter itself.
+    // If the pack is empty, we still need to substitute into the parameter
+    // itself, in case that substitution fails. For non-type parameters, we did
+    // this above. For type parameters, no substitution is ever required.
+    auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param);
+    if (TTP && PackedArgsBuilder.empty()) {
+      // Set up a template instantiation context.
+      LocalInstantiationScope Scope(S);
+      Sema::InstantiatingTemplate Inst(S, Template->getLocation(), Template,
+                                       TTP, Output,
+                                       Template->getSourceRange());
+      if (Inst.isInvalid())
+        return true;
+
+      TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack,
+                                        Output.data(), Output.size());
+      if (!S.SubstDecl(TTP, S.CurContext,
+                       MultiLevelTemplateArgumentList(TemplateArgs)))
+        return true;
+    }
 
     // Create the resulting argument pack.
     Output.push_back(
@@ -2808,11 +2825,22 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate,
         Builder.push_back(TemplateArgument(
             llvm::makeArrayRef(ExplicitArgs, NumExplicitArgs)));
 
-        // Forget the partially-substituted pack; it's substitution is now
+        // Forget the partially-substituted pack; its substitution is now
         // complete.
         CurrentInstantiationScope->ResetPartiallySubstitutedPack();
       } else {
-        Builder.push_back(TemplateArgument::getEmptyPack());
+        // Go through the motions of checking the empty argument pack against
+        // the parameter pack.
+        DeducedTemplateArgument DeducedPack(TemplateArgument::getEmptyPack());
+        if (ConvertDeducedTemplateArgument(*this, Param, DeducedPack,
+                                           FunctionTemplate, Info, true,
+                                           Builder)) {
+          Info.Param = makeTemplateParameter(Param);
+          // FIXME: These template arguments are temporary. Free them!
+          Info.reset(TemplateArgumentList::CreateCopy(Context, Builder.data(),
+                                                      Builder.size()));
+          return TDK_SubstitutionFailure;
+        }
       }
       continue;
     }
diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
new file mode 100644 (file)
index 0000000..bc074ba
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -std=c++11 -verify %s
+
+struct Q { typedef int type; };
+
+// "The substitution occurs in all types and expressions that are used in [...]
+// template parameter declarations." In particular, we must substitute into the
+// type of a parameter pack that is not a pack expansion, even if we know the
+// corresponding argument pack is empty.
+template<typename T, typename T::type...> void a(T);
+int &a(...);
+int &a_disabled = a(0);
+int &a_enabled = a(Q()); // expected-error {{cannot bind to a temporary of type 'void'}}
+
+template<typename T, template<typename T::type> class ...X> void b(T);
+int &b(...);
+int &b_disabled = b(0);
+int &b_enabled = b(Q()); // expected-error {{cannot bind to a temporary of type 'void'}}
+
+template<typename T, template<typename T::type...> class ...X> void c(T);
+int &c(...);
+int &c_disabled = c(0);
+int &c_enabled = c(Q()); // expected-error {{cannot bind to a temporary of type 'void'}}