From 6a83030557ccceef5c12e8b45d6bcd5ba274232b Mon Sep 17 00:00:00 2001 From: Nathaniel Shead Date: Mon, 1 Dec 2025 23:32:40 +1100 Subject: [PATCH] c++/modules: Fix ICE when writing imported using-directive [PR122915] 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 Reviewed-by: Jason Merrill --- gcc/cp/module.cc | 13 +++++++++++-- gcc/cp/name-lookup.cc | 15 ++++++++++----- gcc/cp/name-lookup.h | 2 +- gcc/testsuite/g++.dg/modules/namespace-16_a.C | 9 +++++++++ gcc/testsuite/g++.dg/modules/namespace-16_b.C | 9 +++++++++ gcc/testsuite/g++.dg/modules/namespace-16_c.C | 12 ++++++++++++ gcc/testsuite/g++.dg/modules/namespace-16_d.C | 10 ++++++++++ 7 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/namespace-16_a.C create mode 100644 gcc/testsuite/g++.dg/modules/namespace-16_b.C create mode 100644 gcc/testsuite/g++.dg/modules/namespace-16_c.C create mode 100644 gcc/testsuite/g++.dg/modules/namespace-16_d.C diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 7b3f9326a2a..91e305c280b 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -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 (); diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 682f2ed49e7..4c07fd40f64 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -9015,7 +9015,8 @@ pop_nested_namespace (tree ns) unqualified search. */ static void -add_using_namespace (vec *&usings, tree target) +add_using_namespace (vec *&usings, tree target, + bool imported = false) { /* Find if this using already exists. */ tree old = NULL_TREE; @@ -9032,15 +9033,18 @@ add_using_namespace (vec *&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 *&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. */ diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 859186a2f2c..da277c49b1a 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -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 index 00000000000..8475ca0e8f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/namespace-16_a.C @@ -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 index 00000000000..aed3e8a3e00 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/namespace-16_b.C @@ -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 index 00000000000..0638e35be44 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/namespace-16_c.C @@ -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 index 00000000000..f126d411b4b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/namespace-16_d.C @@ -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; -- 2.47.3