diff mbox series

c++: Redesign pending entity handling [PR 99170]

Message ID c3d8730f-50b2-3700-8eb5-d715f3a6b67e@acm.org
State New
Headers show
Series c++: Redesign pending entity handling [PR 99170] | expand

Commit Message

Nathan Sidwell March 4, 2021, 12:46 p.m. UTC
This patch addresses 99170.  with modules (and in particular header 
units), one module can provide a (maybe nested) class or template and 
another module can provide a definition or (maybe partial) 
specialization of said entity, or member thereof.  when both are 
imported into a 3rd TU, and that TU instantiates or uses the class, it 
needs to stream in those entities (in general).  But how does it key 
those entities to the original?  It can't /just/ use the entity index, 
because, when header-units and/or partitions are in play, the entity 
index /is not unique/.  I had two complicated schemes that tried to 
unify that, but it failed.  Here's a simpler scheme.  Such pending 
entities are keyed to the namespace and identifier of the 
namespace-scope entity that contains them.  Thus the final TU needs to 
find that entity and look in a hash table for lists of sections that 
need loading just before instantiating a template or looking inside a class.

I would like to make this more efficient, but given the complex scheme 
failed, I'm shooting for correctness right now.  There will be a follow 
up patch to complete the cleanup this enables.

         PR c++/99170
         gcc/cp/
         * cp-tree.h
         * lex.c (cxx_dup_lang_specific_decl): Adjust for module_attached_p
         rename.
         * module.cc (class pending_key): New.
         (default_hash_traits<pending_key>): New specialization.
         (pending_map_t): New typedef.
         (pending_table): Replace old table.
         (trees_out::lang_decl_bools): Adjust.
         (trees_in::lang_decl_bools): Adjust.
         (trees_in::install_entity): Drop pending member and specialization
         handling.
         (find_pending_key): New.
         (depset::hash::fiund_dependencies): Use it.
         (pendset_lazy_load): Delete.
         (module_state::write_cluster): Don't count pendings here.  Bye
         Duff's device-like thing.
         (module_state::write_pendings): Reimplement.
         (module_state::read_pendings): Reimplement.
         (lazy_specializations_p): Delete.
         (module_state::write): Adjust write_pendings call.
         (lazy_load_pendings): New.
         (lazy_load_specializations): Delete.
         (lazy_load_members): Delete.
         (init_modules): Adjust.
         * name-lookup.c (maybe_lazily_declare): Call lazy_load_pendings
         not lazy_load_members.
         (note_pending_specializations): Delete.
         (load_pending_specializations): Delete.
         * name-lookup.h (BINDING_VECTR_PENDING_SPECIALIZATIONS_P): Delete.
         (BINDING_VECTOR_PENDING_MEMBERS_P): Delete.
         (BINDING_VECTR_PENDING_MEMBERS_P): Delete.
         (note_pending_specializations): Delete.
         (load_pending_specializations): Delete.
         * pt.c (lookup_template_class_1): Call lazy_load_pendings not
         lazy_load_specializations.
         (instantiate_template_class_1): Likewise.
         (instantiate_decl): Call lazy_load_pendings.
         * typeck.c (complete_type): Likewise.
         gcc/testsuite/
         * g++.dg/modules/pr99170-1_a.H: New.
         * g++.dg/modules/pr99170-1_b.C: New.
         * g++.dg/modules/pr99170-2.h: New.
         * g++.dg/modules/pr99170-2_a.C: New.
         * g++.dg/modules/pr99170-2_b.C: New.
         * g++.dg/modules/pr99170-3_a.H: New.
         * g++.dg/modules/pr99170-3_b.C: New.
         * g++.dg/modules/inst-2_b.C: Adjust scan.
         * g++.dg/modules/inst-4_a.C: Adjust scan.
         * g++.dg/modules/inst-4_b.C: Adjust scan.
         * g++.dg/modules/member-def-1_b.C: Adjust scan.
         * g++.dg/modules/member-def-1_c.C: Adjust scan.
         * g++.dg/modules/tpl-spec-1_a.C: Adjust scan.
         * g++.dg/modules/tpl-spec-1_b.C: Adjust scan.
         * g++.dg/modules/tpl-spec-2_b.C: Adjust scan.
         * g++.dg/modules/tpl-spec-2_c.C: Adjust scan.
         * g++.dg/modules/tpl-spec-2_d.C: Adjust scan.
         * g++.dg/modules/tpl-spec-3_a.C: Adjust scan.
         * g++.dg/modules/tpl-spec-3_b.C: Adjust scan.
         * g++.dg/modules/tpl-spec-4_a.C: Adjust scan.
         * g++.dg/modules/tpl-spec-4_b.C: Adjust scan.
         * g++.dg/modules/tpl-spec-5_a.C: Adjust scan.
         * g++.dg/modules/tpl-spec-5_b.C: Adjust scan.
diff mbox series

Patch

diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index 699c50515e8..39e2ad83abd 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -1678,21 +1678,10 @@  check_constraint_info (tree t)
 #define DECL_MODULE_ENTITY_P(NODE) \
   (DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_entity_p)
 
-/* True if there are unloaded specializations keyed to this template.  */
-#define DECL_MODULE_PENDING_SPECIALIZATIONS_P(NODE)	\
-  (DECL_LANG_SPECIFIC (TEMPLATE_DECL_CHECK (NODE))	\
-   ->u.base.module_pending_p)
-
-/* True if this class has unloaded members.  These should be loaded
-   before we do member lookups.   */
-#define DECL_MODULE_PENDING_MEMBERS_P(NODE)		\
-  (DECL_LANG_SPECIFIC (TYPE_DECL_CHECK (NODE))		\
-   ->u.base.module_pending_p)
-
 /* DECL that has attached decls for ODR-relatedness.  */
 #define DECL_MODULE_ATTACHMENTS_P(NODE)			\
   (DECL_LANG_SPECIFIC (TREE_CHECK2(NODE,FUNCTION_DECL,VAR_DECL))\
-   ->u.base.module_pending_p)
+   ->u.base.module_attached_p)
 
 /* Whether this is an exported DECL.  Held on any decl that can appear
    at namespace scope (function, var, type, template, const or
@@ -2771,10 +2760,8 @@  struct GTY(()) lang_decl_base {
   unsigned module_import_p : 1;     	   /* from an import */
   unsigned module_entity_p : 1;		   /* is in the entitity ary &
 					      hash.  */
-  /* TEMPLATE_DECL has specializations or,
-     TYPE_DECL has class members yet to load, or
-     VAR_DECL or FUNCTION_DECL has attached decls.     */
-  unsigned module_pending_p : 1;
+  /* VAR_DECL or FUNCTION_DECL has attached decls.     */
+  unsigned module_attached_p : 1;
 
   /* 12 spare bits.  */
 };
@@ -7025,9 +7012,7 @@  extern void mangle_module (int m, bool include_partition);
 extern void mangle_module_fini ();
 extern void lazy_load_binding (unsigned mod, tree ns, tree id,
 			       binding_slot *bslot);
