https://gcc.gnu.org/g:014873fdf4bb2e00d704f182aa78b3022019ef48

commit r16-3613-g014873fdf4bb2e00d704f182aa78b3022019ef48
Author: Nathaniel Shead <[email protected]>
Date:   Fri Sep 5 13:29:12 2025 +1000

    c++/modules: Fix exported using-directive of imported namespace [PR121702]
    
    Currently we represent exported using-directives as a list of indices
    into the namespace array that we stream.  However this list of
    namespaces doesn't include any namespaces that we don't expose in this
    module's purview, and so we ICE.
    
    This patch reworks the handling to instead use the existing depset
    tracking for namespaces directly.  This means that we don't need to
    build up a second lookup map when streaming, and we can reuse the logic
    in {read,write}_namespace.  We do need to make sure that we create a
    depset for namespaces only referenced by a using-directive, though.
    
    I don't expect to be exporting large numbers of using-directives from a
    namespace, so for simplicity we stream the names as {parent, target}
    pairs.
    
    This also adjusts read handling so that we load the using-directives for
    any import (including indirect) if it's in the import list for the
    current TU.  Otherwise we run into issues if the using-directive is in
    a namespace that is otherwise never referenced in the 'export import'ing
    module, because we never walk this namespace and so never know that we
    need to emit it.  To do this the patch ensures that we calculate the
    import list before read_language is called.
    
    As a drive-by fix, I noticed that with modules 'add_using_namespace'
    will add duplicate using-directives because we compare usings against
    the target namespace, but we then push a wrapping USING_DECL instead.
    This reworks so that the contents of the structure is equivalent between
    modules and non-modules code.
    
            PR c++/121702
    
    gcc/cp/ChangeLog:
    
            * module.cc (enum module_state_counts): New counter.
            (depset::hash::add_namespace_entities): Seed using-directive
            targets for later streaming.
            (module_state::write_namespaces): Don't handle using-directives
            here.
            (module_state::read_namespaces): Likewise.
            (module_state::write_using_directives): New function.
            (module_state::read_using_directives): New function.
            (module_state::write_counts): Log using-directives.
            (module_state::read_counts): Likewise.
            (module_state::write_begin): Stream using-directives.
            (module_state::read_language): Read using-directives if
            directly importing.
            (module_state::direct_import): Update current TU import list
            before calling read_language.
            * name-lookup.cc (add_using_namespace): Fix lookup of previous
            using-directives.
            * parser.cc (cp_parser_import_declaration): Don't set
            MK_EXPORTING when performing import_module.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/namespace-10_c.C: Add check for log dump.
            * g++.dg/modules/namespace-13_a.C: New test.
            * g++.dg/modules/namespace-13_b.C: New test.
            * g++.dg/modules/namespace-13_c.C: New test.
    
    Signed-off-by: Nathaniel Shead <[email protected]>

Diff:
---
 gcc/cp/module.cc                              | 176 +++++++++++++++-----------
 gcc/cp/name-lookup.cc                         |  32 +++--
 gcc/cp/parser.cc                              |   6 -
 gcc/testsuite/g++.dg/modules/namespace-10_c.C |   4 +-
 gcc/testsuite/g++.dg/modules/namespace-13_a.C |  16 +++
 gcc/testsuite/g++.dg/modules/namespace-13_b.C |  32 +++++
 gcc/testsuite/g++.dg/modules/namespace-13_c.C |  17 +++
 7 files changed, 193 insertions(+), 90 deletions(-)

diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 170a0bdaa9e3..5ab340afe365 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -3633,6 +3633,7 @@ enum module_state_counts
   MSC_pendings,
   MSC_entities,
   MSC_namespaces,
+  MSC_using_directives,
   MSC_bindings,
   MSC_macros,
   MSC_inits,
@@ -3854,6 +3855,10 @@ class GTY((chain_next ("%h.parent"), for_user)) 
module_state {
                         unsigned, unsigned *crc_ptr);
   bool read_namespaces (unsigned);
 
+  unsigned write_using_directives (elf_out *to, depset::hash &,
+                                  vec<depset *> spaces, unsigned *crc_ptr);
+  bool read_using_directives (unsigned);
+
   void intercluster_seed (trees_out &sec, unsigned index, depset *dep);
   unsigned write_cluster (elf_out *to, depset *depsets[], unsigned size,
                          depset::hash &, unsigned *counts, unsigned *crc_ptr);
