https://gcc.gnu.org/g:fe2f86a960435a7c8c4e5134a0dfc64dd6062157

commit r16-4070-gfe2f86a960435a7c8c4e5134a0dfc64dd6062157
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>

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 d5c3a83c728b..7b69e806328c 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -8144,7 +8144,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).  */
@@ -12320,7 +12327,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);
@@ -12337,13 +12352,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);
@@ -21151,7 +21159,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 = this_module ();
+  module_state *old_mod = get_primary (this_module ());
   module_state *new_mod = old_mod;
 
   tree old_origin = get_originating_module_decl (decl);
@@ -21161,7 +21169,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 ();
@@ -21174,7 +21182,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));
        }
     }
 
@@ -21185,8 +21193,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" }

Reply via email to