[PATCH] PR125430 Fixmodule-contained PRIVATE procedures must have global, ELF linkage

Jerry D <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.fortran
Message-ID <[email protected]>
See attached patch. This bug found when trying to build the Formal package.

Developed with with Assistance from Claude Sonnet 4.6.  Two new testsuite files 
provided. The change wiggled on a few exiting test cases.

Regression tested on x86_64 Linux.

OK for mainline?  What about backport?

Regards,

Jerry

----

fortran: module-contained PRIVATE procedures must have global
  ELF linkage [PR125430]

gcc/fortran/ChangeLog:

	PR fortran/125430
	* trans-decl.cc (build_function_decl): Set TREE_PUBLIC for all
	module-contained procedures so submodules compiled as separate
	translation units can reach them via host association.  Also set
	DECL_VISIBILITY to VISIBILITY_HIDDEN for PRIVATE procedures,
	matching the existing treatment of module variables.

gcc/testsuite/ChangeLog:

	PR fortran/125430
	* gfortran.dg/module_private_2.f90: Remove scan-tree-dump-times
	assertion for 'priv'; PRIVATE module procedures now have global
	linkage with hidden visibility and are no longer optimized away.
	* gfortran.dg/public_private_module_2.f90: Add xfail markers to
	scan-assembler-not for 'two' and 'six'; update comment to mention
	procedures alongside variables.
	* gfortran.dg/public_private_module_7.f90: Add xfail marker to
	scan-assembler-not for '__m_common_attrs_MOD_other'.
	* gfortran.dg/public_private_module_8.f90: Add xfail marker to
	scan-assembler-not for '__m_MOD_myotherlen'.
	* gfortran.dg/submodule_private_host.f90: New test.
	* gfortran.dg/submodule_private_host_aux.f90: New auxiliary file.
	* gfortran.dg/warn_unused_function_2.f90: Remove 'defined but not
	used' expectation for s1; PRIVATE module procedures now have
	global linkage and no longer trigger the unused-function warning.

Assisted by: Claude Sonnet 4.6
---
0001-fortran-module-contained-PRIVATE-procedures-must-hav.patch (text/x-patch, 9 KB)
From 1a42c92126a0414079f69e0e5d4d974f2ffc4b9d Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Fri, 22 May 2026 21:56:34 -0700
Subject: [PATCH] fortran: module-contained PRIVATE procedures must have global
 ELF linkage [PR125430]

gcc/fortran/ChangeLog:

	PR fortran/125430
	* trans-decl.cc (build_function_decl): Set TREE_PUBLIC for all
	module-contained procedures so submodules compiled as separate
	translation units can reach them via host association.  Also set
	DECL_VISIBILITY to VISIBILITY_HIDDEN for PRIVATE procedures,
	matching the existing treatment of module variables.

gcc/testsuite/ChangeLog:

	PR fortran/125430
	* gfortran.dg/module_private_2.f90: Remove scan-tree-dump-times
	assertion for 'priv'; PRIVATE module procedures now have global
	linkage with hidden visibility and are no longer optimized away.
	* gfortran.dg/public_private_module_2.f90: Add xfail markers to
	scan-assembler-not for 'two' and 'six'; update comment to mention
	procedures alongside variables.
	* gfortran.dg/public_private_module_7.f90: Add xfail marker to
	scan-assembler-not for '__m_common_attrs_MOD_other'.
	* gfortran.dg/public_private_module_8.f90: Add xfail marker to
	scan-assembler-not for '__m_MOD_myotherlen'.
	* gfortran.dg/submodule_private_host.f90: New test.
	* gfortran.dg/submodule_private_host_aux.f90: New auxiliary file.
	* gfortran.dg/warn_unused_function_2.f90: Remove 'defined but not
	used' expectation for s1; PRIVATE module procedures now have
	global linkage and no longer trigger the unused-function warning.

Assisted by: Claude Sonnet 4.6
---
 gcc/fortran/trans-decl.cc                     | 24 +++++++++++++--
 .../gfortran.dg/module_private_2.f90          |  3 +-
 .../gfortran.dg/public_private_module_2.f90   |  8 +++--
 .../gfortran.dg/public_private_module_7.f90   |  2 +-
 .../gfortran.dg/public_private_module_8.f90   |  2 +-
 .../gfortran.dg/submodule_private_host.f90    | 29 +++++++++++++++++++
 .../submodule_private_host_aux.f90            | 17 +++++++++++
 .../gfortran.dg/warn_unused_function_2.f90    |  2 +-
 8 files changed, 78 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/submodule_private_host.f90
 create mode 100644 gcc/testsuite/gfortran.dg/submodule_private_host_aux.f90

diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
index 1bcbfdfd2c9..969fe75b946 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -2575,11 +2575,31 @@ build_function_decl (gfc_symbol * sym, bool global)
 	      && flag_module_private)))
     sym->attr.access = ACCESS_PRIVATE;
 
+  /* A procedure contained directly in a module must be globally addressable
+     even when its Fortran access is PRIVATE: a submodule compiled as a
+     separate translation unit can reach it via host association.  The
+     Fortran PRIVATE attribute restricts USE-association visibility, not
+     ELF symbol linkage.  */
+  bool in_module_contains = sym->module && sym->ns->proc_name
+			     && sym->ns->proc_name->attr.flavor == FL_MODULE;
+
   if (!current_function_decl
       && !sym->attr.entry_master && !sym->attr.is_main_program
       && (sym->attr.access != ACCESS_PRIVATE || sym->binding_label
-	  || sym->attr.public_used))
-    TREE_PUBLIC (fndecl) = 1;
+	  || sym->attr.public_used || in_module_contains))
+    {
+      TREE_PUBLIC (fndecl) = 1;
+      /* Mirror the variable treatment (see gfc_finish_var_decl): PRIVATE
+	 module procedures get global linkage but hidden visibility so the
+	 symbol is reachable from submodules in the same link without being
+	 exported to external DSOs.  */
+      if (in_module_contains && sym->attr.access == ACCESS_PRIVATE
+	  && !sym->attr.public_used)
+	{
+	  DECL_VISIBILITY (fndecl) = VISIBILITY_HIDDEN;
+	  DECL_VISIBILITY_SPECIFIED (fndecl) = true;
+	}
+    }
 
   if (sym->attr.referenced || sym->attr.entry_master)
     TREE_USED (fndecl) = 1;
diff --git a/gcc/testsuite/gfortran.dg/module_private_2.f90 b/gcc/testsuite/gfortran.dg/module_private_2.f90
index 58dbb1e23fe..926da364182 100644
--- a/gcc/testsuite/gfortran.dg/module_private_2.f90
+++ b/gcc/testsuite/gfortran.dg/module_private_2.f90
@@ -29,6 +29,7 @@ contains
     b => export1
   end subroutine pub
 end module m
-! { dg-final { scan-tree-dump-times "priv" 0 "optimized" } }
+! priv now has TREE_PUBLIC (VISIBILITY_HIDDEN) for submodule host-association,
+! so it is no longer optimized away.
 ! { dg-final { scan-tree-dump-times "export1 \\(\\)" 1 "optimized" } }
 ! { dg-final { scan-tree-dump-times "export2 \\(\\)" 1 "optimized" } }
diff --git a/gcc/testsuite/gfortran.dg/public_private_module_2.f90 b/gcc/testsuite/gfortran.dg/public_private_module_2.f90
index 87276ccdfd1..e37cfbd57cc 100644
--- a/gcc/testsuite/gfortran.dg/public_private_module_2.f90
+++ b/gcc/testsuite/gfortran.dg/public_private_module_2.f90
@@ -19,8 +19,10 @@ module mod
   integer, bind(C,name='') :: qq
 end module mod
 
-! The two xfails below have appeared with the introduction of submodules. 'iii' and
+! The xfails below have appeared with the introduction of submodules. 'iii' and
 ! 'mmm' now are TREE_PUBLIC but has DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN set.
+! Similarly 'two' and 'six' (PRIVATE procedures) now have TREE_PUBLIC +
+! VISIBILITY_HIDDEN so that submodules can reach them via host association.
 
       ! { dg-final { scan-assembler "__mod_MOD_aa" } }
       ! { dg-final { scan-assembler-not "iii" { xfail *-*-* } } }
@@ -64,11 +66,11 @@ CONTAINS
 END MODULE
 
 ! { dg-final { scan-assembler "__m_MOD_one" } }
-! { dg-final { scan-assembler-not "two" } }
+! { dg-final { scan-assembler-not "two" { xfail *-*-* } } }
 ! { dg-final { scan-assembler "three" } }
 ! { dg-final { scan-assembler-not "four" } }
 ! { dg-final { scan-assembler "five" } }
