diff mbox series

c++: Incorrect specialization hash table [PR 99285]

Message ID e8a5f564-83a7-31ef-9507-6d08072ba7db@acm.org
State New
Headers show
Series c++: Incorrect specialization hash table [PR 99285] | expand

Commit Message

Nathan Sidwell March 8, 2021, 6:08 p.m. UTC
Class template partial specializations need to be in the
specialization hash, but not all of them.  This	defers adding
streamed-in entities to the hash table, in the same way I deferred
adding the instantiation and specialization lists for 99170.

         PR c++/99285
         gcc/cp/
         * cp-tree.h (match_mergeable_specialization)
         (add_mergeable_specialization): Adjust parms.
         * module.cc (trees_in::decl_value): Adjust
         add_mergeable_specialization calls.
         (trees_out::key_mergeable): Adjust match_mergeable_specialization
         calls.
         (specialization_add): Likewise.
         * pt.c (match_mergeable_specialization): Do not insert.
         (add_mergeable_specialization): Add to hash table here.
         gcc/testsuite/
         * g++.dg/modules/pr99285_a.H: New.
         * g++.dg/modules/pr99285_b.H: New.
diff mbox series

Patch

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index 39e2ad83abd..81ff375f8a5 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -7239,11 +7239,10 @@  extern void walk_specializations		(bool,
 						 void (*)(bool, spec_entry *,
 							  void *),
 						 void *);
-extern tree match_mergeable_specialization	(bool is_decl, spec_entry *,
-						 bool insert = true);
+extern tree match_mergeable_specialization	(bool is_decl, spec_entry *);
 extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec);
-extern void add_mergeable_specialization        (tree tmpl, tree args,
-						 tree spec, unsigned);
+extern void add_mergeable_specialization        (bool is_decl, spec_entry *,
+						 tree outer, unsigned);
 extern tree add_outermost_template_args		(tree, tree);
 extern tree add_extra_args			(tree, tree);
 extern tree build_extra_args			(tree, tree, tsubst_flags_t);
diff --git c/gcc/cp/module.cc w/gcc/cp/module.cc
index 48862dd9bbc..2518d73c220 100644
--- c/gcc/cp/module.cc
+++ w/gcc/cp/module.cc
@@ -8059,9 +8059,14 @@  trees_in::decl_value ()
 	set_constraints (decl, spec.spec);
       if (mk & MK_template_mask
 	  || mk == MK_partial)