-extern void lazy_load_specializations (tree tmpl);
-extern void lazy_load_members (tree decl);
-extern bool lazy_specializations_p (unsigned, bool, bool);
+extern void lazy_load_pendings (tree decl);
 extern module_state *preprocess_module (module_state *, location_t,
 					bool in_purview, 
 					bool is_import, bool export_p,
diff --git c/gcc/cp/lex.c w/gcc/cp/lex.c
index 848f3d92cc9..c83346b617d 100644
--- c/gcc/cp/lex.c
+++ w/gcc/cp/lex.c
@@ -1010,7 +1010,7 @@  cxx_dup_lang_specific_decl (tree node)
      (module_purview_p still does).  */
   ld->u.base.module_entity_p = false;
   ld->u.base.module_import_p = false;
-  ld->u.base.module_pending_p = false;
+  ld->u.base.module_attached_p = false;
   
   if (GATHER_STATISTICS)
     {
diff --git c/gcc/cp/module.cc w/gcc/cp/module.cc
index b7b9c3734f2..3ee71e5211f 100644
--- c/gcc/cp/module.cc
+++ w/gcc/cp/module.cc
@@ -53,16 +53,13 @@  along with GCC; see the file COPYING3.  If not see
    the third indicates whether it was an import into this TU or not.
 
    The more detailed flags are DECL_MODULE_PARTITION_P,
-   DECL_MODULE_ENTITY_P & DECL_MODULE_PENDING_SPECIALIZATIONS_P.  The
-   first is set in a primary interface unit on decls that were read
-   from module partitions (these will have DECL_MODULE_IMPORT_P set
-   too).  Such decls will be streamed out to the primary's CMI.
-   DECL_MODULE_ENTITY_P is set when an entity is imported, even if it
-   matched a non-imported entity.  Such a decl will not have
-   DECL_MODULE_IMPORT_P set, even though it has an entry in the entity
-   map and array.  DECL_MODULE_PENDING_SPECIALIZATIONS_P is set on a
-   primary template, and indicates there are specializations that
-   should be streamed in before trying to specialize this template.
+   DECL_MODULE_ENTITY_P.  The first is set in a primary interface unit
+   on decls that were read from module partitions (these will have
+   DECL_MODULE_IMPORT_P set too).  Such decls will be streamed out to
+   the primary's CMI.  DECL_MODULE_ENTITY_P is set when an entity is
+   imported, even if it matched a non-imported entity.  Such a decl
+   will not have DECL_MODULE_IMPORT_P set, even though it has an entry
+   in the entity map and array.
 
    Header units are module-like.
 
@@ -2644,6 +2641,58 @@  depset *depset::make_entity (tree entity, entity_kind ek, bool is_defn)
   return r;
 }
 
+class pending_key
+{
+public:
+  tree ns;
+  tree id;
+};
+
+template<>
+struct default_hash_traits<pending_key>
+{
+  using value_type = pending_key;
+
+  static const bool empty_zero_p = false;
+  static hashval_t hash (const value_type &k)
+  {
+    hashval_t h = IDENTIFIER_HASH_VALUE (k.id);
+    h = iterative_hash_hashval_t (DECL_UID (k.ns), h);
+
+    return h;
+  }
+  static bool equal (const value_type &k, const value_type &l)
+  {
+    return k.ns == l.ns && k.id == l.id;
+  }
+  static void mark_empty (value_type &k)
+  {
+    k.ns = k.id = NULL_TREE;
+  }
+  static void mark_deleted (value_type &k)
+  {
+    k.ns = NULL_TREE;
+    gcc_checking_assert (k.id);
+  }
+  static bool is_empty (const value_type &k)
+  {
+    return k.ns == NULL_TREE && k.id == NULL_TREE;
+  }
+  static bool is_deleted (const value_type &k)
+  {
+    return k.ns == NULL_TREE && k.id != NULL_TREE;
+  }
+  static void remove (value_type &)
+  {
+  }
+};
+
+typedef hash_map<pending_key, auto_vec<unsigned>> pending_map_t;
+
+/* Not-loaded entities that are keyed to a namespace-scope
+   identifier.  See module_state::write_pendings for details.  */
+pending_map_t *pending_table;
+
 /* Decls that need some post processing once a batch of lazy loads has
    completed.  */
 vec<tree, va_heap, vl_embed> *post_load_decls;
@@ -2804,20 +2853,6 @@  uintset<T>::hash::get (typename uintset<T>::hash::key_t key, bool extract)
   return res;
 }
 
-/* Entities keyed to some other entity.  When we load the other
-   entity, we mark it in some way to indicate there are further
-   entities to load when you start looking inside it.  For instance
-   template specializations are keyed to their most general template.
-   When we instantiate that, we need to know all the partial
-   specializations (to pick the right template), and all the known
-   specializations (to avoid reinstantiating it, and/or whether it's
-   extern).  The values split into two ranges.  If !MSB set, indices
-   into the entity array.  If MSB set, an indirection to another
-   pendset.  */
-
-typedef uintset<unsigned> pendset;
-static pendset::hash *pending_table;
-
 /* Some entities are attached to another entitity for ODR purposes.
    For example, at namespace scope, 'inline auto var = []{};', that
    lambda is attached to 'var', and follows its ODRness.  */
@@ -3709,8 +3744,8 @@  class GTY((chain_next ("%h.parent"), for_user)) module_state {
   bool read_inits (unsigned count);
 
  private:
-  void write_pendings (elf_out *to, vec<depset *> depsets,
-		      depset::hash &, unsigned count, unsigned *crc_ptr);
+  unsigned write_pendings (elf_out *to, vec<depset *> depsets,
+			   depset::hash &, unsigned *crc_ptr);
   bool read_pendings (unsigned count);
 
  private:
@@ -5644,7 +5679,7 @@  trees_out::lang_decl_bools (tree t)
   WB (lang->u.base.dependent_init_p);
   WB (lang->u.base.module_purview_p);
   if (VAR_OR_FUNCTION_DECL_P (t))
-    WB (lang->u.base.module_pending_p);
+    WB (lang->u.base.module_attached_p);
   switch (lang->u.base.selector)
     {
     default:
@@ -5714,7 +5749,7 @@  trees_in::lang_decl_bools (tree t)
   RB (lang->u.base.dependent_init_p);
   RB (lang->u.base.module_purview_p);
   if (VAR_OR_FUNCTION_DECL_P (t))
-    RB (lang->u.base.module_pending_p);
+    RB (lang->u.base.module_attached_p);
   switch (lang->u.base.selector)
     {
     default:
@@ -7572,12 +7607,7 @@  trees_in::install_entity (tree decl)
 
   /* Insert the real decl into the entity ary.  */
   unsigned ident = state->entity_lwm + entity_index - 1;
-  binding_slot &elt = (*entity_ary)[ident];
-
-  /* See module_state::read_pendings for how this got set.  */
-  int pending = elt.get_lazy () & 3;
-
-  elt = decl;
+  (*entity_ary)[ident] = decl;
 
   /* And into the entity map, if it's not already there.  */
   if (!DECL_LANG_SPECIFIC (decl)
@@ -7592,26 +7622,6 @@  trees_in::install_entity (tree decl)
       gcc_checking_assert (!existed);
       slot = ident;
     }
-  else if (pending != 0)
-    {
-      unsigned key_ident = import_entity_index (decl);
-      if (pending & 1)
-	if (!pending_table->add (key_ident, ~ident))
-	  pending &= ~1;
-
-      if (pending & 2)
-	if (!pending_table->add (~key_ident, ~ident))
-	  pending &= ~2;
-    }
-
-  if (pending & 1)
-    DECL_MODULE_PENDING_SPECIALIZATIONS_P (decl) = true;
-
-  if (pending & 2)
-    {
-      DECL_MODULE_PENDING_MEMBERS_P (decl) = true;
-      gcc_checking_assert (TREE_CODE (decl) != TEMPLATE_DECL);
-    }
 
   return true;
 }
@@ -10558,7 +10568,6 @@  trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
 		// FIXME: What if the return type is a voldemort?
 		key.ret = fndecl_declared_return_type (inner);
 	    }
-
 	  break;
 
 	case MK_field:
@@ -13225,6 +13234,28 @@  depset::hash::add_mergeable (depset *mergeable)
   dep->deps.safe_push (mergeable);
 }
 
+/* Find the innermost-namespace scope of DECL, and that
+   namespace-scope decl.  */
+
+tree
+find_pending_key (tree decl, tree *decl_p = nullptr)
+{
+  tree ns = decl;
+  do
+    {
+      decl = ns;
+      ns = CP_DECL_CONTEXT (ns);
+      if (TYPE_P (ns))
+	ns = TYPE_NAME (ns);
+    }
+  while (TREE_CODE (ns) != NAMESPACE_DECL);
+
+  if (decl_p)
+    *decl_p = decl;
+
+  return ns;
+}
+
 /* Iteratively find dependencies.  During the walk we may find more
    entries on the same binding that need walking.  */
 
@@ -13259,13 +13290,22 @@  depset::hash::find_dependencies (module_state *module)
 		walker.tree_node (OVL_FUNCTION (decl));
 	      else if (TREE_VISITED (decl))
 		/* A global tree.  */;