@@ -14691,17 +14696,22 @@ depset::hash::add_namespace_entities (tree ns, bitmap 
partitions)
   data.partitions = partitions;
   data.hash = this;
 
-  hash_table<named_decl_hash>::iterator end
-    (DECL_NAMESPACE_BINDINGS (ns)->end ());
-  for (hash_table<named_decl_hash>::iterator iter
-        (DECL_NAMESPACE_BINDINGS (ns)->begin ()); iter != end; ++iter)
+  for (tree binding : *DECL_NAMESPACE_BINDINGS (ns))
     {
       data.binding = nullptr;
       data.met_namespace = false;
-      if (walk_module_binding (*iter, partitions, add_binding_entity, &data))
+      if (walk_module_binding (binding, partitions, add_binding_entity, &data))
        count++;
     }
 
+  /* Seed any using-directives so that we emit the relevant namespaces.  */
+  for (tree udir : NAMESPACE_LEVEL (ns)->using_directives)
+    if (TREE_CODE (udir) == USING_DECL && DECL_MODULE_EXPORT_P (udir))
+      {
+       make_dependency (USING_DECL_DECLS (udir), depset::EK_NAMESPACE);
+       count++;
+      }
+
   if (count)
     dump () && dump ("Found %u entries", count);
   dump.outdent ();
@@ -17128,15 +17138,11 @@ module_state::write_namespaces (elf_out *to, 
vec<depset *> spaces,
   bytes_out sec (to);
   sec.begin ();
 
-  hash_map<tree, unsigned> ns_map;
-
   for (unsigned ix = 0; ix != num; ix++)
     {
       depset *b = spaces[ix];
       tree ns = b->get_entity ();
 
-      ns_map.put (ns, ix);
-
       /* This could be an anonymous namespace even for a named module,
         since we can still emit no-linkage decls.  */
       gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL);
@@ -17178,31 +17184,6 @@ module_state::write_namespaces (elf_out *to, 
vec<depset *> spaces,
        }
     }
 
-  /* Now write exported using-directives, as a sequence of 1-origin indices in
-     the spaces array (not entity indices): First the using namespace, then the
-     used namespaces.  And then a zero terminating the list.  :: is
-     represented as index -1.  */
-  auto emit_one_ns = [&](unsigned ix, tree ns) {
-    for (auto udir: NAMESPACE_LEVEL (ns)->using_directives)
-      {
-       if (TREE_CODE (udir) != USING_DECL || !DECL_MODULE_EXPORT_P (udir))
-         continue;
-       tree ns2 = USING_DECL_DECLS (udir);
-       dump() && dump ("Writing using-directive in %N for %N",
-                       ns, ns2);
-       sec.u (ix);
-       sec.u (*ns_map.get (ns2) + 1);
-      }
-  };
-  emit_one_ns (-1, global_namespace);
-  for (unsigned ix = 0; ix != num; ix++)
-    {
-      depset *b = spaces[ix];
-      tree ns = b->get_entity ();
-      emit_one_ns (ix + 1, ns);
-    }
-  sec.u (0);
-
   sec.end (to, to->name (MOD_SNAME_PFX ".nms"), crc_p);
   dump.outdent ();
 }
@@ -17221,8 +17202,6 @@ module_state::read_namespaces (unsigned num)
   dump () && dump ("Reading namespaces");
   dump.indent ();
 
-  tree *ns_map = XALLOCAVEC (tree, num);
-
   for (unsigned ix = 0; ix != num; ix++)
     {
       unsigned entity_index = sec.u ();
@@ -17284,8 +17263,6 @@ module_state::read_namespaces (unsigned num)
        DECL_ATTRIBUTES (inner)
          = tree_cons (get_identifier ("abi_tag"), tags, DECL_ATTRIBUTES 
(inner));
 
-      ns_map[ix] = inner;
-
       /* Install the namespace.  */
       (*entity_ary)[entity_lwm + entity_index] = inner;
       if (DECL_MODULE_IMPORT_P (inner))
@@ -17301,41 +17278,79 @@ module_state::read_namespaces (unsigned num)
        }
     }
 