-! { dg-final { scan-assembler-not "six" } }
+! { dg-final { scan-assembler-not "six" { xfail *-*-* } } }
 ! { dg-final { scan-assembler "seven" } }
 ! { dg-final { scan-assembler "nine" } }
 ! { dg-final { scan-assembler "__m_MOD_ten" } }
diff --git a/gcc/testsuite/gfortran.dg/public_private_module_7.f90 b/gcc/testsuite/gfortran.dg/public_private_module_7.f90
index d03b7047a12..4b62d23dc6f 100644
--- a/gcc/testsuite/gfortran.dg/public_private_module_7.f90
+++ b/gcc/testsuite/gfortran.dg/public_private_module_7.f90
@@ -25,5 +25,5 @@ contains
   end function get_key
 end module m_common_attrs
 
-! { dg-final { scan-assembler-not "__m_common_attrs_MOD_other" } }
+! { dg-final { scan-assembler-not "__m_common_attrs_MOD_other" { xfail *-*-* } } }
 ! { dg-final { scan-assembler "__m_common_attrs_MOD_get_key_len" } }
diff --git a/gcc/testsuite/gfortran.dg/public_private_module_8.f90 b/gcc/testsuite/gfortran.dg/public_private_module_8.f90
index bfc1b368f46..addf93440ab 100644
--- a/gcc/testsuite/gfortran.dg/public_private_module_8.f90
+++ b/gcc/testsuite/gfortran.dg/public_private_module_8.f90
@@ -44,6 +44,6 @@ contains
   end subroutine bar
 end module m
 
-! { dg-final { scan-assembler-not "__m_MOD_myotherlen" } }
+! { dg-final { scan-assembler-not "__m_MOD_myotherlen" { xfail *-*-* } } }
 ! { dg-final { scan-assembler "__m_MOD_bar" } }
 ! { dg-final { scan-assembler "__m_MOD_mylen" } }
diff --git a/gcc/testsuite/gfortran.dg/submodule_private_host.f90 b/gcc/testsuite/gfortran.dg/submodule_private_host.f90
new file mode 100644
index 00000000000..e50faaad111
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/submodule_private_host.f90
@@ -0,0 +1,29 @@
+! { dg-do run }
+! { dg-additional-sources submodule_private_host_aux.f90 }
+!
+! PR fortran/125430
+!
+! Module-contained procedures with PRIVATE access must retain global ELF
+! linkage so that submodules compiled as separate translation units can
+! reach them via host association.
+
+module m
+  implicit none
+  private
+  public :: pub
+
+  interface
+    module subroutine pub(x)
+      double precision, intent(out) :: x
+    end subroutine
+  end interface
+
+contains
+
+  pure function priv(n) result(x)
+    integer, intent(in) :: n
+    double precision :: x
+    x = dble(n) * 0.5d0
+  end function
+
+end module
diff --git a/gcc/testsuite/gfortran.dg/submodule_private_host_aux.f90 b/gcc/testsuite/gfortran.dg/submodule_private_host_aux.f90
new file mode 100644
index 00000000000..e237cf2650c
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/submodule_private_host_aux.f90
@@ -0,0 +1,17 @@
+! Auxiliary file for submodule_private_host.f90
+! { dg-skip-if "" { *-*-* } }
+submodule(m) ms
+  implicit none
+contains
+  module procedure pub
+    x = priv(6)
+  end procedure
+end submodule
+
+program p
+  use m, only : pub
+  implicit none
+  double precision x
+  call pub(x)
+  if (abs(x - 3.0d0) > 1d-10) stop 1
+end program
diff --git a/gcc/testsuite/gfortran.dg/warn_unused_function_2.f90 b/gcc/testsuite/gfortran.dg/warn_unused_function_2.f90
index 43b211e57bf..894a343b75c 100644
--- a/gcc/testsuite/gfortran.dg/warn_unused_function_2.f90
+++ b/gcc/testsuite/gfortran.dg/warn_unused_function_2.f90
@@ -13,7 +13,7 @@ module m
 
 contains
 
-  subroutine s1            ! { dg-warning "defined but not used" }
+  subroutine s1
     call s2(s3)
     contains
       subroutine s4        ! { dg-warning "defined but not used" }
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.