https://gcc.gnu.org/g:e1db44e3060237004f30006e630b94e8f4095f08
commit r15-10351-ge1db44e3060237004f30006e630b94e8f4095f08 Author: Nathaniel Shead <nathanielosh...@gmail.com> Date: Mon Sep 22 19:16:01 2025 +1000 c++/modules: Fix language linkage handling [PR122019] The ICE in the linked PR is caused because when current_lang_name is lang_name_c, set_decl_linkage calls decl_linkage on the retrofitted declaration. This is problematic because at this point we haven't finished streaming the declaration, and so we crash when attempting to access these missing bits (such as the type). The only declarations we can reach here will be things like types that don't get a language linkage anyway, so it seems reasonable to just hardcode a C++ language linkage here to work around the issue. An alternative fix would be to override current_lang_name in lazy_load_binding instead, but this is potentially confusing if we ever deliberately implement `extern "C" import "header_unit.hpp";` to override the language linkage of imported declarations, so I went with this approach instead. (Though it seems we never will do this.) While testing this I found that we don't currently complain about mismatching language linkages for variables, and module_may_redeclare doesn't cope well with implementation units, so this patch also fixes those issues. PR c++/122019 gcc/cp/ChangeLog: * module.cc (trees_in::install_entity): Don't be affected by global language linkage state. (trees_in::is_matching_decl): Check mismatching language linkage for variables too. (module_may_redeclare): Report the correct module name for partitions and implementation units. gcc/testsuite/ChangeLog: * g++.dg/modules/lang-4_a.C: New test. * g++.dg/modules/lang-4_b.C: New test. Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> Reviewed-by: Jason Merrill <ja...@redhat.com> (cherry picked from commit fe2f86a960435a7c8c4e5134a0dfc64dd6062157) Diff: --- gcc/cp/module.cc | 35 ++++++++++++++++++++------------- gcc/testsuite/g++.dg/modules/lang-4_a.C | 22 +++++++++++++++++++++ gcc/testsuite/g++.dg/modules/lang-4_b.C | 26 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 1deadcc189ab..9ff2863d04eb 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -8109,7 +8109,14 @@ trees_in::install_entity (tree decl) if (!DECL_LANG_SPECIFIC (not_tmpl) || !DECL_MODULE_ENTITY_P (not_tmpl)) { - retrofit_lang_decl (not_tmpl); + /* We don't want to use retrofit_lang_decl directly so that we aren't + affected by the language state when we load in. */ + if (!DECL_LANG_SPECIFIC (not_tmpl)) + { + maybe_add_lang_decl_raw (not_tmpl, false); + gcc_checking_assert (!VAR_OR_FUNCTION_DECL_P (not_tmpl)); + SET_DECL_LANGUAGE (not_tmpl, lang_cplusplus); + } DECL_MODULE_ENTITY_P (not_tmpl) = true; /* Insert into the entity hash (it cannot already be there). */ @@ -12148,7 +12155,15 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) // FIXME: do more precise errors at point of mismatch const char *mismatch_msg = nullptr; - if (TREE_CODE (d_inner) == FUNCTION_DECL) + + if (VAR_OR_FUNCTION_DECL_P (d_inner) + && DECL_EXTERN_C_P (d_inner) != DECL_EXTERN_C_P (e_inner)) + { + mismatch_msg = G_("conflicting language linkage for imported " + "declaration %#qD"); + goto mismatch; + } + else if (TREE_CODE (d_inner) == FUNCTION_DECL) { tree e_ret = fndecl_declared_return_type (existing); tree d_ret = fndecl_declared_return_type (decl); @@ -12165,13 +12180,6 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) tree e_type = TREE_TYPE (e_inner); tree d_type = TREE_TYPE (d_inner); - if (DECL_EXTERN_C_P (d_inner) != DECL_EXTERN_C_P (e_inner)) - { - mismatch_msg = G_("conflicting language linkage for imported " - "declaration %#qD"); - goto mismatch; - } - for (tree e_args = TYPE_ARG_TYPES (e_type), d_args = TYPE_ARG_TYPES (d_type); e_args != d_args && (e_args || d_args); @@ -20641,7 +20649,7 @@ module_may_redeclare (tree olddecl, tree newdecl) // FIXME: Should we be checking this in more places on the scope chain? return true; - module_state *old_mod = (*modules)[0]; + module_state *old_mod = get_primary ((*modules)[0]); module_state *new_mod = old_mod; tree old_origin = get_originating_module_decl (decl); @@ -20651,7 +20659,7 @@ module_may_redeclare (tree olddecl, tree newdecl) if (DECL_LANG_SPECIFIC (old_inner) && DECL_MODULE_IMPORT_P (old_inner)) { unsigned index = import_entity_index (old_origin); - old_mod = import_entity_module (index); + old_mod = get_primary (import_entity_module (index)); } bool newdecl_attached_p = module_attach_p (); @@ -20664,7 +20672,7 @@ module_may_redeclare (tree olddecl, tree newdecl) if (DECL_LANG_SPECIFIC (new_inner) && DECL_MODULE_IMPORT_P (new_inner)) { unsigned index = import_entity_index (new_origin); - new_mod = import_entity_module (index); + new_mod = get_primary (import_entity_module (index)); } } @@ -20675,8 +20683,7 @@ module_may_redeclare (tree olddecl, tree newdecl) /* Both are GM entities, OK. */ return true; - if (new_mod == old_mod - || (new_mod && get_primary (new_mod) == get_primary (old_mod))) + if (new_mod == old_mod) /* Both attached to same named module, OK. */ return true; } diff --git a/gcc/testsuite/g++.dg/modules/lang-4_a.C b/gcc/testsuite/g++.dg/modules/lang-4_a.C new file mode 100644 index 000000000000..cef2aae00ed2 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lang-4_a.C @@ -0,0 +1,22 @@ +// PR c++/122019 +// { dg-additional-options "-fmodules -Wno-global-module" } + +module; + +typedef int pthread_once_t; + +export module M; + +namespace ns { + using ::pthread_once_t; +} + +// note: non-function types don't have language linkage +extern "C++" enum E { c }; +extern "C" typedef int T; + +extern "C" int foo; +extern "C++" int bar; + +extern "C" int qux; +extern "C++" int zap; diff --git a/gcc/testsuite/g++.dg/modules/lang-4_b.C b/gcc/testsuite/g++.dg/modules/lang-4_b.C new file mode 100644 index 000000000000..2085ed152113 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lang-4_b.C @@ -0,0 +1,26 @@ +// PR c++/122019 +// { dg-additional-options "-fmodules -Wno-global-module" } +// Language linkage for types, variables, and lazy-loading in extern contexts. + +module; + +extern "C" enum E { c }; +extern "C++" typedef int T; + +extern "C++" int foo; // { dg-message "existing" } +extern "C" int bar; // { dg-message "existing" } + +module M; + +extern "C" ns::pthread_once_t x; + +E e; +T t; + +extern "C" { int use1 = foo; } // { dg-message "during load" } +extern "C" { int use2 = bar; } // { dg-message "during load" } +// { dg-error "conflicting language linkage for imported declaration 'int foo'" "" { target *-*-* } 0 } +// { dg-error "conflicting language linkage for imported declaration 'int bar'" "" { target *-*-* } 0 } + +extern "C++" int qux; // { dg-error "conflicting declaration" } +extern "C" int zap; // { dg-error "conflicting declaration" }