-	      else if (TREE_CODE (decl) == NAMESPACE_DECL
-		       && !DECL_NAMESPACE_ALIAS (decl))
+	      else if (item->get_entity_kind () == EK_NAMESPACE)
 		add_namespace_context (current, CP_DECL_CONTEXT (decl));
 	      else
 		{
 		  walker.mark_declaration (decl, current->has_defn ());
 
+		  if (!walker.is_key_order ()
+		      && (item->get_entity_kind () == EK_SPECIALIZATION
+			  || item->get_entity_kind () == EK_PARTIAL
+			  || (item->get_entity_kind () == EK_DECL
+			      && item->is_member ())))
+		    {
+		      tree ns = find_pending_key (decl, nullptr);
+		      add_namespace_context (item, ns);
+		    }
+
 		  // FIXME: Perhaps p1815 makes this redundant? Or at
 		  // least simplifies it.  Voldemort types are only
 		  // ever emissable when containing (inline) function
@@ -13709,43 +13749,6 @@  depset::hash::connect ()
   return connector.result;
 }
 
-/* Load the entities referred to by this pendset.  */
-
-static bool
-pendset_lazy_load (pendset *pendings, bool specializations_p)
-{
-  bool ok = true;
-
-  for (unsigned ix = 0; ok && ix != pendings->num; ix++)
-    {
-      unsigned index = pendings->values[ix];
-      if (index & ~(~0u >> 1))
-	{
-	  /* An indirection.  */
-	  if (specializations_p)
-	    index = ~index;
-	  pendset *other = pending_table->get (index, true);
-	  if (!pendset_lazy_load (other, specializations_p))
-	    ok = false;
-	}
-      else
-	{
-	  module_state *module = import_entity_module (index);
-	  binding_slot *slot = &(*entity_ary)[index];
-	  if (!slot->is_lazy ())
-	    dump () && dump ("Specialiation %M[%u] already loaded",
-			     module, index - module->entity_lwm);
-	  else if (!module->lazy_load (index - module->entity_lwm, slot))
-	    ok = false;
-	}
-    }
-
-  /* We own set, so delete it now.  */
-  delete pendings;
-
-  return ok;
-}
-
 /* Initialize location spans.  */
 
 void
@@ -14665,12 +14668,8 @@  module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
 	  break;
 
 	case depset::EK_DECL:
-	  if (b->is_member ())
-	    {
-	    case depset::EK_SPECIALIZATION:  /* Yowzer! */
-	    case depset::EK_PARTIAL:  /* Hey, let's do it again! */
-	      counts[MSC_pendings]++;
-	    }
+	case depset::EK_SPECIALIZATION:
+	case depset::EK_PARTIAL:
 	  b->cluster = counts[MSC_entities]++;
 	  sec.mark_declaration (b->get_entity (), b->has_defn ());
 	  /* FALLTHROUGH  */
