]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: modules and using-directives
authorJason Merrill <jason@redhat.com>
Wed, 20 Nov 2024 22:46:54 +0000 (23:46 +0100)
committerJason Merrill <jason@redhat.com>
Wed, 28 May 2025 13:01:47 +0000 (09:01 -0400)
We weren't representing 'using namespace' at all in modules, which broke
some of the <chrono> literals tests.

This only represents exported using-declarations; others should be
irrelevant to importers, as any name lookup in the imported module that
would have cared about them was done while compiling the header unit.

I experimented with various approaches to representing them; this patch
handles them in read/write_namespaces, after the namespaces themselves.  I
spent a while pondering how to deal with the depset code in order to connect
them, but then realized it would be simpler to refer to them based on their
index in the array of namespaces.

Any using-directives from an indirect import are ignored, so in an export
import, any imported using-directives are exported again.

gcc/cp/ChangeLog:

* module.cc (module_state::write_namespaces): Write
using-directives.
(module_state::read_namespaces): And read them.
* name-lookup.cc (add_using_namespace): Add overload.  Build a
USING_DECL for modules.
(name_lookup::search_usings, name_lookup::queue_usings)
(using_directives_contain_std_p): Strip the USING_DECL.
* name-lookup.h: Declare it.
* parser.cc (cp_parser_import_declaration): Set MK_EXPORTING
for export import.

gcc/testsuite/ChangeLog:

* g++.dg/modules/namespace-8_a.C: New test.
* g++.dg/modules/namespace-8_b.C: New test.
* g++.dg/modules/namespace-9_a.C: New test.
* g++.dg/modules/namespace-9_b.C: New test.
* g++.dg/modules/namespace-10_a.C: New test.
* g++.dg/modules/namespace-10_b.C: New test.
* g++.dg/modules/namespace-10_c.C: New test.
* g++.dg/modules/namespace-11_a.C: New test.
* g++.dg/modules/namespace-11_b.C: New test.
* g++.dg/modules/namespace-11_c.C: New test.

14 files changed:
gcc/cp/module.cc
gcc/cp/name-lookup.cc
gcc/cp/name-lookup.h
gcc/cp/parser.cc
gcc/testsuite/g++.dg/modules/namespace-10_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-10_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-10_c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-11_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-11_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-11_c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-8_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-8_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-9_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-9_b.C [new file with mode: 0644]

index 17c040d26b06388561efddde1d75162283991c43..ad2acaf559fbde48fa4e99f4fbbde9093c8c048b 100644 (file)
@@ -16865,11 +16865,15 @@ 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);
@@ -16911,6 +16915,31 @@ 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 ();
 }
@@ -16929,6 +16958,8 @@ 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 ();
@@ -16990,6 +17021,8 @@ 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))
@@ -17004,6 +17037,44 @@ module_state::read_namespaces (unsigned num)
            *slot = entity_lwm + entity_index;
        }
     }
+
+  /* Read the exported using-directives.  */
+  while (unsigned ix = sec.u ())
+    {
+      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)
+       {
+         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);
+       }
+      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);
+    }
+
   dump.outdent ();
   if (!sec.end (from ()))
     return false;
index 84b5e673a6d29fad7463838700d981c8d5544b8b..9aa7c16f64b4321a77eca65d574284feb32233a0 100644 (file)
@@ -1049,7 +1049,7 @@ name_lookup::search_usings (tree scope)
   bool found = false;
   if (vec<tree, va_gc> *usings = NAMESPACE_LEVEL (scope)->using_directives)
     for (unsigned ix = usings->length (); ix--;)
-      found |= search_qualified ((*usings)[ix], true);
+      found |= search_qualified (strip_using_decl ((*usings)[ix]), true);
 
   /* Look in its inline children.  */
   if (vec<tree, va_gc> *inlinees = DECL_NAMESPACE_INLINEES (scope))