-  /* Read the exported using-directives.  */
-  while (unsigned ix = sec.u ())
+  dump.outdent ();
+  if (!sec.end (from ()))
+    return false;
+  return true;
+}
+
+unsigned
+module_state::write_using_directives (elf_out *to, depset::hash &table,
+                                     vec<depset *> spaces, unsigned *crc_p)
+{
+  dump () && dump ("Writing using-directives");
+  dump.indent ();
+
+  bytes_out sec (to);
+  sec.begin ();
+
+  unsigned num = 0;
+  auto emit_one_ns = [&](depset *parent_dep)
     {
-      tree ns;
-      if (ix == (unsigned)-1)
-       ns = global_namespace;
-      else
-       {
-         if (--ix >= num)
-           {
-             sec.set_overrun ();
-             break;
-           }
-         ns = ns_map [ix];
-       }
-      unsigned ix2 = sec.u ();
-      if (--ix2 >= num)
+      tree parent = parent_dep->get_entity ();
+      for (auto udir : NAMESPACE_LEVEL (parent)->using_directives)
        {
-         sec.set_overrun ();
-         break;
-       }
-      tree ns2 = ns_map [ix2];
-      if (directness)
-       {
-         dump() && dump ("Reading using-directive in %N for %N",
-                         ns, ns2);
-         /* In an export import this will mark the using-directive as
-            exported, so it will be emitted again.  */
-         add_using_namespace (ns, ns2);
+         if (TREE_CODE (udir) != USING_DECL || !DECL_MODULE_EXPORT_P (udir))
+           continue;
+         tree target = USING_DECL_DECLS (udir);
+         depset *target_dep = table.find_dependency (target);
+         gcc_checking_assert (target_dep);
+
+         dump () && dump ("Writing using-directive in %N for %N",
+                          parent, target);
+         write_namespace (sec, parent_dep);
+         write_namespace (sec, target_dep);
+         ++num;
        }
-      else
-       /* Ignore using-directives from indirect imports, we only want them
-          from our own imports.  */
-       dump() && dump ("Ignoring using-directive in %N for %N",
-                       ns, ns2);
+    };
+
+  emit_one_ns (table.find_dependency (global_namespace));
+  for (depset *parent_dep : spaces)
+    emit_one_ns (parent_dep);
+
+  sec.end (to, to->name (MOD_SNAME_PFX ".udi"), crc_p);
+  dump.outdent ();
+
+  return num;
+}
+
+bool
+module_state::read_using_directives (unsigned num)
+{
+  if (!bitmap_bit_p ((*modules)[0]->imports, mod))
+    {
+      dump () && dump ("Ignoring using-directives because module %M "
+                      "is not visible in this TU", this);
+      return true;
+    }
+
+  bytes_in sec;
+
+  if (!sec.begin (loc, from (), MOD_SNAME_PFX ".udi"))
+    return false;
+
+  dump () && dump ("Reading using-directives");
+  dump.indent ();
+
+  for (unsigned ix = 0; ix != num; ++ix)
+    {
+      tree parent = read_namespace (sec);
+      tree target = read_namespace (sec);
+      if (sec.get_overrun ())
+       break;
+
+      dump () && dump ("Read using-directive in %N for %N", parent, target);
+      add_using_namespace (parent, target);
     }
 
   dump.outdent ();
@@ -19857,6 +19872,7 @@ module_state::write_counts (elf_out *to, unsigned 
counts[MSC_HWM],
       dump ("Pendings %u", counts[MSC_pendings]);
       dump ("Entities %u", counts[MSC_entities]);
       dump ("Namespaces %u", counts[MSC_namespaces]);
+      dump ("Using-directives %u", counts[MSC_using_directives]);
       dump ("Macros %u", counts[MSC_macros]);
       dump ("Initializers %u", counts[MSC_inits]);
     }
@@ -19883,6 +19899,7 @@ module_state::read_counts (unsigned counts[MSC_HWM])
       dump ("Pendings %u", counts[MSC_pendings]);
       dump ("Entities %u", counts[MSC_entities]);
       dump ("Namespaces %u", counts[MSC_namespaces]);
+      dump ("Using-directives %u", counts[MSC_using_directives]);
       dump ("Macros %u", counts[MSC_macros]);
       dump ("Initializers %u", counts[MSC_inits]);
     }
