]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/modules: Fix ICE when writing imported using-directive [PR122915]
authorNathaniel Shead <nathanieloshead@gmail.com>
Mon, 1 Dec 2025 12:32:40 +0000 (23:32 +1100)
committerNathaniel Shead <nathanieloshead@gmail.com>
Mon, 1 Dec 2025 23:12:08 +0000 (10:12 +1100)
The crash in the PR is caused because we are attempting to write a
using-directive that we never made a dep for.  This should only happen
for imported using-directives, where if we never opened the relevant
namespace in the module purview we don't think there's anything
interesting to discover and so never walk it.

There's actually no reason we need to emit imported using-directives at
all, however, unless they came from a partition, because importers will
be able to get that directive directly from the originating module if it
was going to be visible anyway.  And we will always walk and create a
dependency (marked !import_p) for partition decls.  So this patch fixes
the ICE by just skipping such cases.

To help validate this the patch also starts setting DECL_MODULE_IMPORT_P
correctly for using-directives.

PR c++/122915

gcc/cp/ChangeLog:

* module.cc (module_state::write_using_directives): Don't emit
imported using-directives.
(module_state::read_using_directives): Rename
add_using_namespace to add_imported_using_namespace.
* name-lookup.cc (add_using_namespace): Handle imported
using-directives.
(add_imported_using_namespace): Rename to match new
functionality.
* name-lookup.h (add_using_namespace): Rename to...
(add_imported_using_namespace): ...this.

gcc/testsuite/ChangeLog:

* g++.dg/modules/namespace-16_a.C: New test.
* g++.dg/modules/namespace-16_b.C: New test.
* g++.dg/modules/namespace-16_c.C: New test.
* g++.dg/modules/namespace-16_d.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/module.cc
gcc/cp/name-lookup.cc
gcc/cp/name-lookup.h
gcc/testsuite/g++.dg/modules/namespace-16_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-16_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-16_c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/namespace-16_d.C [new file with mode: 0644]

index 7b3f9326a2a2bb9efa5e3500fb7cb1a3fb56e611..91e305c280b79a37eb50eb06740018207e8e94d0 100644 (file)
@@ -17639,7 +17639,16 @@ module_state::write_using_directives (elf_out *to, depset::hash &table,
          bool exported = DECL_MODULE_EXPORT_P (udir);
          tree target = USING_DECL_DECLS (udir);
          depset *target_dep = table.find_dependency (target);
-         gcc_checking_assert (target_dep);
+
+         /* An using-directive imported from a different module might not
+            have been walked earlier (PR c++/122915).  But importers will
+            be able to just refer to the decl in that module unless it was
+            a partition anyway, so we don't have anything to do here.  */
+         if (!target_dep || target_dep->is_import ())
+           {
+             gcc_checking_assert (DECL_MODULE_IMPORT_P (udir));
+             continue;
+           }
 
          dump () && dump ("Writing using-directive in %N for %N",
                           parent, target);
@@ -17688,7 +17697,7 @@ module_state::read_using_directives (unsigned num)
 
       dump () && dump ("Read using-directive in %N for %N", parent, target);
       if (exported || is_module () || is_partition ())
-       add_using_namespace (parent, target);
+       add_imported_using_namespace (parent, target);
     }
 
   dump.outdent ();
index 682f2ed49e7c535e85f5ce80938484dbc1050c0b..4c07fd40f644b824e2a1c8219dd5e6ac6357dca1 100644 (file)
@@ -9015,7 +9015,8 @@ pop_nested_namespace (tree ns)
    unqualified search.  */
 
 static void
