Dear all,

the attached almost obvious patch fixes actually two issues:
- when C_FUNLOC is applied to a procedure pointer instead of
  a procedure, and the procedure pointer is a dummy, we need
  to dereference it;
- a procedure pointer dummy with intent(out) was clobbered,
  but the clobber was using the dereferenced pointer, which
  did fail in gimple at -O1 and higher (and is actually
  detecting wrong code).
  The solution is to   not clobber (and we have a couple of
  similar exceptions).

Regtested on x86_64-pc-linux-gnu.  OK for mainline?

As the above issues can be naughty, is it OK to backport to 15-branch?

Thanks,
Harald

From b76f5b9108d8ac1dfaa54817d42fae292451ff02 Mon Sep 17 00:00:00 2001
From: Harald Anlauf <[email protected]>
Date: Wed, 1 Apr 2026 22:28:02 +0200
Subject: [PATCH] Fortran: fix passing a procedure pointer to c_funloc
 [PR124652]

	PR fortran/124652

gcc/fortran/ChangeLog:

	* trans-expr.cc (gfc_conv_procedure_call): Do not clobber a
	procedure pointer intent(out) argument.
	* trans-intrinsic.cc (conv_isocbinding_function): When passing to
	C_FUNLOC a procedure pointer that is a dummy, dereference it.

gcc/testsuite/ChangeLog:

	* gfortran.dg/c_funloc_tests_10.f90: New test.
---
 gcc/fortran/trans-expr.cc                     |  1 +
 gcc/fortran/trans-intrinsic.cc                |  3 +
 .../gfortran.dg/c_funloc_tests_10.f90         | 86 +++++++++++++++++++
 3 files changed, 90 insertions(+)
 create mode 100644 gcc/testsuite/gfortran.dg/c_funloc_tests_10.f90

diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 52918961584..3945d9eaa67 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -7549,6 +7549,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 				  && !e->ts.u.derived->attr.alloc_comp
 				  && !e->ts.u.derived->attr.pdt_type
 				  && !gfc_is_finalizable (e->ts.u.derived, NULL)))
+			  && e->ts.type != BT_PROCEDURE
 			  && !sym->attr.elemental)
 			{
 			  tree var;
diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index 578851e1b0b..dbf645886f5 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -9901,6 +9901,9 @@ conv_isocbinding_function (gfc_se *se, gfc_expr *expr)
   else if (expr->value.function.isym->id == GFC_ISYM_C_FUNLOC)
     {
       gfc_conv_expr_reference (se, arg->expr);
+      if (arg->expr->symtree->n.sym->attr.proc_pointer
+	  && arg->expr->symtree->n.sym->attr.dummy)
+	se->expr = build_fold_indirect_ref_loc (input_location, se->expr);
       /* The code below is necessary to create a reference from the calling
 	 subprogram to the argument of C_FUNLOC() in the call graph.
 	 Please see PR 117303 for more details. */
diff --git a/gcc/testsuite/gfortran.dg/c_funloc_tests_10.f90 b/gcc/testsuite/gfortran.dg/c_funloc_tests_10.f90
new file mode 100644
index 00000000000..f320c8e3aea
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_funloc_tests_10.f90
@@ -0,0 +1,86 @@
+! { dg-do run }
+!
+! PR fortran/124652 - passing a procedure pointer to c_funloc
+!
+! Contributed by Damian Rouson
+
+program proc_ptr_demo
+  use iso_c_binding
+  implicit none
+
+  ! Define an interface for the type of procedure we are pointing to
+  abstract interface
+     function compute_interface(x) result(res) bind(c)
+       use iso_c_binding
+       real(c_float), intent(in), value :: x
+       real(c_float)                    :: res
+     end function compute_interface
+  end interface
+
+  ! Procedure pointers
+  procedure(compute_interface), pointer :: original_ptr => null()
+  procedure(compute_interface), pointer :: restored_ptr => null()
+  type(c_funptr) :: c_address
+  real(c_float)  :: expect
+
+  expect = square_it (5.0)
+  ! Point to our actual function
+  original_ptr => square_it
+  if (original_ptr (5.0) /= expect) stop 1
+
+  ! Convert pointers "inline"
+  c_address = c_funloc (square_it)
+  call c_f_procpointer(c_address, restored_ptr)
+  if (.not. associated (original_ptr, restored_ptr)) stop 2
+  if (restored_ptr (5.0) /= expect) stop 3
+
+  c_address = c_funloc (original_ptr)
+  call c_f_procpointer (c_address, restored_ptr)
+  if (.not. associated (original_ptr, restored_ptr)) stop 4
+  if (restored_ptr (5.0) /= expect) stop 5
+
+  ! Call contained subroutines to perform the C conversion logic
+  call round_trip_conversion_proc (square_it, restored_ptr)
+  if (.not. associated (original_ptr, restored_ptr)) stop 6
+  if (restored_ptr (5.0) /= expect) stop 7
+
+  call round_trip_conversion_proc (original_ptr, restored_ptr)
+  if (.not. associated (original_ptr, restored_ptr)) stop 8
+  if (restored_ptr (5.0) /= expect) stop 9
+
+  ! The following used to fail
+  call round_trip_conversion_ptr (square_it, restored_ptr)
+  if (.not. associated (original_ptr, restored_ptr)) stop 10
+  if (restored_ptr (5.0) /= expect) stop 11
+
+  call round_trip_conversion_ptr (original_ptr, restored_ptr)
+  if (.not. associated (original_ptr, restored_ptr)) stop 12
+  if (restored_ptr (5.0) /= expect) stop 13
+
+contains
+
+  subroutine round_trip_conversion_proc (proc_in, fptr_out)
+    procedure(compute_interface)                       :: proc_in
+    procedure(compute_interface), pointer, intent(out) :: fptr_out
+    type(c_funptr) :: c_address
+!   print *, proc_in(1.0)
+    c_address = c_funloc (proc_in)
+    call c_f_procpointer (c_address, fptr_out)
+  end subroutine round_trip_conversion_proc
+
+  subroutine round_trip_conversion_ptr (fptr_in, fptr_out)
+    procedure(compute_interface), pointer, intent(in)  :: fptr_in
+    procedure(compute_interface), pointer, intent(out) :: fptr_out
+    type(c_funptr) :: c_address_s
+!   print *, fptr_in(2.0)
+    c_address_s = c_funloc (fptr_in)
+    call c_f_procpointer (c_address_s, fptr_out)
+  end subroutine round_trip_conversion_ptr
+
+  function square_it (x) result(res) bind(c)
+    real(c_float), intent(in), value :: x
+    real(c_float)                    :: res
+    res = x * x
+  end function square_it
+
+end program
-- 
2.51.0

Reply via email to