@@ -20165,7 +20182,8 @@ ool_cmp (const void *a_, const void *b_)
      qualified-names       : binding value(s)
      MOD_SNAME_PFX.README   : human readable, strings
      MOD_SNAME_PFX.ENV      : environment strings, strings
-     MOD_SNAME_PFX.nms             : namespace hierarchy
+     MOD_SNAME_PFX.nms      : namespace hierarchy
+     MOD_SNAME_PFX.udi      : namespace using-directives
      MOD_SNAME_PFX.bnd      : binding table
      MOD_SNAME_PFX.spc      : specialization table
      MOD_SNAME_PFX.imp      : import table
@@ -20411,6 +20429,11 @@ module_state::write_begin (elf_out *to, cpp_reader 
*reader,
   if (counts[MSC_namespaces])
     write_namespaces (to, spaces, counts[MSC_namespaces], &crc);
 
+  /* Write any using-directives.  */
+  if (counts[MSC_namespaces])
+    counts[MSC_using_directives]
+      = write_using_directives (to, table, spaces, &crc);
+
   /* Write the bindings themselves.  */
   counts[MSC_bindings] = write_bindings (to, sccs, &crc);
 
@@ -20662,11 +20685,16 @@ module_state::read_language (bool outermost)
                         counts[MSC_sec_lwm], counts[MSC_sec_hwm]))
     ok = false;
 
-  /* Read the namespace hierarchy. */
+  /* Read the namespace hierarchy.  */
   if (ok && counts[MSC_namespaces]
       && !read_namespaces (counts[MSC_namespaces]))
     ok = false;
 
+  /* Read any using-directives.  */
+  if (ok && counts[MSC_using_directives]
+      && !read_using_directives (counts[MSC_using_directives]))
+    ok = false;
+
   if (ok && !read_bindings (counts[MSC_bindings],
                            counts[MSC_sec_lwm], counts[MSC_sec_hwm]))
     ok = false;
@@ -21753,6 +21781,8 @@ direct_import (module_state *import, cpp_reader *reader)
     if (!import->do_import (reader, true))
       gcc_unreachable ();
 
+  (*modules)[0]->set_import (import, import->exported_p);
+
   if (import->loadedness < ML_LANGUAGE)
     {
       if (!keyed_table)
@@ -21760,8 +21790,6 @@ direct_import (module_state *import, cpp_reader *reader)
       import->read_language (true);
     }
 