@@ -14688,10 +14687,9 @@  module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
   dump (dumper::CLUSTER) && (dump.outdent (), true);
 
   /* Ensure every imported decl is referenced before we start
-     streaming.  This ensures that we never encounter the
-     situation where this cluster instantiates some implicit
-     member that importing some other decl causes to be
-     instantiated.  */
+     streaming.  This ensures that we never encounter the situation
+     where this cluster instantiates some implicit member that
+     importing some other decl causes to be instantiated.  */
   sec.set_importing (+1);
   for (unsigned ix = 0; ix != size; ix++)
     {
@@ -14704,14 +14702,14 @@  module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
 
 	  if (dep->is_binding ())
 	    {
-	      /* A cross-module using decl could be here.  */
 	      for (unsigned ix = dep->deps.length (); --ix;)
 		{
 		  depset *bind = dep->deps[ix];
-		  if (bind->get_entity_kind () == depset::EK_USING
-		      && bind->deps[1]->is_import ())
+		  if (bind->get_entity_kind () == depset::EK_USING)
+		    bind = bind->deps[1];
+		  if (bind->is_import ())
 		    {
-		      tree import = bind->deps[1]->get_entity ();
+		      tree import = bind->get_entity ();
 		      if (!TREE_VISITED (import))
 			{
 			  sec.tree_node (import);
@@ -15397,98 +15395,208 @@  module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm)
 
 /* Write the pending table to MOD_SNAME_PFX.pnd
 
-   Specializations & partials are keyed to their primary template.
-   Members are keyed to their context.
-
-   For specializations & partials, primary templates are keyed to the
-   (namespace name) of their originating decl (because that's the only
-   handle we have).  */
+   The pending table holds information about clusters that need to be
+   loaded because they contain information about something that is not
+   found by namespace-scope lookup.
+
+   The three cases are:
+
+   (a) Template (maybe-partial) specializations that we have
+   instantiated or defined.  When an importer needs to instantiate
+   that template, they /must have/ the partial, explicit & extern
+   specializations available.  If they have the other specializations
+   available, they'll have less work to do.  Thus, when we're about to
+   instantiate FOO, we have to be able to ask 'are there any
+   specialization of FOO in our imports?'.
+
+   (b) (Maybe-implicit) member functions definitions.  A class could
+   be defined in one header, and an inline member defined in a
+   different header (this occurs in the STL).  Similarly, like the
+   specialization case, an implicit member function could have been
+   'instantiated' in one module, and it'd be nice to not have to
+   reinstantiate it in another.
+
+   (c) A member classes completed elsewhere.  A member class could be
+   declared in one header and defined in another.  We need to know to
+   load the class definition before looking in it.  This turns out to
+   be a specific case of #b, so we can treat these the same.  But it
+   does highlight an issue -- there could be an intermediate import
+   between the outermost containing namespace-scope class and the
+   innermost being-defined member class.  This is actually possible
+   with all of these cases, so be aware -- we're not just talking of
+   one level of import to get to the innermost namespace.
+
+   This gets complicated fast, it took me multiple attempts to even
+   get something remotely working.  Partially because I focussed on
+   optimizing what I think turns out to be a smaller problem, given
+   the known need to do the more general case *anyway*.  I document
+   the smaller problem, because it does appear to be the natural way
+   to do it.  It's trap!
+
+   **** THE TRAP
+
+   Let's refer to the primary template or the containing class as the
+   KEY.  And the specialization or member as the PENDING-ENTITY.  (To
+   avoid having to say those mouthfuls all the time.)
+
+   In either case, we have an entity and we need some way of mapping
+   that to a set of entities that need to be loaded before we can
+   proceed with whatever processing of the entity we were going to do.
+
+   We need to link the key to the pending-entity in some way.  Given a
+   key, tell me the pending-entities I need to have loaded.  However
+   we tie the key to the pending-entity must not rely on the key being
+   loaded -- that'd defeat the lazy loading scheme.
+
+   As the key will be an import in we know its entity number (either
+   because we imported it, or we're writing it out too).  Thus we can
+   generate a map of key-indices to pending-entities.  The
+   pending-entity indices will be into our span of the entity table,
+   and thus allow them to be lazily loaded.  The key index will be
+   into another slot of the entity table.  Notice that this checking
+   could be expensive, we don't want to iterate over a bunch of
+   pending-entity indices (across multiple imports), every time we're
+   about do to the thing with the key.  We need to quickly determine
+   'definitely nothing needed'.
+
+   That's almost good enough, except that key indices are not unique
+   in a couple of cases :( Specifically the Global Module or a module
+   partition can result in multiple modules assigning an entity index
+   for the key.  The decl-merging on loading will detect that so we
+   only have one Key loaded, and in the entity hash it'll indicate the
+   entity index of first load.  Which might be different to how we
+   know it.  Notice this is restricted to GM entities or this-module
+   entities.  Foreign imports cannot have this.
+
+   We can simply resolve this in the direction of how this module
+   referred to the key to how the importer knows it.  Look in the
+   entity table slot that we nominate, maybe lazy load it, and then
+   lookup the resultant entity in the entity hash to learn how the
+   importer knows it.
+
+   But we need to go in the other direction :( Given the key, find all
+   the index-aliases of that key.  We can partially solve that by
+   adding an alias hash table.  Whenever we load a merged decl, add or
+   augment a mapping from the entity (or its entity-index) to the
+   newly-discovered index.  Then when we look for pending entities of
+   a key, we also iterate over this aliases this mapping provides.
+
+   But that requires the alias to be loaded.  And that's not
+   necessarily true.
+
+   *** THE SIMPLER WAY
+
+   The remaining fixed thing we have is the innermost namespace
+   containing the ultimate namespace-scope container of the key and
+   the name of that container (which might be the key itself).  I.e. a
+   namespace-decl/identifier/module tuple.  Let's call this the
+   top-key.  We'll discover that the module is not important here,
+   because of cross-module possibilities mentioned in case #c above.
+   We can't markup namespace-binding slots.  The best we can do is
+   mark the binding vector with 'there's something here', and have
+   another map from namespace/identifier pairs to a vector of pending
+   entity indices.
+
+   Maintain a pending-entity map.  This is keyed by top-key, and
+   maps to a vector of pending-entity indices.  On the binding vector
+   have flags saying whether the pending-name-entity map has contents.
+   (We might want to further extend the key to be GM-vs-Partition and
+   specialization-vs-member, but let's not get ahead of ourselves.)
+
+   For every key-like entity, find the outermost namespace-scope
+   name.  Use that to lookup in the pending-entity map and then make
+   sure the specified entities are loaded.
+
+   An optimization might be to have a flag in each key-entity saying
+   that it's top key might be in the entity table.  It's not clear to
+   me how to set that flag cheaply -- cheaper than just looking.
+
+   FIXME: It'd be nice to have a bit in decls to tell us whether to
+   even try this.  We can have a 'already done' flag, that we set when
+   we've done KLASS's lazy pendings.  When we import a module that
+   registers pendings on the same top-key as KLASS we need to clear
+   the flag.  A recursive walk of the top-key clearing the bit will
+   suffice.  Plus we only need to recurse on classes that have the bit
+   set.  (That means we need to set the bit on parents of KLASS here,
+   don't forget.)  However, first: correctness, second: efficiency.  */
 
-void
+unsigned
 module_state::write_pendings (elf_out *to, vec<depset *> depsets,
-			      depset::hash &table,
-			      unsigned count, unsigned *crc_p)
+			      depset::hash &table, unsigned *crc_p)
 {
-  dump () && dump ("Writing %u pendings", count);
+  dump () && dump ("Writing pending-entities");
   dump.indent ();
 
   trees_out sec (to, this, table);
   sec.begin ();
 
+  unsigned count = 0;
+  tree cache_ns = NULL_TREE;
+  tree cache_id = NULL_TREE;
+  unsigned cache_section = ~0;
   for (unsigned ix = 0; ix < depsets.length (); ix++)
     {
       depset *d = depsets[ix];
-      depset::entity_kind kind = d->get_entity_kind ();
-      tree key = NULL_TREE;
-      bool is_spec = false;
-      
 
-      if (kind == depset::EK_SPECIALIZATION)
-	{
-	  is_spec = true;
-	  key = reinterpret_cast <spec_entry *> (d->deps[0])->tmpl;
-	}
-      else if (kind == depset::EK_PARTIAL)
-	{
-	  is_spec = true;
-	  key = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (d->get_entity ()));
-	}
-      else if (kind == depset::EK_DECL && d->is_member ())
-	{
-	  tree ctx = DECL_CONTEXT (d->get_entity ());
-	  key = TYPE_NAME (ctx);
-	  if (tree ti = CLASSTYPE_TEMPLATE_INFO (ctx))
-	    if (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == key)
-	      key = TI_TEMPLATE (ti);
-	}
+      if (d->is_binding ())
+	continue;
 
-      // FIXME:OPTIMIZATION More than likely when there is one pending
-      // member, there will be others.  All written in the same
-      // section and keyed to the same class.  We only need to record
-      // one of them.  The same is not true for specializations
+      if (d->is_import ())
+	continue;
 
-      if (key)
-	{
-	  gcc_checking_assert (!d->is_import ());
+      if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION
+	    || d->get_entity_kind () == depset::EK_PARTIAL
+	    || (d->get_entity_kind () == depset::EK_DECL && d->is_member ())))
+	continue;
 
-	  {
-	    /* Key the entity to its key.  */
-	    depset *key_dep = table.find_dependency (key);
-	    if (key_dep->get_entity_kind () == depset::EK_REDIRECT)
-	      key_dep = key_dep->deps[0];
-	    unsigned key_origin
-	      = key_dep->is_import () ? key_dep->section : 0;
-	    sec.u (key_origin);
-	    sec.u (key_dep->cluster);
-	    sec.u (d->cluster);
-	    dump () && dump ("%s %N entity:%u keyed to %M[%u] %N",
-			     is_spec ? "Specialization" : "Member",
-			     d->get_entity (),
-			     d->cluster, (*modules)[key_origin],
-			     key_dep->cluster, key);
-	  }
+      tree key_decl = nullptr;
+      tree key_ns = find_pending_key (d->get_entity (), &key_decl);
+      tree key_name = DECL_NAME (key_decl);
 
-	  if (is_spec)
+      if (IDENTIFIER_ANON_P (key_name))
+	{
+	  gcc_checking_assert (IDENTIFIER_LAMBDA_P (key_name));
+	  if (tree attached = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (key_decl)))
+	    key_name = DECL_NAME (attached);
+	  else
 	    {
-	      /* Key the general template to the originating decl.  */
-	      tree origin = get_originating_module_decl (key);
-	      sec.tree_node (CP_DECL_CONTEXT (origin));
-	      sec.tree_node (DECL_NAME (origin));
-
-	      unsigned origin_ident = import_entity_index (origin);
-	      module_state *origin_from = this;
-	      if (!(origin_ident & ~(~0u>>1)))
-		origin_from = import_entity_module (origin_ident);
-	      sec.u (origin_from->remap);
+	      /* There's nothing to attach it to.  Must
+		 always reinstantiate.  */
+	      dump ()
+		&& dump ("Unattached lambda %N[%u] section:%u",
+			 d->get_entity_kind () == depset::EK_DECL
+			 ? "Member" : "Specialization", d->get_entity (),
+			 d->cluster, d->section);
+	      continue;
 	    }
-	  else
-	    sec.tree_node (NULL);
-	  count--;
 	}
+
+      char const *also = "";
+      if (d->section == cache_section
+	  && key_ns == cache_ns
+	  && key_name == cache_id)
+	/* Same section & key as previous, no need to repeat ourselves.  */
+	also = "also ";
+      else
+	{
+	  cache_ns = key_ns;
+	  cache_id = key_name;
+	  cache_section = d->section;
+	  gcc_checking_assert (table.find_dependency (cache_ns));
+	  sec.tree_node (cache_ns);
+	  sec.tree_node (cache_id);
+	  sec.u (d->cluster);
+	  count++;
+	}
+      dump () && dump ("Pending %s %N entity:%u section:%u %skeyed to %P",
+		       d->get_entity_kind () == depset::EK_DECL
+		       ? "member" : "specialization", d->get_entity (),
+		       d->cluster, cache_section, also, cache_ns, cache_id);
       }
-  gcc_assert (!count);
   sec.end (to, to->name (MOD_SNAME_PFX ".pnd"), crc_p);
   dump.outdent ();
+
+  return count;
 }
 
 bool
@@ -15504,72 +15612,28 @@  module_state::read_pendings (unsigned count)
 
   for (unsigned ix = 0; ix != count; ix++)
     {
-      unsigned key_origin = slurp->remap_module (sec.u ());
-      unsigned key_index = sec.u ();
-      unsigned ent_index = sec.u ();
-      module_state *from = (*modules)[key_origin];
-      tree ns = sec.tree_node ();
+      pending_key key;
+      unsigned index;
 
-      if (!key_origin
-	  || key_index >= from->entity_num || ent_index >= entity_num
-	  || (ns && TREE_CODE (ns) != NAMESPACE_DECL))
+      key.ns = sec.tree_node ();
+      key.id = sec.tree_node ();
+      index = sec.u ();
+
+      if (!key.ns || !key.id
+	  || !(TREE_CODE (key.ns) == NAMESPACE_DECL
+	       && !DECL_NAMESPACE_ALIAS (key.ns))
+	  || !identifier_p (key.id)
+	  || index >= entity_num)
 	sec.set_overrun ();
 
       if (sec.get_overrun ())
 	break;
 
-      bool loaded = false;
-      dump () && dump ("%s keyed to %M[%u] entity:%u",
-		       ns ? "Specialization" : "Member",
-		       from, key_index, ent_index);
-      unsigned key_ident = from->entity_lwm + key_index;
-      if (pending_table->add (ns ? key_ident : ~key_ident,
-			      ent_index + entity_lwm))
-	{
-	  binding_slot &slot = (*entity_ary)[key_ident];
-	  if (slot.is_lazy ())
-	    slot.or_lazy (ns ? 1 : 2);
-	  else
-	    {
-	      tree key = slot;
-
-	      loaded = true;
-	      if (ns)
-		{
-		  if (key && TREE_CODE (key) == TEMPLATE_DECL)
-		    DECL_MODULE_PENDING_SPECIALIZATIONS_P (key) = true;
-		  else
-		    sec.set_overrun ();
-		}
-	      else
-		{
-		  if (key && TREE_CODE (key) == TYPE_DECL)
-		    DECL_MODULE_PENDING_MEMBERS_P (key) = true;
-		  else
-		    sec.set_overrun ();
-		}
-	    }
-	}
+      dump () && dump ("Pending:%u keyed to %P", index, key.ns, key.id);
 
-      if (ns)
-	{
-	  /* We also need to mark the namespace binding of the
-	     originating template, so we know to set its pending
-	     specializations flag, when we load it.  */
-	  tree name = sec.tree_node ();
-	  unsigned origin = slurp->remap_module (sec.u ());
-	  if (!origin || !name || TREE_CODE (name) != IDENTIFIER_NODE)
-	    sec.set_overrun ();
-	  if (sec.get_overrun ())
-	    break;
-
-	  module_state *origin_from = (*modules)[origin];
-	  if (!loaded
-	      && (origin_from->is_header ()
-		  || (origin_from->is_partition ()
-		      || origin_from->is_module ())))
-	    note_pending_specializations (ns, name, origin_from->is_header ());
-	}
+      index += entity_lwm;
+      auto &vec = pending_table->get_or_insert (key);
+      vec.safe_push (index);
     }
 
   dump.outdent ();
@@ -15578,23 +15642,6 @@  module_state::read_pendings (unsigned count)
   return true;
 }
 
-/* Return true if module MOD cares about lazy specializations keyed to
-   possibly duplicated entity bindings.  */
-
-bool
-lazy_specializations_p (unsigned mod, bool header_p, bool partition_p)
-{
-  module_state *module = (*modules)[mod];
-
-  if (module->is_header ())
-    return header_p;
-
-  if (module->is_module () || module->is_partition ())
-    return partition_p;
-
-  return false;
-}
-
 /* Read & write locations.  */
 enum loc_kind {
   LK_ORDINARY,
@@ -17873,6 +17920,8 @@  module_state::write (elf_out *to, cpp_reader *reader)
 	}
     }
 
+  /* depset::cluster - entity number (on entities)
+     depset::section - cluster number  */
   /* We'd better have written as many sections and found as many
      namespaces as we predicted.  */
   gcc_assert (counts[MSC_sec_hwm] == to->get_section_limit ()
@@ -17892,8 +17941,7 @@  module_state::write (elf_out *to, cpp_reader *reader)
   counts[MSC_bindings] = write_bindings (to, sccs, &crc);
 
   /* Write the unnamed.  */
-  if (counts[MSC_pendings])
-    write_pendings (to, sccs, table, counts[MSC_pendings], &crc);
+  counts[MSC_pendings] = write_pendings (to, sccs, table, &crc);
 
   /* Write the import table.  */
   if (config.num_imports > 1)
@@ -18928,14 +18976,19 @@  lazy_load_binding (unsigned mod, tree ns, tree id, binding_slot *mslot)
 	    module->get_flatname ());
 }
 
-/* Load any pending specializations of TMPL.  Called just before
-   instantiating TMPL.  */
+/* Load any pending entities keyed to the top-key of DECL.  */
 
 void
-lazy_load_specializations (tree tmpl)
+lazy_load_pendings (tree decl)
 {
-  gcc_checking_assert (DECL_MODULE_PENDING_SPECIALIZATIONS_P (tmpl)
-		       && DECL_MODULE_ENTITY_P (tmpl));
+  tree key_decl;
+  pending_key key;
+  key.ns = find_pending_key (decl, &key_decl);
+  key.id = DECL_NAME (key_decl);
+
+  auto *pending_vec = pending_table ? pending_table->get (key) : nullptr;
+  if (!pending_vec)
+    return;
 
   int count = errorcount + warningcount;
 
@@ -18943,21 +18996,32 @@  lazy_load_specializations (tree tmpl)
   bool ok = !recursive_lazy ();
   if (ok)
     {
-      unsigned ident = import_entity_index (tmpl);
-      if (pendset *set = pending_table->get (ident, true))
+      function_depth++; /* Prevent GC */
+      unsigned n = dump.push (NULL);
+      dump () && dump ("Reading %u pending entities keyed to %P",
+		       pending_vec->length (), key.ns, key.id);
+      for (unsigned ix = pending_vec->length (); ix--;)
 	{
-	  function_depth++; /* Prevent GC */
-	  unsigned n = dump.push (NULL);
-	  dump ()
-	    && dump ("Reading %u pending specializations keyed to %M[%u] %N",
-		     set->num, import_entity_module (ident),
-		     ident - import_entity_module (ident)->entity_lwm, tmpl);
-	  if (!pendset_lazy_load (set, true))
-	    ok = false;
-	  dump.pop (n);
+	  unsigned index = (*pending_vec)[ix];
+	  binding_slot *slot = &(*entity_ary)[index];
 
-	  function_depth--;
+	  if (slot->is_lazy ())
+	    {
+	      module_state *import = import_entity_module (index);
+	      if (!import->lazy_load (index - import->entity_lwm, slot))
+		ok = false;
+	    }
+	  else if (dump ())
+	    {
+	      module_state *import = import_entity_module (index);
+	      dump () && dump ("Entity %M[%u] already loaded",
+			       import, index - import->entity_lwm);
+	    }
 	}
+
+      pending_table->remove (key);
+      dump.pop (n);
+      function_depth--;
       lazy_snum = 0;
       post_load_processing ();
     }
@@ -18965,47 +19029,12 @@  lazy_load_specializations (tree tmpl)
   timevar_stop (TV_MODULE_IMPORT);
 
   if (!ok)
-    fatal_error (input_location, "failed to load specializations keyed to %qD",
-		 tmpl);
+    fatal_error (input_location, "failed to load pendings for %<%E%s%E%>",
+		 key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
 
   if (count != errorcount + warningcount)
-    inform (input_location,
-	    "during load of specializations keyed to %qD", tmpl);
-}
-
-void
-lazy_load_members (tree decl)
-{
-  gcc_checking_assert (DECL_MODULE_PENDING_MEMBERS_P (decl));
-  if (!DECL_MODULE_ENTITY_P (decl))
-    {
-      // FIXME: I can't help feeling that DECL_TEMPLATE_RESULT should
-      // be inserted into the entity map, or perhaps have the same
-      // DECL_UID as the template, so I don't have to do this dance
-      // here and elsewhere.  It also simplifies when DECL is a
-      // partial specialization.  (also noted elsewhere as an issue)
-      tree ti = CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (decl));
-      tree tmpl = TI_TEMPLATE (ti);
-      gcc_checking_assert (DECL_TEMPLATE_RESULT (tmpl) == decl);
-      decl = tmpl;
-    }
-
-  timevar_start (TV_MODULE_IMPORT);
-  unsigned ident = import_entity_index (decl);
-  if (pendset *set = pending_table->get (~ident, true))
-    {
-      function_depth++; /* Prevent GC */
-      unsigned n = dump.push (NULL);
-      dump () && dump ("Reading %u pending members keyed to %M[%u] %N",
-		       set->num, import_entity_module (ident),
-		       ident - import_entity_module (ident)->entity_lwm, decl);
-      pendset_lazy_load (set, false);
-      post_load_processing ();
-      dump.pop (n);
-
-      function_depth--;
-    }
-  timevar_stop (TV_MODULE_IMPORT);
+    inform (input_location, "during load of pendings for %<%E%s%E%>",
+	    key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
 }
 
 static void
@@ -19532,7 +19561,7 @@  preprocess_module (module_state *module, location_t from_loc,
 	{
 	  unsigned n = dump.push (NULL);
 
-	  dump () && dump ("Reading %s preprocessor state", module);
+	  dump () && dump ("Reading %M preprocessor state", module);
 	  name_pending_imports (reader, false);
 
 	  /* Preserve the state of the line-map.  */
@@ -19873,8 +19902,7 @@  init_modules (cpp_reader *reader)
 
   if (!flag_preprocess_only)
     {
-      pending_table = new pendset::hash (EXPERIMENT (1, 400));
-
+      pending_table = new pending_map_t (EXPERIMENT (1, 400));
       entity_map = new entity_map_t (EXPERIMENT (1, 400));
       vec_safe_reserve (entity_ary, EXPERIMENT (1, 400));
     }
diff --git c/gcc/cp/name-lookup.c w/gcc/cp/name-lookup.c
index 66c35a1c16d..f57708700c2 100644
--- c/gcc/cp/name-lookup.c
+++ w/gcc/cp/name-lookup.c
@@ -1916,10 +1916,10 @@  get_class_binding_direct (tree klass, tree name, bool want_type)
 static void
 maybe_lazily_declare (tree klass, tree name)
 {
-  tree main_decl = TYPE_NAME (TYPE_MAIN_VARIANT (klass));
-  if (DECL_LANG_SPECIFIC (main_decl)
-      && DECL_MODULE_PENDING_MEMBERS_P (main_decl))
-    lazy_load_members (main_decl);
+  /* See big comment anout module_state::write_pendings regarding adding a check
+     bit.  */
+  if (modules_p ())
+    lazy_load_pendings (TYPE_NAME (klass));
 
   /* Lazily declare functions, if we're going to search these.  */
   if (IDENTIFIER_CTOR_P (name))
@@ -4100,57 +4100,6 @@  set_module_binding (tree ns, tree name, unsigned mod, int mod_glob,
   return true;
 }
 
-void
-note_pending_specializations (tree ns, tree name, bool is_header)
-{
-  if (tree *slot = find_namespace_slot (ns, name, false))
-    if (TREE_CODE (*slot) == BINDING_VECTOR)
-      {
-	tree vec = *slot;
-	BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = true;
-	if (is_header)
-	  BINDING_VECTOR_PENDING_IS_HEADER_P (vec) = true;
-	else
-	  BINDING_VECTOR_PENDING_IS_PARTITION_P (vec) = true;
-      }
-}
-
-void
-load_pending_specializations (tree ns, tree name)
-{
-  tree *slot = find_namespace_slot (ns, name, false);
-
-  if (!slot || TREE_CODE (*slot) != BINDING_VECTOR
-      || !BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (*slot))
-    return;
-
-  tree vec = *slot;
-  BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = false;
-
-  bool do_header = BINDING_VECTOR_PENDING_IS_HEADER_P (vec);
-  bool do_partition = BINDING_VECTOR_PENDING_IS_PARTITION_P (vec);
-  BINDING_VECTOR_PENDING_IS_HEADER_P (vec) = false;
-  BINDING_VECTOR_PENDING_IS_PARTITION_P (vec) = false;
-
-  gcc_checking_assert (do_header | do_partition);
-  binding_cluster *cluster = BINDING_VECTOR_CLUSTER_BASE (vec);
-  unsigned ix = BINDING_VECTOR_NUM_CLUSTERS (vec);
-  if (BINDING_VECTOR_SLOTS_PER_CLUSTER == BINDING_SLOTS_FIXED)
-    {
-      ix--;
-      cluster++;
-    }
-
-  for (; ix--; cluster++)
-    for (unsigned jx = 0; jx != BINDING_VECTOR_SLOTS_PER_CLUSTER; jx++)
-      if (cluster->indices[jx].span
-	  && cluster->slots[jx].is_lazy ()
-	  && lazy_specializations_p (cluster->indices[jx].base,
-				     do_header, do_partition))
-	lazy_load_binding (cluster->indices[jx].base, ns, name,
-			   &cluster->slots[jx]);
-}
-
 void
 add_module_namespace_decl (tree ns, tree decl)
 {
diff --git c/gcc/cp/name-lookup.h w/gcc/cp/name-lookup.h
index d95472b7545..67e923fc611 100644
--- c/gcc/cp/name-lookup.h
+++ w/gcc/cp/name-lookup.h
@@ -177,17 +177,6 @@  struct GTY(()) tree_binding_vec {
 #define MODULE_BINDING_PARTITION_P(NODE)		\
   (OVERLOAD_CHECK (NODE)->base.volatile_flag)
 
-/* There are specializations of a template keyed to this binding.  */
-#define BINDING_VECTOR_PENDING_SPECIALIZATIONS_P(NODE) \
-  (BINDING_VECTOR_CHECK (NODE)->base.public_flag)
-/* The key is in a header unit (not a named module partition or
-   primary).  */
-#define BINDING_VECTOR_PENDING_IS_HEADER_P(NODE) \
-  (BINDING_VECTOR_CHECK (NODE)->base.protected_flag)
-/* The key is in a named module (primary or partition).  */
-#define BINDING_VECTOR_PENDING_IS_PARTITION_P(NODE) \
-  (BINDING_VECTOR_CHECK (NODE)->base.private_flag)
-
 extern void set_identifier_type_value (tree, tree);
 extern void push_binding (tree, tree, cp_binding_level*);
 extern void pop_local_binding (tree, tree);
@@ -507,8 +496,6 @@  extern unsigned walk_module_binding (tree binding, bitmap partitions,
 extern tree add_imported_namespace (tree ctx, tree name, location_t,
 				    unsigned module,
 				    bool inline_p, bool visible_p);
-extern void note_pending_specializations (tree ns, tree name, bool is_header);
-extern void load_pending_specializations (tree ns, tree name);
 extern const char *get_cxx_dialect_name (enum cxx_dialect dialect);
 
 #endif /* GCC_CP_NAME_LOOKUP_H */
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index a4686e0affb..83589101c0d 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -9796,13 +9796,7 @@  lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
 
       gen_tmpl = most_general_template (templ);
       if (modules_p ())
-	{
-	  tree origin = get_originating_module_decl (gen_tmpl);
-	  load_pending_specializations (CP_DECL_CONTEXT (origin),
-					DECL_NAME (origin));
-	  if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
-	    lazy_load_specializations (gen_tmpl);
-	}
+	lazy_load_pendings (gen_tmpl);
 
       parmlist = DECL_TEMPLATE_PARMS (gen_tmpl);
       parm_depth = TMPL_PARMS_DEPTH (parmlist);
@@ -21001,6 +20995,9 @@  instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 
   gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
 
+  if (modules_p ())
+    lazy_load_pendings (tmpl);
+
   /* If this function is a clone, handle it specially.  */
   if (DECL_CLONED_FUNCTION_P (tmpl))
     {
@@ -21037,15 +21034,6 @@  instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 		(DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl)),
 		 targ_ptr));
 
-  if (modules_p ())
-    {
-      tree origin = get_originating_module_decl (gen_tmpl);
-      load_pending_specializations (CP_DECL_CONTEXT (origin),
-				    DECL_NAME (origin));
-      if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
-	lazy_load_specializations (gen_tmpl);
-    }
-
   /* It would be nice to avoid hashing here and then again in tsubst_decl,
      but it doesn't seem to be on the hot path.  */
   spec = retrieve_specialization (gen_tmpl, targ_ptr, 0);
@@ -25946,6 +25934,10 @@  instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
 
   gcc_checking_assert (!DECL_FUNCTION_SCOPE_P (d));
 
+  if (modules_p ())
+    /* We may have a pending instantiation of D itself.  */
+    lazy_load_pendings (d);
+
   /* Variables are never deferred; if instantiation is required, they
      are instantiated right away.  That allows for better code in the
      case that an expression refers to the value of the variable --
diff --git c/gcc/cp/typeck.c w/gcc/cp/typeck.c
index d06d7440d5c..dff4e9b63ca 100644
--- c/gcc/cp/typeck.c
+++ w/gcc/cp/typeck.c
@@ -133,8 +133,15 @@  complete_type (tree type)
 	  TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = has_nontrivial_dtor;
 	}
     }
-  else if (CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type))
-    instantiate_class_template (TYPE_MAIN_VARIANT (type));
+  else if (CLASS_TYPE_P (type))
+    {
+      if (modules_p ())
+	/* TYPE could be a class member we've not loaded the definition of.  */ 
+	lazy_load_pendings (TYPE_NAME (TYPE_MAIN_VARIANT (type)));
+
+      if (CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+	instantiate_class_template (TYPE_MAIN_VARIANT (type));
+    }
 
   return type;
 }
diff --git c/gcc/testsuite/g++.dg/modules/inst-2_b.C w/gcc/testsuite/g++.dg/modules/inst-2_b.C
index 3e918bcfb31..59e48b31a4c 100644
--- c/gcc/testsuite/g++.dg/modules/inst-2_b.C
+++ w/gcc/testsuite/g++.dg/modules/inst-2_b.C
@@ -1,4 +1,4 @@ 
-// { dg-additional-options {-fmodules-ts -fdump-lang-module-uid-alias} }
+// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias} }
 import foo;
 
 int main ()
@@ -9,6 +9,6 @@  int main ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
 
 // { dg-final { scan-lang-dump {Read:-[0-9]*'s decl spec merge key \(new\) function_decl:'::foo'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/inst-4_a.C w/gcc/testsuite/g++.dg/modules/inst-4_a.C
index e7435ec2ee1..fa94ab636cd 100644
--- c/gcc/testsuite/g++.dg/modules/inst-4_a.C
+++ w/gcc/testsuite/g++.dg/modules/inst-4_a.C
@@ -1,5 +1,5 @@ 
 // { dg-module-do run }
-// { dg-additional-options {-fmodules-ts -fdump-lang-module-graph-blocks-alias} }
+// { dg-additional-options {-fmodules-ts -fdump-lang-module-graph} }
 
 export module foo;
 // { dg-module-cmi foo }
@@ -16,5 +16,5 @@  export inline int user (int i)
   return x.m;
 }
 
-// { dg-final { scan-lang-dump {Specialization '::TPL<int>' entity:. keyed to foo\[.\] '::template TPL'} module } }
-// { dg-final { scan-lang-dump {Specialization '::TPL<int>::TPL<int>' entity:. keyed to foo\[.\] '::template TPL<T>::template TPL'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::TPL<int>' entity:. section:. keyed to '::TPL'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::TPL<int>::TPL<int>' entity:. section:. also keyed to '::TPL'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/inst-4_b.C w/gcc/testsuite/g++.dg/modules/inst-4_b.C
index c83e1c1437c..c7b02b470bd 100644
--- c/gcc/testsuite/g++.dg/modules/inst-4_b.C
+++ w/gcc/testsuite/g++.dg/modules/inst-4_b.C
@@ -1,4 +1,4 @@ 
-// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias-uid} }
+// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias} }
 import foo;
 
 int main ()
@@ -9,5 +9,5 @@  int main ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to foo\[.\] '::template TPL@foo:.'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::TPL'} module } }
 // { dg-final { scan-lang-dump {Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::TPL'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/member-def-1_b.C w/gcc/testsuite/g++.dg/modules/member-def-1_b.C
index fbe1ac44cf2..ad4826ccacd 100644
--- c/gcc/testsuite/g++.dg/modules/member-def-1_b.C
+++ w/gcc/testsuite/g++.dg/modules/member-def-1_b.C
@@ -11,4 +11,4 @@  struct frob::inner
 };
 
 // { dg-final { scan-lang-dump { Cluster members:\n  \[0\]=decl definition '::frob@foo:part1:1::inner'\n  \[1\]=decl declaration '::frob@foo:part1:1::inner::inner'\n} module } }
-// { dg-final { scan-lang-dump {Member '::frob@foo:part1:1::inner' entity:0 keyed to foo:part1\[0\] '::frob@foo:part1:1'} module } }
+// { dg-final { scan-lang-dump {Pending member '::frob@foo:part1:1::inner' entity:0 section:. keyed to '::frob'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/member-def-1_c.C w/gcc/testsuite/g++.dg/modules/member-def-1_c.C
index c7c3f6eb194..d4190a84d58 100644
--- c/gcc/testsuite/g++.dg/modules/member-def-1_c.C
+++ w/gcc/testsuite/g++.dg/modules/member-def-1_c.C
@@ -11,6 +11,6 @@  export auto foo ()
   return frob::inner ();
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending members keyed to foo:part1\[0\] '::frob@foo:part1:1'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } }
 // { dg-final { scan-lang-dump { Cluster members:\n  \[0\]=decl definition '::frob@foo:part1:1'\n  \[1\]=decl definition '::frob@foo:part1:1::inner@foo:part1:1'\n  \[2\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__dt '\n(  \[.\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__ct '\n)*  \[6\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::inner@foo:part2:2'\n  \[7\]=decl declaration '::frob@foo:part1:1::frob@foo:part1:1'\n  \[8\]=decl declaration '::frob@foo:part1:1::__as_base @foo:part1:1'\n  \[9\]=binding '::frob'\n} module } }
 // { dg-final { scan-lang-dump {Pendings 0} module } }
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-1_a.H w/gcc/testsuite/g++.dg/modules/pr99170-1_a.H
new file mode 100644
index 00000000000..d9a0b8b650a
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-1_a.H
@@ -0,0 +1,10 @@ 
+// PR 99170, pending instantiation snafu
+// { dg-additional-options {-fmodule-header} }
+// { dg-module-cmi {} }
+namespace STD {
+class string {
+public:
+  template <typename T>
+  string (const T *);
+};
+}
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-1_b.C w/gcc/testsuite/g++.dg/modules/pr99170-1_b.C
new file mode 100644
index 00000000000..d3d8ac96a79
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-1_b.C
@@ -0,0 +1,7 @@ 
+// { dg-additional-options {-fmodules-ts} }
+export module test;
+// { dg-module-cmi test }
+import "pr99170-1_a.H";
+export class A {
+  STD::string str{"ayyy"};
+};
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-2.h w/gcc/testsuite/g++.dg/modules/pr99170-2.h
new file mode 100644
index 00000000000..10259d40899
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-2.h
@@ -0,0 +1,44 @@ 
+
+namespace std
+{
+typedef long unsigned int size_t;
+}
+
+namespace __gnu_cxx
+{
+template<typename _CharT>
+struct char_traits
+{
+  typedef _CharT char_type;
+
+  static constexpr std::size_t
+    length(const char_type* __s);
+};
+
+template<typename _CharT>
+constexpr std::size_t
+  char_traits<_CharT>::
+  length(const char_type* __p)
+{
+  std::size_t __i = 0;
+  return __i;
+}
+}
+
+namespace std
+{
+template<class _CharT>
+struct char_traits;
+
+template<>
+struct char_traits<char>
+{
+  typedef char char_type;
+
+  static constexpr size_t
+    length(const char_type* __s)
+  {
+    return __gnu_cxx::char_traits<char_type>::length(__s);
+  }
+};
+}
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-2_a.C w/gcc/testsuite/g++.dg/modules/pr99170-2_a.C
new file mode 100644
index 00000000000..8b0b5e80345
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-2_a.C
@@ -0,0 +1,9 @@ 
+// pr99170 pending instantiations
+// { dg-additional-options -fmodules-ts }
+module;
+#include "pr99170-2.h"
+
+export  module  hello;
+// { dg-module-cmi hello }
+
+export void greeter (__gnu_cxx::char_traits<char> const &name);
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-2_b.C w/gcc/testsuite/g++.dg/modules/pr99170-2_b.C
new file mode 100644
index 00000000000..335eb1ffa13
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-2_b.C
@@ -0,0 +1,4 @@ 
+// { dg-additional-options -fmodules-ts }
+
+#include "pr99170-2.h"
+import hello;
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-3_a.H w/gcc/testsuite/g++.dg/modules/pr99170-3_a.H
new file mode 100644
index 00000000000..6b570b5b431
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-3_a.H
@@ -0,0 +1,11 @@ 
+// PR 99170
+// { dg-module-do link }
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+struct Foo 
+{
+  Foo () {};
+  
+};
+
+static Foo __ioinit;
diff --git c/gcc/testsuite/g++.dg/modules/pr99170-3_b.C w/gcc/testsuite/g++.dg/modules/pr99170-3_b.C
new file mode 100644
index 00000000000..76fa71d8427
--- /dev/null
+++ w/gcc/testsuite/g++.dg/modules/pr99170-3_b.C
@@ -0,0 +1,6 @@ 
+// { dg-additional-options -fmodules-ts }
+import "pr99170-3_a.H";
+
+int main ()
+{
+}
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-1_a.C w/gcc/testsuite/g++.dg/modules/tpl-spec-1_a.C
index 26350f09856..3bfb6394d56 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-1_a.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-1_a.C
@@ -17,6 +17,6 @@  template <> int foo<int> (int y)
 // { dg-final { scan-lang-dump {Dependencies of specialization function_decl:'::foo<int>'} module } }
 // { dg-final { scan-lang-dump-not {Depending definition function_decl:'::foo<int>'} module } }
 // { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=specialization declaration '::foo<int>'} module } }
-// { dg-final { scan-lang-dump {Specialization '::foo<int>' entity:[0-9]* keyed to TPL\[0\] '::template foo'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::foo<int>' entity:[0-9]* section:. keyed to '::foo'} module } }
 
 // { dg-final { scan-assembler {_Z3fooIiEiT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-1_b.C w/gcc/testsuite/g++.dg/modules/tpl-spec-1_b.C
index 1f64fbbbcb8..a1dd6566412 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-1_b.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-1_b.C
@@ -14,7 +14,6 @@  int main ()
 }
 
 // { dg-final { scan-lang-dump-not {Reading definition function_decl '::foo@TPL:.<int>'} module } }
-// { dg-final { scan-lang-dump {Specialization keyed to TPL\[0\] entity:1} module } }
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template foo@TPL:1'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
 
 // { dg-final { scan-assembler-not {_Z3fooIiEiT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-2_b.C w/gcc/testsuite/g++.dg/modules/tpl-spec-2_b.C
index d1374819261..a8b40caf912 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-2_b.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-2_b.C
@@ -13,6 +13,6 @@  template <> int foo<int> (int y)
 // { dg-final { scan-lang-dump {Dependencies of specialization function_decl:'::foo<int>'} module } }
 // { dg-final { scan-lang-dump-not {Depending definition function_decl:'::foo<int>'} module } }
 // { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=specialization declaration '::foo<int>'} module } }
-// { dg-final { scan-lang-dump {Specialization '::foo<int>' entity:[0-9]* keyed to TPL\[.\] '::template foo@TPL:.'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::foo<int>' entity:[0-9]* section:. keyed to '::foo'} module } }
 
 // { dg-final { scan-assembler {_Z3fooIiEiT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-2_c.C w/gcc/testsuite/g++.dg/modules/tpl-spec-2_c.C
index 3b0a7c733e7..51b5eadfc96 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-2_c.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-2_c.C
@@ -13,7 +13,7 @@  int main ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template foo@TPL:.'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
 // { dg-final { scan-lang-dump-not {Reading definition function_decl '::foo@TPL:.<int>'} module } }
 
 // { dg-final { scan-assembler-not {_Z3fooIiEiT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-2_d.C w/gcc/testsuite/g++.dg/modules/tpl-spec-2_d.C
index 4095dea021a..f8aae310b31 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-2_d.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-2_d.C
@@ -20,7 +20,7 @@  int two ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template foo@TPL:.'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
 // { dg-final { scan-lang-dump-not {Reading definition function_decl '::foo@TPL:.<int>'} module } }
 
 // { dg-final { scan-assembler-not {_Z3fooIiEiT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-3_a.C w/gcc/testsuite/g++.dg/modules/tpl-spec-3_a.C
index 1f3fb4023c8..1e677fc42df 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-3_a.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-3_a.C
@@ -22,6 +22,6 @@  template <> void frob::store (int i_)
 // { dg-final { scan-lang-dump {Dependencies of specialization function_decl:'::frob::store<int>'} module } }
 // { dg-final { scan-lang-dump-not {Depending definition function_decl:'::frob::store<int>'} module } }
 // { dg-final { scan-lang-dump {Cluster members:\n  \[0\]=specialization declaration '::frob::store<int>'} module } }
-// { dg-final { scan-lang-dump {Specialization '::frob::store<int>' entity:[0-9]* keyed to TPL\[1\] '::frob::template store'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::frob::store<int>' entity:[0-9]* section:. keyed to '::frob'} module } }
 
 // { dg-final { scan-assembler {_ZN4frob5storeIiEEvT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C w/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C
index a9cd8d72308..a7105ae6759 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-3_b.C
@@ -17,7 +17,7 @@  int main ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[1\] '::frob@TPL:.::template store@TPL:.'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } }
 // { dg-final { scan-lang-dump-not {Reading definition function_decl '::frob@TPL:.::store@TPL:.<int>'} module } }
 
 // { dg-final { scan-assembler-not {_ZN4frob5storeIiEEvT_:} } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-4_a.C w/gcc/testsuite/g++.dg/modules/tpl-spec-4_a.C
