[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