-  (*modules)[0]->set_import (import, import->exported_p);
-
   dump.pop (n);
   timevar_stop (TV_MODULE_IMPORT);
 }
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 9a2d0747ba48..43b95bffecfa 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -8957,20 +8957,34 @@ pop_nested_namespace (tree ns)
 static void
 add_using_namespace (vec<tree, va_gc> *&usings, tree target)
 {
+  /* Find if this using already exists.  */
+  tree old = NULL_TREE;
   if (usings)
-    for (unsigned ix = usings->length (); ix--;)
-      if ((*usings)[ix] == target)
-       return;
+    for (tree t : *usings)
+      if (USING_DECL_DECLS (t) == target)
+       {
+         old = t;
+         break;
+       }
 
+  tree decl = old;
+  if (!decl)
+    {
+      decl = build_lang_decl (USING_DECL, NULL_TREE, NULL_TREE);
+      USING_DECL_DECLS (decl) = target;
+    }
+
+  /* Update purviewness and exportedness in case that has changed.  */
   if (modules_p ())
     {
-      tree u = build_lang_decl (USING_DECL, NULL_TREE, NULL_TREE);
-      USING_DECL_DECLS (u) = target;
-      DECL_MODULE_EXPORT_P (u) = module_exporting_p ();
-      DECL_MODULE_PURVIEW_P (u) = module_purview_p ();
-      target = u;
+      if (module_purview_p ())
+       DECL_MODULE_PURVIEW_P (decl) = true;
+      if (module_exporting_p ())
+       DECL_MODULE_EXPORT_P (decl) = true;
     }
-  vec_safe_push (usings, target);
+
+  if (!old)
+    vec_safe_push (usings, decl);
 }
 
 /* Convenience overload for the above, taking the user as its first
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 9f9bd5650047..8d1187f8cdae 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16267,13 +16267,7 @@ cp_parser_import_declaration (cp_parser *parser, 
module_parse mp_state,
                      " must not be from header inclusion");
        }
 
-      auto mk = module_kind;
-      if (exporting)
-       module_kind |= MK_EXPORTING;
-
       import_module (mod, token->location, exporting, attrs, parse_in);
-
-      module_kind = mk;
     }
 }
 
diff --git a/gcc/testsuite/g++.dg/modules/namespace-10_c.C 
b/gcc/testsuite/g++.dg/modules/namespace-10_c.C
index aee00bdc4388..7e03c19d2461 100644
--- a/gcc/testsuite/g++.dg/modules/namespace-10_c.C
+++ b/gcc/testsuite/g++.dg/modules/namespace-10_c.C
@@ -1,6 +1,8 @@
-// { dg-additional-options -fmodules }
+// { dg-additional-options "-fmodules -fdump-lang-module" }
 
 import M2;
 
 A::AT var1;
 AT var2;                       // { dg-error "AT" }
+
+// { dg-final { scan-lang-dump {Ignoring using-directives because module M1 is 
not visible} module } }
diff --git a/gcc/testsuite/g++.dg/modules/namespace-13_a.C 
b/gcc/testsuite/g++.dg/modules/namespace-13_a.C
new file mode 100644
index 000000000000..09255fd8bbba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/namespace-13_a.C
@@ -0,0 +1,16 @@
+// PR c++/121702
+// { dg-additional-options "-fmodules -fdump-lang-module" }
+// { dg-module-cmi a }
+
+export module a;
+export namespace a {
+  constexpr int f() { return 42; }
+}
+
+namespace x {}
+namespace y {
+  export using namespace x;
+}
+
+// { dg-final { scan-lang-dump {Using-directives 1} module } }
+// { dg-final { scan-lang-dump {Writing using-directive in '::y' for '::x'} 
module } }
diff --git a/gcc/testsuite/g++.dg/modules/namespace-13_b.C 
b/gcc/testsuite/g++.dg/modules/namespace-13_b.C
new file mode 100644
index 000000000000..1b309617814a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/namespace-13_b.C
@@ -0,0 +1,32 @@
+// PR c++/121702
+// { dg-additional-options "-fmodules -Wno-global-module -fdump-lang-module" }
+// { dg-module-cmi b }
+
+module;
+
+namespace gmf::blah {}
+namespace gmf::other {}
+
+export module b;
+export import a;
+// { dg-final { scan-lang-dump {Read using-directive in '::y' for '::x'} 
module } }
+
+export namespace b {
+  using namespace a;
+  using namespace gmf::blah;
+}
+namespace c {
+  using namespace gmf;
+  export using namespace gmf;
+  using namespace a;
+}
+
+// { dg-final { scan-lang-dump {Using-directives 3} module } }
+
+// { dg-final { scan-lang-dump {Writing using-directive in '::b' for '::a'} 
module } }
+// { dg-final { scan-lang-dump {Writing using-directive in '::b' for 
'::gmf::blah'} module } }
+// { dg-final { scan-lang-dump {Writing using-directive in '::c' for '::gmf'} 
module } }
+// { dg-final { scan-lang-dump-not {Writing using-directive in '::y' for 
'::x'} module } }
+
+// { dg-final { scan-lang-dump {Writing namespace:[0-9]* '::gmf::blah', 
public} module } }
+// { dg-final { scan-lang-dump-not {Writing namespace:[0-9]* '::gmf::other'} 
module } }
diff --git a/gcc/testsuite/g++.dg/modules/namespace-13_c.C 
b/gcc/testsuite/g++.dg/modules/namespace-13_c.C
new file mode 100644
index 000000000000..d04ef37cdbf5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/namespace-13_c.C
@@ -0,0 +1,17 @@
+// PR c++/121702
+// { dg-additional-options "-fmodules" }
+
+import b;
+namespace gmf::blah {
+  constexpr int g() { return 123; }
+}
+namespace gmf::other {
+  constexpr int h() { return 99; }
+}
+namespace x {
+  constexpr int i() { return 5; }
+}
+static_assert(b::f() == 42);
+static_assert(b::g() == 123);
+static_assert(c::other::h() == 99);
+static_assert(y::i() == 5);

Reply via email to