index 29e09deebd9..85068215d63 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-4_a.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-4_a.C
@@ -16,4 +16,4 @@  template<> struct X<int>
 
 // { dg-final { scan-lang-dump {Dependencies of specialization type_decl:'::X<int>'} module } }
 // { dg-final { scan-lang-dump {Cluster members:\n(  \[.\]=[^\n]*'\n)*  \[.\]=specialization definition '::X<int>'} module } }
-// { dg-final { scan-lang-dump {Specialization '::X<int>' entity:[0-9]* keyed to TPL\[0\] '::template X'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::X<int>' entity:[0-9]* section:. keyed to '::X'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C w/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C
index 94ced9bed38..97aa251d3e0 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-4_b.C
@@ -14,4 +14,4 @@  int main ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template X@TPL:.'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-5_a.C w/gcc/testsuite/g++.dg/modules/tpl-spec-5_a.C
index 08bcac2e078..f475b824ebd 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-5_a.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-5_a.C
@@ -16,4 +16,4 @@  template<typename T> struct X<T,1>
 
 // { dg-final { scan-lang-dump {Dependency on partial template_decl:'::template X<T,0x1>' found} module } }
 // { dg-final { scan-lang-dump {Cluster members:\n(  \[.\][^\n]*'\n)*  \[.\]=partial definition '::template X<T,0x1>'} module } }
-// { dg-final { scan-lang-dump {Specialization '::template X<T,0x1>' entity:[0-9]* keyed to TPL\[0\] '::template X'} module } }
+// { dg-final { scan-lang-dump {Pending specialization '::template X<T,0x1>' entity:[0-9]* section:. keyed to '::X'} module } }
diff --git c/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C w/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C
index 8a1a5575f45..ff3d84c1384 100644
--- c/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C
+++ w/gcc/testsuite/g++.dg/modules/tpl-spec-5_b.C
@@ -14,4 +14,4 @@  int main ()
   return 0;
 }
 
-// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template X@TPL:.'} module } }
+// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } }