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)

Reply via email to