@@ -1121,7 +1121,7 @@ name_lookup::queue_usings (using_queue& queue, int depth, vec<tree, va_gc> *usin
 {
   if (usings)
     for (unsigned ix = usings->length (); ix--;)
-      queue_namespace (queue, depth, (*usings)[ix]);
+      queue_namespace (queue, depth, strip_using_decl ((*usings)[ix]));
 }
 
 /* Unqualified namespace lookup in SCOPE.
@@ -6868,7 +6868,7 @@ using_directives_contain_std_p (vec<tree, va_gc> *usings)
     return false;
 
   for (unsigned ix = usings->length (); ix--;)
-    if ((*usings)[ix] == std_node)
+    if (strip_using_decl ((*usings)[ix]) == std_node)
       return true;
 
   return false;
@@ -8943,9 +8943,27 @@ add_using_namespace (vec<tree, va_gc> *&usings, tree target)
       if ((*usings)[ix] == target)
        return;
 
+  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;
+    }
   vec_safe_push (usings, target);
 }
 
+/* Convenience overload for the above, taking the user as its first
+   parameter.  */
+
+void
+add_using_namespace (tree ns, tree target)
+{
+  add_using_namespace (NAMESPACE_LEVEL (ns)->using_directives,
+                      ORIGINAL_NAMESPACE (target));
+}
+
 /* Tell the debug system of a using directive.  */
 
 static void
index f1596c60c7a12a517281ce170f8747101807626e..4216a515afad6d4f74db62f5c1e91d518be465ac 100644 (file)
@@ -462,6 +462,7 @@ extern cxx_binding *outer_binding (tree, cxx_binding *, bool);
 extern void cp_emit_debug_info_for_using (tree, tree);
 
 extern void finish_nonmember_using_decl (tree scope, tree name);
+extern void add_using_namespace (tree, tree);
 extern void finish_using_directive (tree target, tree attribs);
 void push_local_extern_decl_alias (tree decl);
 extern tree pushdecl (tree, bool hiding = false);
index 1fb9e7fd87298e4539a5325c91086ff5b6ae5de7..d9fc5f76bc3df8bebdacb46215f3e3e99217cc2d 100644 (file)
@@ -15827,7 +15827,13 @@ 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_a.C b/gcc/testsuite/g++.dg/modules/namespace-10_a.C
new file mode 100644 (file)
index 0000000..c67c93a
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-additional-options -fmodules }
+
+export module M1;
+
+namespace A
+{
+  export using AT = int;
+}
+
+export using namespace A;
+inline AT var;
diff --git a/gcc/testsuite/g++.dg/modules/namespace-10_b.C b/gcc/testsuite/g++.dg/modules/namespace-10_b.C
new file mode 100644 (file)
index 0000000..5a3558d
--- /dev/null
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules" }
+
+export module M2;
+import M1;
+
+namespace A
+{
+  export using A::AT;
+}
diff --git a/gcc/testsuite/g++.dg/modules/namespace-10_c.C b/gcc/testsuite/g++.dg/modules/namespace-10_c.C
new file mode 100644 (file)
index 0000000..aee00bd
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-additional-options -fmodules }
+
+import M2;
+
+A::AT var1;
+AT var2;                       // { dg-error "AT" }
diff --git a/gcc/testsuite/g++.dg/modules/namespace-11_a.C b/gcc/testsuite/g++.dg/modules/namespace-11_a.C
new file mode 100644 (file)
index 0000000..c67c93a
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-additional-options -fmodules }
+
+export module M1;
+
+namespace A
+{
+  export using AT = int;
+}
+
+export using namespace A;
+inline AT var;
diff --git a/gcc/testsuite/g++.dg/modules/namespace-11_b.C b/gcc/testsuite/g++.dg/modules/namespace-11_b.C
new file mode 100644 (file)
index 0000000..c0e0b9e
--- /dev/null
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules" }
+
+export module M2;
+export import M1;
+
+namespace A
+{
+  export using A::AT;
+}
diff --git a/gcc/testsuite/g++.dg/modules/namespace-11_c.C b/gcc/testsuite/g++.dg/modules/namespace-11_c.C
new file mode 100644 (file)
index 0000000..e12a8ae
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-additional-options -fmodules }
+
+import M2;
+
+A::AT var1;
+AT var2;
diff --git a/gcc/testsuite/g++.dg/modules/namespace-8_a.C b/gcc/testsuite/g++.dg/modules/namespace-8_a.C
new file mode 100644 (file)
index 0000000..67ffc6a
--- /dev/null
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fmodules" }
+
+export module M;
+
+export namespace B
+{
+  int i;
+}
+export namespace C
+{
+  using namespace B;
+}
diff --git a/gcc/testsuite/g++.dg/modules/namespace-8_b.C b/gcc/testsuite/g++.dg/modules/namespace-8_b.C
new file mode 100644 (file)
index 0000000..7db35bf
--- /dev/null
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules" }
+
+import M;
+
+int main()
+{
+  C::i = 42;
+}
diff --git a/gcc/testsuite/g++.dg/modules/namespace-9_a.C b/gcc/testsuite/g++.dg/modules/namespace-9_a.C
new file mode 100644 (file)
index 0000000..4cc3ab0
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-additional-options -fmodules }
+
+export module M;
+
+namespace A
+{
+  export using AT = int;
+}
+
+using namespace A;
+inline AT var;
diff --git a/gcc/testsuite/g++.dg/modules/namespace-9_b.C b/gcc/testsuite/g++.dg/modules/namespace-9_b.C
new file mode 100644 (file)
index 0000000..05bb300
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-additional-options -fmodules }
+
+import M;
+
+A::AT var1;
+AT var2;                       // { dg-error "AT" }