-	/* Add to specialization tables now that constraints etc are
-	   added.  */
-	add_mergeable_specialization (spec.tmpl, spec.args, decl, spec_flags);
+	{
+	  /* Add to specialization tables now that constraints etc are
+	     added.  */
+	  bool is_decl = (mk & MK_template_mask) && (mk & MK_tmpl_decl_mask);
+
+	  spec.spec = is_decl ? inner : type;
+	  add_mergeable_specialization (is_decl, &spec, decl, spec_flags);
+	}
 
       if (TREE_CODE (decl) == INTEGER_CST && !TREE_OVERFLOW (decl))
 	{
@@ -8154,7 +8159,10 @@  trees_in::decl_value ()
     {
       tree e = match_mergeable_specialization (true, &spec);
       if (!e)
-	add_mergeable_specialization (spec.tmpl, spec.args, decl, spec_flags);
+	{
+	  spec.spec = inner;
+	  add_mergeable_specialization (true, &spec, decl, spec_flags);
+	}
       else if (e != existing)
 	set_overrun ();
     }
@@ -10344,14 +10352,14 @@  trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
 	{
 	  /* Make sure we can locate the decl.  */
 	  tree existing = match_mergeable_specialization
-	    (bool (mk & MK_tmpl_decl_mask), entry, false);
+	    (bool (mk & MK_tmpl_decl_mask), entry);
 
 	  gcc_assert (existing);
 	  if (mk & MK_tmpl_decl_mask)
 	    {
 	      if (mk & MK_tmpl_alias_mask)
 		/* It should be in both tables.  */
-		gcc_assert (match_mergeable_specialization (false, entry, false)
+		gcc_assert (match_mergeable_specialization (false, entry)
 			    == TREE_TYPE (existing));
 	      else if (mk & MK_tmpl_tmpl_mask)
 		if (tree ti = DECL_TEMPLATE_INFO (existing))
@@ -10659,6 +10667,7 @@  trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
 
   if (mk & MK_template_mask)
     {
+      // FIXME: We could stream the specialization hash?
       spec_entry spec;
       spec.tmpl = tree_node ();
       spec.args = tree_node ();
@@ -12869,7 +12878,7 @@  specialization_add (bool decl_p, spec_entry *entry, void *data_)
        /* Only alias templates can appear in both tables (and
 	  if they're in the type table they must also be in the decl table).  */
        gcc_checking_assert
-	 (!match_mergeable_specialization (true, entry, false)
+	 (!match_mergeable_specialization (true, entry)
 	  == (decl_p || !DECL_ALIAS_TEMPLATE_P (entry->tmpl)));
     }
   else if (VAR_OR_FUNCTION_DECL_P (entry->spec))
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index 81df8c88ccd..5e485f10d19 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -29955,28 +29955,19 @@  walk_specializations (bool decls_p,
 }
 
 /* Lookup the specialization of *ELT, in the decl or type
-   specialization table.  Return the SPEC that's already there (NULL if
-   nothing).  If INSERT is true, and there was nothing, add the new
-   spec.  */
+   specialization table.  Return the SPEC that's already there, or
+   NULL if nothing.  */
 
 tree
-match_mergeable_specialization (bool decl_p, spec_entry *elt, bool insert)
+match_mergeable_specialization (bool decl_p, spec_entry *elt)
 {
   hash_table<spec_hasher> *specializations
     = decl_p ? decl_specializations : type_specializations;
   hashval_t hash = spec_hasher::hash (elt);
-  spec_entry **slot
-    = specializations->find_slot_with_hash (elt, hash,
-					    insert ? INSERT : NO_INSERT);
-  if (slot && *slot)
-    return (*slot)->spec;
+  auto *slot = specializations->find_slot_with_hash (elt, hash, NO_INSERT);
 
-  if (insert)
-    {
-      auto entry = ggc_alloc<spec_entry> ();
-      *entry = *elt;
-      *slot = entry;
-    }
+  if (slot)
+    return (*slot)->spec;
 
   return NULL_TREE;
 }
@@ -30012,23 +30003,43 @@  get_mergeable_specialization_flags (tree tmpl, tree decl)
   return flags;
 }
 
-/* Add a new specialization of TMPL.  FLAGS is as returned from
+/* Add a new specialization described by SPEC.  DECL is the
+   maybe-template decl and FLAGS is as returned from
    get_mergeable_specialization_flags.  */
 
 void
-add_mergeable_specialization (tree tmpl, tree args, tree decl, unsigned flags)
+add_mergeable_specialization (bool decl_p, spec_entry *elt,
+			      tree decl, unsigned flags)
 {
+  hash_table<spec_hasher> *specializations
+    = decl_p ? decl_specializations : type_specializations;
+
+  hashval_t hash = spec_hasher::hash (elt);
+  auto *slot = specializations->find_slot_with_hash (elt, hash, INSERT);
+
+  /* We don't distinguish different constrained partial type
+     specializations, so there could be duplicates.  Everything else
+     must be new.   */
+  if (!(flags & 2 && *slot))
+    {
+      gcc_checking_assert (!*slot);
+
+      auto entry = ggc_alloc<spec_entry> ();
+      *entry = *elt;
+      *slot = entry;
+    }
+
   if (flags & 1)
-    DECL_TEMPLATE_INSTANTIATIONS (tmpl)
-      = tree_cons (args, decl, DECL_TEMPLATE_INSTANTIATIONS (tmpl));
+    DECL_TEMPLATE_INSTANTIATIONS (elt->tmpl)
+      = tree_cons (elt->args, decl, DECL_TEMPLATE_INSTANTIATIONS (elt->tmpl));
 
   if (flags & 2)
     {
       /* A partial specialization.  */
-      DECL_TEMPLATE_SPECIALIZATIONS (tmpl)
-	= tree_cons (args, decl, DECL_TEMPLATE_SPECIALIZATIONS (tmpl));
-      TREE_TYPE (DECL_TEMPLATE_SPECIALIZATIONS (tmpl))
-	= TREE_TYPE (DECL_TEMPLATE_RESULT (decl));
+      tree cons = tree_cons (elt->args, decl,
+			     DECL_TEMPLATE_SPECIALIZATIONS (elt->tmpl));
+      TREE_TYPE (cons) = elt->spec;
+      DECL_TEMPLATE_SPECIALIZATIONS (elt->tmpl) = cons;
     }
 }
 
diff --git c/gcc/testsuite/g++.dg/modules/pr99285_a.H w/gcc/testsuite/g++.dg/modules/pr99285_a.H
new file mode 100644
index 00000000000..14d0aa06c03
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99285_a.H
@@ -0,0 +1,11 @@ 
+// PR 99285 ICE with template-template-parm
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+
+template<typename... _Tp> struct common_type;
+
+template<> struct common_type<> {};
+
+template<typename _Tp0> struct common_type<_Tp0> {};
+
+template<typename _Tp1, typename _Tp2> struct common_type<_Tp1, _Tp2> {};
diff --git c/gcc/testsuite/g++.dg/modules/pr99285_b.H w/gcc/testsuite/g++.dg/modules/pr99285_b.H
new file mode 100644
index 00000000000..01a8eaf6228
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99285_b.H
@@ -0,0 +1,7 @@ 
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+import  "pr99285_a.H";
+
+template<typename _Rep1, typename _Rep2,
+	 typename _CRep = typename common_type<_Rep1, _Rep2>::type>
+struct X;