diff mbox

C++ PATCH for c++/60095, partial specialization of variable template

Message ID 8bfb03cf-b681-ba19-6170-ee3ed27b864b@redhat.com
State New
Headers show

Commit Message

Jason Merrill May 31, 2016, 7:12 p.m. UTC
In the implementation of partially specialized variable templates, we 
decided to put the partial specialization template and args in 
DECL_TEMPLATE_INFO rather than the general template and args as we do 
for classes.  This decision came out of the fact that for variables, we 
need to decide what partial specialization we're using in order to get 
the type of the variable, not just for instantiating its value.  But 
that has caused a number of problems with code that expects DECL_TI_ARGS 
to be the general args; I previously fixed 69009 in instantiate_decl by 
adding special handling there.  But the same issue affects many other 
places around the compiler, notably in name mangling; it seems safer to 
go back to the earlier pattern of using the general template/args and 
looking up the partial specialization again at instantiation time.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox

Patch

commit 51c534b3d0d0c53a1f3b788eb85ff4e70f9ac52a
Author: Jason Merrill <jason@redhat.com>
Date:   Mon May 30 15:38:29 2016 -0400

    	PR c++/60095 - partial specialization of variable templates
    
    	PR c++/69515
    	PR c++/69009
    	* pt.c (instantiate_template_1): Don't put the partial
    	specialization in DECL_TI_TEMPLATE.
    	(partial_specialization_p, impartial_args): Remove.
    	(regenerate_decl_from_template): Add args parm.
    	(instantiate_decl): Look up the partial specialization again.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 5008b68..70ca207 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -182,7 +182,6 @@  static tree copy_template_args (tree);
 static tree tsubst_template_arg (tree, tree, tsubst_flags_t, tree);
 static tree tsubst_template_args (tree, tree, tsubst_flags_t, tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
-static void regenerate_decl_from_template (tree, tree);
 static tree most_specialized_partial_spec (tree, tsubst_flags_t);
 static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
@@ -17398,6 +17397,7 @@  instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 
   tree pattern = DECL_TEMPLATE_RESULT (gen_tmpl);
 
+  fndecl = NULL_TREE;
   if (VAR_P (pattern))
     {
       /* We need to determine if we're using a partial or explicit
@@ -17409,14 +17409,16 @@  instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 	pattern = error_mark_node;
       else if (elt)
 	{
-	  tmpl = TREE_VALUE (elt);
-	  pattern = DECL_TEMPLATE_RESULT (tmpl);
-	  targ_ptr = TREE_PURPOSE (elt);
+	  tree partial_tmpl = TREE_VALUE (elt);
+	  tree partial_args = TREE_PURPOSE (elt);
+	  tree partial_pat = DECL_TEMPLATE_RESULT (partial_tmpl);
+	  fndecl = tsubst (partial_pat, partial_args, complain, gen_tmpl);
 	}
     }
 
   /* Substitute template parameters to obtain the specialization.  */
-  fndecl = tsubst (pattern, targ_ptr, complain, gen_tmpl);
+  if (fndecl == NULL_TREE)
+    fndecl = tsubst (pattern, targ_ptr, complain, gen_tmpl);
   if (DECL_CLASS_SCOPE_P (gen_tmpl))
     pop_nested_class ();
   pop_from_top_level ();
@@ -20888,36 +20890,6 @@  most_general_template (tree decl)
   return decl;
 }
 
-/* True iff the TEMPLATE_DECL tmpl is a partial specialization.  */
-
-static bool
-partial_specialization_p (tree tmpl)
-{
-  /* Any specialization has DECL_TEMPLATE_SPECIALIZATION.  */
-  if (!DECL_TEMPLATE_SPECIALIZATION (tmpl))
-    return false;
-  tree t = DECL_TI_TEMPLATE (tmpl);
-  /* A specialization that fully specializes one of the containing classes is
-     not a partial specialization.  */
-  return (list_length (DECL_TEMPLATE_PARMS (tmpl))
-	  == list_length (DECL_TEMPLATE_PARMS (t)));
-}
-
-/* If TMPL is a partial specialization, return the arguments for its primary
-   template.  */
-
-static tree
-impartial_args (tree tmpl, tree args)
-{
-  if (!partial_specialization_p (tmpl))
-    return args;
-
-  /* If TMPL is a partial specialization, we need to substitute to get
-     the args for the primary template.  */
-  return tsubst_template_args (DECL_TI_ARGS (tmpl), args,
-			       tf_warning_or_error, tmpl);
-}
-
 /* Return the most specialized of the template partial specializations
    which can produce TARGET, a specialization of some class or variable
    template.  The value returned is actually a TREE_LIST; the TREE_VALUE is
@@ -21419,14 +21391,12 @@  do_type_instantiation (tree t, tree storage, tsubst_flags_t complain)
    to instantiate the DECL, we regenerate it.  */
 
 static void
-regenerate_decl_from_template (tree decl, tree tmpl)
+regenerate_decl_from_template (tree decl, tree tmpl, tree args)
 {
   /* The arguments used to instantiate DECL, from the most general
      template.  */
-  tree args;
   tree code_pattern;
 
-  args = DECL_TI_ARGS (decl);
   code_pattern = DECL_TEMPLATE_RESULT (tmpl);
 
   /* Make sure that we can see identifiers, and compute access
@@ -21742,7 +21712,7 @@  instantiate_decl (tree d, int defer_ok,
     return d;
 
   gen_tmpl = most_general_template (tmpl);
-  gen_args = impartial_args (tmpl, DECL_TI_ARGS (d));
+  gen_args = DECL_TI_ARGS (d);
 
   if (tmpl != gen_tmpl)
     /* We should already have the extra args.  */
@@ -21761,6 +21731,20 @@  instantiate_decl (tree d, int defer_ok,
   /* Set TD to the template whose DECL_TEMPLATE_RESULT is the pattern
      for the instantiation.  */
   td = template_for_substitution (d);
+  args = gen_args;
+
+  if (VAR_P (d))
+    {
+      /* Look up an explicit specialization, if any.  */
+      tree tid = lookup_template_variable (gen_tmpl, gen_args);
+      tree elt = most_specialized_partial_spec (tid, tf_warning_or_error);
+      if (elt && elt != error_mark_node)
+	{
+	  td = TREE_VALUE (elt);
+	  args = TREE_PURPOSE (elt);
+	}
+    }
+
   code_pattern = DECL_TEMPLATE_RESULT (td);
 
   /* We should never be trying to instantiate a member of a class
@@ -21773,9 +21757,7 @@  instantiate_decl (tree d, int defer_ok,
        outside the class, we may have too many arguments.  Drop the
        ones we don't need.  The same is true for specializations.  */
     args = get_innermost_template_args
-      (gen_args, TMPL_PARMS_DEPTH  (DECL_TEMPLATE_PARMS (td)));
-  else
-    args = gen_args;
+      (args, TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (td)));
 
   if (TREE_CODE (d) == FUNCTION_DECL)
     {
@@ -21941,7 +21923,7 @@  instantiate_decl (tree d, int defer_ok,
 
   /* Regenerate the declaration in case the template has been modified
      by a subsequent redeclaration.  */
-  regenerate_decl_from_template (d, td);
+  regenerate_decl_from_template (d, td, args);
 
   /* We already set the file and line above.  Reset them now in case
      they changed as a result of calling regenerate_decl_from_template.  */
diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ39.C b/gcc/testsuite/g++.dg/cpp1y/var-templ39.C
index e06519d..5170a5b 100644
--- a/gcc/testsuite/g++.dg/cpp1y/var-templ39.C
+++ b/gcc/testsuite/g++.dg/cpp1y/var-templ39.C
@@ -1,5 +1,5 @@ 
 // PR c++/66260
-// { dg-do compile { target c++14 } }
+// { dg-do assemble { target c++14 } }
 
 template <class>
 constexpr bool foo = false;
diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ39a.C b/gcc/testsuite/g++.dg/cpp1y/var-templ39a.C
new file mode 100644
index 0000000..5ba1b9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/var-templ39a.C
@@ -0,0 +1,27 @@ 
+// PR c++/66260
+// { dg-do compile { target c++14 } }
+
+template <class>
+bool foo = false;
+template <>
+bool foo<int> = true;
+template <class T, int N>
+bool foo<T[N]> = foo<T>;
+
+#define assert(X) if (!(X)) __builtin_abort();
+
+int main()
+{
+  // { dg-final { scan-assembler "_Z3fooIiE" } }
+  assert(foo<int>);
+  // { dg-final { scan-assembler "_Z3fooIdE" } }
+  assert(!foo<double>);
+  // { dg-final { scan-assembler "_Z3fooIA3_iE" } }
+  assert(foo<int[3]>);
+  // { dg-final { scan-assembler "_Z3fooIA3_dE" } }
+  assert(!foo<double[3]>);
+  // { dg-final { scan-assembler "_Z3fooIA2_A5_A3_iE" } }
+  assert(foo<int[2][5][3]>);
+  // { dg-final { scan-assembler "_Z3fooIA2_A5_A3_dE" } }
+  assert(!foo<double[2][5][3]>);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ51.C b/gcc/testsuite/g++.dg/cpp1y/var-templ51.C
new file mode 100644
index 0000000..f85ef9c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/var-templ51.C
@@ -0,0 +1,11 @@ 
+// PR c++/60095
+// { dg-do link { target c++14 } }
+
+template <class>
+constexpr bool b = false;
+template<typename T>
+constexpr bool b<T*> = true;
+int main() {
+    b<int*>;
+    b<double*>;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ52.C b/gcc/testsuite/g++.dg/cpp1y/var-templ52.C
new file mode 100644
index 0000000..61fd19e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/var-templ52.C
@@ -0,0 +1,14 @@ 
+// PR c++/69515
+// { dg-do link { target c++14 } }
+
+struct A { A(int = 0) {} };
+
+template<class...> class meow;
+
+template<typename T> A foo;
+template<typename... Ts> A foo<meow<Ts...>> = 1;
+
+auto&& a = foo<meow<int>>;
+auto&& b = foo<meow<int, int>>;
+
+int main() {}