Hello All,

This is the penultimate submodule fix. It is actually straightforward.
However, the version produced by Claude produced error cascades and
modified the existing testcase to check for the error. The latter is
unacceptable because it hides the result of the original fix in all
cases. Instead, I have corrected the error and added the module
prefix, where appropriate.

The attached says the rest. It passes regression testing on FC44/x86_64.

OK for mainline and eventual backporting to gcc-16?

Cheers

Paul
From 0de976d38c58e81210b12c2bd46823a55d3e9d39 Mon Sep 17 00:00:00 2001
From: Paul Thomas <[email protected]>
Date: Fri, 5 Jun 2026 15:11:20 +0100
Subject: [PATCH] Fortran: require MODULE prefix for separate module procedures
 [PR121379]
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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-05  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]>
---
 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(-)
 create mode 100644 gcc/testsuite/gfortran.dg/submodule_36.f90

diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index fe4aa8119b9..722ce35f20c 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 7367897c8e7..09ca99ae375 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 5c2acaf9b7f..2a99aa7fd6e 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 3bef326172f..d6b7b424aec 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 373b11c9f96..d9968e9b147 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 b61d750def1..85c29183ac1 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 00000000000..a2b03f84f7f
--- /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 2e8c01c9453..3bfde9ab67b 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)
-- 
2.54.0

Reply via email to