https://gcc.gnu.org/g:3872da84760f827365fc0f8d1fdc45ca25810f89
commit r17-1405-g3872da84760f827365fc0f8d1fdc45ca25810f89 Author: Paul Thomas <[email protected]> Date: Sun Jun 7 15:11:20 2026 +0100 Fortran: require MODULE prefix for separate module procedures [PR121379] set_syms_host_assoc clears the 'external' attribute on module procedures when making them accessible via host association in a submodule. The existing check in get_proc_name for a missing MODULE prefix used 'sym->attr.external' as a guard, so the diagnostic was silently suppressed and gfortran accepted — and incorrectly linked — a bare function definition as if it were a valid separate module procedure. Replace 'sym->attr.external' with 'sym->attr.module_procedure', which is not cleared by set_syms_host_assoc, to restore the intended diagnostic. Also update the error message to be clearer about what is required. Both errors have been corrected to avoid returning any value, thereby avoiding an error cascade. After all, these are OK for further processing and argument mismatches etc. can be caught. Additionally, a number of existing testcases contained this error. In order to avoid masking the intended fix, this has been corrected in all cases. 2026-06-07 Paul Thomas <[email protected]> gcc/fortran PR fortran/121379 * decl.cc (get_proc_name): Use module_procedure attr. instead of external to guard the missing-MODULE-prefix diagnostic. Use gfc_error_now and do not return, so that an error cascade does not occur. gcc/testsuite/ChangeLog PR fortran/121379 * gfortran.dg/pdt_59.f03: Add module attribute as required. * gfortran.dg/pr87907.f90: Ditto. * gfortran.dg/pr93461.f90: Ditto. * gfortran.dg/submodule_10.f08: Ditto. * gfortran.dg/submodule_33.f08: Ditto. * gfortran.dg/submodule_36.f90: New test. * gfortran.dg/submodule_37.f90: Add module attribute. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Diff: --- gcc/fortran/decl.cc | 26 ++++++++++++++------ gcc/testsuite/gfortran.dg/pdt_59.f03 | 2 +- gcc/testsuite/gfortran.dg/pr87907.f90 | 2 +- gcc/testsuite/gfortran.dg/pr93461.f90 | 2 +- gcc/testsuite/gfortran.dg/submodule_10.f08 | 14 +++++------ gcc/testsuite/gfortran.dg/submodule_33.f08 | 2 +- gcc/testsuite/gfortran.dg/submodule_36.f90 | 39 ++++++++++++++++++++++++++++++ gcc/testsuite/gfortran.dg/submodule_37.f90 | 2 +- 8 files changed, 70 insertions(+), 19 deletions(-) diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc index fe4aa8119b90..722ce35f20c2 100644 --- a/gcc/fortran/decl.cc +++ b/gcc/fortran/decl.cc @@ -1463,7 +1463,7 @@ get_proc_name (const char *name, gfc_symbol **result, bool module_fcn_entry) } } - /* C1246 (R1225) MODULE shall appear only in the function-stmt or + /* F2023: C1247 (R1526) MODULE shall appear only in the function-stmt or subroutine-stmt of a module subprogram or of a nonabstract interface body that is declared in the scoping unit of a module or submodule. */ if (sym->attr.external @@ -1472,12 +1472,24 @@ get_proc_name (const char *name, gfc_symbol **result, bool module_fcn_entry) && !current_attr.module_procedure && sym->attr.proc == PROC_MODULE && gfc_state_stack->state == COMP_CONTAINS) - { - gfc_error_now ("Procedure %qs defined in interface body at %L " - "clashes with internal procedure defined at %C", - name, &sym->declared_at); - return true; - } + gfc_error_now ("Procedure %qs defined in interface body at %L " + "clashes with internal procedure defined at %C", + name, &sym->declared_at); + + /* This is the converse requirement: The separate-module-subprogram for a + module procedure shall have the MODULE prefix or be declared a MODULE + PROCEDURE, otherwise it would be ambiguous. */ + if (sym->attr.module_procedure + && (sym->attr.subroutine || sym->attr.function) + && sym->attr.if_source == IFSRC_IFBODY + && !current_attr.module_procedure + && sym->attr.proc == PROC_MODULE + && gfc_state_stack->state == COMP_CONTAINS + && gfc_state_stack->previous + && gfc_state_stack->previous->state == COMP_SUBMODULE) + gfc_error_now ("Procedure %qs at %C requires the MODULE prefix because " + "it is a module procedure declared in module %qs", + name, sym->module ? sym->module : ""); if (sym && !sym->gfc_new && sym->attr.flavor != FL_UNKNOWN diff --git a/gcc/testsuite/gfortran.dg/pdt_59.f03 b/gcc/testsuite/gfortran.dg/pdt_59.f03 index 7367897c8e72..09ca99ae3753 100644 --- a/gcc/testsuite/gfortran.dg/pdt_59.f03 +++ b/gcc/testsuite/gfortran.dg/pdt_59.f03 @@ -30,7 +30,7 @@ end module submodule(input_output_pair_m) input_output_pair_smod contains - function default_real_construct() + module function default_real_construct() type(mini_batch_t) default_real_construct allocate (default_real_construct%input_output_pairs_(2)) default_real_construct%input_output_pairs_%a = [42,43] diff --git a/gcc/testsuite/gfortran.dg/pr87907.f90 b/gcc/testsuite/gfortran.dg/pr87907.f90 index 5c2acaf9b7fd..2a99aa7fd6e3 100644 --- a/gcc/testsuite/gfortran.dg/pr87907.f90 +++ b/gcc/testsuite/gfortran.dg/pr87907.f90 @@ -12,7 +12,7 @@ end submodule(m) m2 contains - subroutine g(x) ! { dg-error "FUNCTION attribute conflicts with SUBROUTINE" } + module subroutine g(x) ! { dg-error "FUNCTION attribute conflicts with SUBROUTINE" } end end diff --git a/gcc/testsuite/gfortran.dg/pr93461.f90 b/gcc/testsuite/gfortran.dg/pr93461.f90 index 3bef326172fb..d6b7b424aec0 100644 --- a/gcc/testsuite/gfortran.dg/pr93461.f90 +++ b/gcc/testsuite/gfortran.dg/pr93461.f90 @@ -9,7 +9,7 @@ end module aModuleWithAnAllowedName submodule (aModuleWithAnAllowedName) aSubmoduleWithAVeryVeryVeryLongButEntirelyLegalName contains - subroutine aShortName() + module subroutine aShortName() call aSubroutineWithAVeryLongNameThatWillCauseAProblem() call aSubroutineWithAVeryLongNameThatWillCauseAProblemAlso() end subroutine aShortName diff --git a/gcc/testsuite/gfortran.dg/submodule_10.f08 b/gcc/testsuite/gfortran.dg/submodule_10.f08 index 373b11c9f969..d9968e9b147a 100644 --- a/gcc/testsuite/gfortran.dg/submodule_10.f08 +++ b/gcc/testsuite/gfortran.dg/submodule_10.f08 @@ -99,14 +99,14 @@ submodule (error_mod) error_impl_mod use const_mod contains ! checks whether an error has occurred on one of the processes in the execution pool - subroutine errcomm(ictxt, err) + module subroutine errcomm(ictxt, err) integer(mpik_), intent(in) :: ictxt integer(ipk_), intent(inout):: err end subroutine errcomm - subroutine ser_error_handler(err_act) + module subroutine ser_error_handler(err_act) implicit none integer(ipk_), intent(inout) :: err_act @@ -117,7 +117,7 @@ contains return end subroutine ser_error_handler - subroutine par_error_handler(ictxt,err_act) + module subroutine par_error_handler(ictxt,err_act) implicit none integer(mpik_), intent(in) :: ictxt integer(ipk_), intent(in) :: err_act @@ -131,25 +131,25 @@ contains end subroutine par_error_handler - subroutine par_error_print_stack(ictxt) + module subroutine par_error_print_stack(ictxt) integer(mpik_), intent(in) :: ictxt call error(ictxt, abrt=.false.) end subroutine par_error_print_stack - subroutine ser_error_print_stack() + module subroutine ser_error_print_stack() call error() end subroutine ser_error_print_stack - subroutine serror() + module subroutine serror() implicit none end subroutine serror - subroutine perror(ictxt,abrt) + module subroutine perror(ictxt,abrt) use const_mod implicit none integer(mpik_), intent(in) :: ictxt diff --git a/gcc/testsuite/gfortran.dg/submodule_33.f08 b/gcc/testsuite/gfortran.dg/submodule_33.f08 index b61d750def16..85c29183ac11 100644 --- a/gcc/testsuite/gfortran.dg/submodule_33.f08 +++ b/gcc/testsuite/gfortran.dg/submodule_33.f08 @@ -14,7 +14,7 @@ module m end submodule(m) m2 contains - subroutine g(x) ! { dg-error "FUNCTION attribute conflicts with SUBROUTINE" } + module subroutine g(x) ! { dg-error "FUNCTION attribute conflicts with SUBROUTINE" } integer, intent(in) :: x ! { dg-error "Unexpected data declaration" } end end diff --git a/gcc/testsuite/gfortran.dg/submodule_36.f90 b/gcc/testsuite/gfortran.dg/submodule_36.f90 new file mode 100644 index 000000000000..a2b03f84f7f2 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/submodule_36.f90 @@ -0,0 +1,39 @@ +! { dg-do compile } +! +! Test the fix for pr121379, in which the missing MODULE prefix for 'realg' was +! not detected. +! +! Contributed by Paul Thomas <[email protected]> +! +module m + interface g + real module function realg1 (arg1, arg2) + real, intent(in) :: arg1, arg2 + end + end interface + + interface h + real module function realg2 (arg1, arg2) + real, intent(in) :: arg1, arg2 + end + end interface + +contains +end module m + +submodule (m) subm +contains + real module function realg1 (arg1, arg2) + real, intent(in) :: arg1, arg2 + realg1 = arg1 + arg2 + end + + real function realg2 (arg1, arg2) ! { dg-error "requires the MODULE prefix" } + real, intent(in) :: arg1, arg2 + realg2 = arg1 + arg2 + end +end + + use m + print *, g(1.0, 1.0), h(2.0, 2.0) +end diff --git a/gcc/testsuite/gfortran.dg/submodule_37.f90 b/gcc/testsuite/gfortran.dg/submodule_37.f90 index 2e8c01c94531..3bfde9ab67b6 100644 --- a/gcc/testsuite/gfortran.dg/submodule_37.f90 +++ b/gcc/testsuite/gfortran.dg/submodule_37.f90 @@ -37,7 +37,7 @@ end module t submodule (t) ts contains - function bp(s) + module function bp(s) class(b), intent(inout) :: s class(c), pointer :: bp select type (s)
