PR c++/89744 - ICE with specialization of member class template.
authorJason Merrill <jason@redhat.com>
Sat, 30 Mar 2019 15:23:37 +0000 (11:23 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Sat, 30 Mar 2019 15:23:37 +0000 (11:23 -0400)
My fix five years ago for PR 60241 was incomplete: when we reassign implicit
instances of a partial instantiation of a member template to the explicit
specialization of that partial instantiation, we also need to adjust the
CLASSTYPE_TI_ARGS to match what we'd get when looking up that instance after
the explicit specialization.  We also need to do this when we later look up
the instance in a way that only finds the explicit specialization halfway
through lookup_template_class_1.

* pt.c (lookup_template_class_1): If the partial instantiation is
explicitly specialized, adjust.
(maybe_process_partial_specialization): Also adjust
CLASSTYPE_TI_ARGS.

From-SVN: r270036

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/pt.c
gcc/testsuite/g++.dg/template/mem-spec1.C [new file with mode: 0644]

index a31e439..8a5e920 100644 (file)
@@ -1,3 +1,11 @@
+2019-03-30  Jason Merrill  <jason@redhat.com>
+
+       PR c++/89744 - ICE with specialization of member class template.
+       * pt.c (lookup_template_class_1): If the partial instantiation is
+       explicitly specialized, adjust.
+       (maybe_process_partial_specialization): Also adjust
+       CLASSTYPE_TI_ARGS.
+
 2019-03-29  Jakub Jelinek  <jakub@redhat.com>
 
        PR sanitizer/89869
index fd612b0..5915275 100644 (file)
@@ -3534,7 +3534,15 @@ struct GTY(()) lang_decl {
       template <typename T> struct S {};
       template <typename T> struct S<T*> {};
       
-   the CLASSTPYE_TI_TEMPLATE for S<int*> will be S, not the S<T*>.  */
+   the CLASSTPYE_TI_TEMPLATE for S<int*> will be S, not the S<T*>.
+
+   For a member class template, CLASSTYPE_TI_TEMPLATE always refers to the
+   partial instantiation rather than the primary template.  CLASSTYPE_TI_ARGS
+   are for the primary template if the partial instantiation isn't
+   specialized, or for the explicit specialization if it is, e.g.
+
+      template <class T> class C { template <class U> class D; }
+      template <> template <class U> class C<int>::D;  */
 #define CLASSTYPE_TI_TEMPLATE(NODE) TI_TEMPLATE (CLASSTYPE_TEMPLATE_INFO (NODE))
 #define CLASSTYPE_TI_ARGS(NODE)     TI_ARGS (CLASSTYPE_TEMPLATE_INFO (NODE))
 
index 91c3415..f3faa89 100644 (file)
@@ -1090,7 +1090,8 @@ maybe_process_partial_specialization (tree type)
                  type_specializations->remove_elt (&elt);
 
                  elt.tmpl = tmpl;
-                 elt.args = INNERMOST_TEMPLATE_ARGS (elt.args);
+                 CLASSTYPE_TI_ARGS (inst)
+                   = elt.args = INNERMOST_TEMPLATE_ARGS (elt.args);
 
                  spec_entry **slot
                    = type_specializations->find_slot (&elt, INSERT);
@@ -9662,6 +9663,16 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
                   : (TREE_CODE (found) == TYPE_DECL
                      ? DECL_TI_TEMPLATE (found)
                      : CLASSTYPE_TI_TEMPLATE (found)));
+
+         if (DECL_CLASS_TEMPLATE_P (found)
+             && CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (found)))
+           {
+             /* If this partial instantiation is specialized, we want to
+                use it for hash table lookup.  */
+             elt.tmpl = found;
+             elt.args = arglist = INNERMOST_TEMPLATE_ARGS (arglist);
+             hash = spec_hasher::hash (&elt);
+           }
        }
 
       // Build template info for the new specialization.
@@ -9669,6 +9680,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
 
       elt.spec = t;
       slot = type_specializations->find_slot_with_hash (&elt, hash, INSERT);
+      gcc_checking_assert (*slot == NULL);
       entry = ggc_alloc<spec_entry> ();
       *entry = elt;
       *slot = entry;
diff --git a/gcc/testsuite/g++.dg/template/mem-spec1.C b/gcc/testsuite/g++.dg/template/mem-spec1.C
new file mode 100644 (file)
index 0000000..b06df0a
--- /dev/null
@@ -0,0 +1,68 @@
+// PR c++/89744
+
+namespace N1 {
+  template<typename> struct A
+  {
+    template<typename> struct B {};
+    A() { B<int> b; }
+  };
+
+  template<> template<typename>
+  struct A<int>::B
+  {
+    virtual void foo() {}
+  };
+
+  A<int> a;
+}
+
+namespace N2 {
+  template<typename> struct A
+  {
+    template<typename> struct B {};
+    A() { B<int> b; }
+  };
+
+  template<> template<typename>
+  struct A<int>::B
+  {
+    virtual void foo() {}
+    void bar() {}
+  };
+
+  A<int> a;
+}
+
+namespace N3 {
+  template<typename> struct A
+  {
+    template<typename> struct B {};
+    A() { B<int> b; }
+  };
+
+  template<> template<typename>
+  struct A<int>::B
+  {
+    ~B() {}
+  };
+
+  A<int> a;
+}
+
+#if __cpp_variadic_templates
+namespace N4 {
+  template<typename...> struct A
+  {
+    template<typename> struct B {};
+    typedef B<int> X;
+  };
+
+  template<> template<typename>
+  struct A<int>::B
+  {
+    typedef int Y;
+  };
+
+  A<int>::B<int> b;
+}
+#endif