-add_using_namespace (vec<tree, va_gc> *&usings, tree target)
+add_using_namespace (vec<tree, va_gc> *&usings, tree target,
+                    bool imported = false)
 {
   /* Find if this using already exists.  */
   tree old = NULL_TREE;
@@ -9032,15 +9033,18 @@ add_using_namespace (vec<tree, va_gc> *&usings, tree target)
     {
       decl = build_lang_decl (USING_DECL, NULL_TREE, NULL_TREE);
       USING_DECL_DECLS (decl) = target;
+      DECL_MODULE_IMPORT_P (decl) = imported;
     }
 
-  /* Update purviewness and exportedness in case that has changed.  */
+  /* Update module flags in case that has changed.  */
   if (modules_p ())
     {
       if (module_purview_p ())
        DECL_MODULE_PURVIEW_P (decl) = true;
       if (module_exporting_p ())
        DECL_MODULE_EXPORT_P (decl) = true;
+      if (!imported)
+       DECL_MODULE_IMPORT_P (decl) = false;
     }
 
   if (!old)
@@ -9048,13 +9052,14 @@ add_using_namespace (vec<tree, va_gc> *&usings, tree target)
 }
 
 /* Convenience overload for the above, taking the user as its first
-   parameter.  */
+   parameter, for use when importing a using-directive.  */
 
 void
-add_using_namespace (tree ns, tree target)
+add_imported_using_namespace (tree ns, tree target)
 {
   add_using_namespace (NAMESPACE_LEVEL (ns)->using_directives,
-                      ORIGINAL_NAMESPACE (target));
+                      ORIGINAL_NAMESPACE (target),
+                      /*imported=*/true);
 }
 
 /* Tell the debug system of a using directive.  */
index 859186a2f2c5bdb34c8f53d46f641482030bcb2c..da277c49b1a64eac8b4aee459890b0c04ac390dc 100644 (file)
@@ -477,7 +477,6 @@ 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);
@@ -508,6 +507,7 @@ extern bool set_module_binding (tree ctx, tree name, unsigned mod,
                                tree value, tree type, tree visible,
                                tree internal);
 extern void add_module_namespace_decl (tree ns, tree decl);
+extern void add_imported_using_namespace (tree, tree);
 
 enum WMB_Flags
 {
diff --git a/gcc/testsuite/g++.dg/modules/namespace-16_a.C b/gcc/testsuite/g++.dg/modules/namespace-16_a.C
new file mode 100644 (file)
index 0000000..8475ca0
--- /dev/null
@@ -0,0 +1,9 @@
+// PR c++/122915
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi imagine }
+
+export module imagine;
+namespace ns {}
+export namespace ig {
+  using namespace ns;
+}
diff --git a/gcc/testsuite/g++.dg/modules/namespace-16_b.C b/gcc/testsuite/g++.dg/modules/namespace-16_b.C
new file mode 100644 (file)
index 0000000..aed3e8a
--- /dev/null
@@ -0,0 +1,9 @@
+// PR c++/122915
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi tests:part }
+
+export module tests:part;
+namespace abc {}
+namespace part {
+  export using namespace abc;
+}
diff --git a/gcc/testsuite/g++.dg/modules/namespace-16_c.C b/gcc/testsuite/g++.dg/modules/namespace-16_c.C
new file mode 100644 (file)
index 0000000..0638e35
--- /dev/null
@@ -0,0 +1,12 @@
+// PR c++/122915
+// { dg-additional-options "-fmodules -fdump-lang-module" }
+// { dg-module-cmi tests }
+
+export module tests;
+export import :part;
+import imagine;
+using namespace ig;
+
+// { dg-final { scan-lang-dump {Writing using-directive in '::' for '::ig'} module } }
+// { dg-final { scan-lang-dump {Writing using-directive in '::part' for '::abc'} module } }
+// { dg-final { scan-lang-dump-not {Writing using-directive in '::ig' for '::ns'} module } }
diff --git a/gcc/testsuite/g++.dg/modules/namespace-16_d.C b/gcc/testsuite/g++.dg/modules/namespace-16_d.C
new file mode 100644 (file)
index 0000000..f126d41
--- /dev/null
@@ -0,0 +1,10 @@
+// PR c++/122915
+// { dg-additional-options "-fmodules" }
+
+module tests;
+
+namespace ns { using T = int; };
+T x = 123;
+
+namespace abc { using U = double; };
+part::U y = 3.14;