See attached patch. Passing NULL() to an allocatable dummy of intrinsic
type ICEs on a gcc_assert in gfc_conv_procedure_call, which expects a
NULL() actual there only for an optional, non-allocatable dummy. Only
derived-type dummies were sent to conv_null_actual. The patch sends
non-class, non-character allocatable dummies there as well, and
conv_null_actual builds the null descriptor for them with
gfc_create_null_actual_descriptor, as it already does for derived types.
CLASS and CHARACTER allocatable dummies are left as they are.

Start with the condition in gfc_conv_procedure_call that selects
conv_null_actual, then the widened derived-type branch of
conv_null_actual, both in trans-expr.cc; the rest is the new test. The
bug was found with the Fujitsu compiler test suite; the test is new.

Regression tested on x86_64.

OK for mainline?

Regards,

Jerry

---

fortran: [PR125724] ICE passing NULL() to an allocatable dummy argument

Pass a NULL() actual argument for a non-class, non-character
allocatable dummy through conv_null_actual, as for derived-type
dummies.

Assisted-by: Claude Opus 5.5

        PR fortran/125724

gcc/fortran/ChangeLog:

        * trans-expr.cc (conv_null_actual): Handle intrinsic allocatable
        and pointer dummies.
        (gfc_conv_procedure_call): Use conv_null_actual for a NULL()
        actual with a non-class, non-character allocatable dummy.

gcc/testsuite/ChangeLog:

        * gfortran.dg/null_actual_8.f90: New test.
>From 2115f48b3cd59e7d12d17d06e8b641c2cc369289 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Wed, 10 Jun 2026 08:37:01 -0700
Subject: [PATCH] fortran: [PR125724] ICE passing NULL() to an allocatable
 dummy argument

Pass a NULL() actual argument for a non-class, non-character
allocatable dummy through conv_null_actual, as for derived-type
dummies.

Assisted-by: Claude Opus 5.5

	PR fortran/125724

gcc/fortran/ChangeLog:

	* trans-expr.cc (conv_null_actual): Handle intrinsic allocatable
	and pointer dummies.
	(gfc_conv_procedure_call): Use conv_null_actual for a NULL()
	actual with a non-class, non-character allocatable dummy.

gcc/testsuite/ChangeLog:

	* gfortran.dg/null_actual_8.f90: New test.
---
 gcc/fortran/trans-expr.cc                   | 10 ++++--
 gcc/testsuite/gfortran.dg/null_actual_8.f90 | 38 +++++++++++++++++++++
 2 files changed, 45 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/null_actual_8.f90

diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 8ebaba045ad..10ca0995ec0 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -7068,7 +7068,9 @@ conv_null_actual (gfc_se * parmse, gfc_expr * e, gfc_symbol * fsym)
 	    }
 	}
     }
-  else if (fsym->ts.type == BT_DERIVED)
+  else if (fsym->ts.type == BT_DERIVED
+	   || ((fsym->attr.allocatable || fsym->attr.pointer)
+	       && fsym->ts.type != BT_CLASS))
     {
       if (e->ts.type != BT_UNKNOWN)
 	/* MOLD is present.  Pass a corresponding temporary NULL pointer.
@@ -7407,9 +7409,11 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 	    }
 	}
       else if (e->expr_type == EXPR_NULL
-	       && (e->ts.type == BT_UNKNOWN || e->ts.type == BT_DERIVED)
 	       && fsym && attr && (attr->pointer || attr->allocatable)
-	       && fsym->ts.type == BT_DERIVED)
+	       && ((fsym->ts.type == BT_DERIVED
+		    && (e->ts.type == BT_UNKNOWN || e->ts.type == BT_DERIVED))
+		   || (fsym->attr.allocatable && fsym->ts.type != BT_CLASS
+		       && fsym->ts.type != BT_CHARACTER)))
 	{
 	  gfc_init_se (&parmse, NULL);
 	  gfc_conv_expr_reference (&parmse, e);
diff --git a/gcc/testsuite/gfortran.dg/null_actual_8.f90 b/gcc/testsuite/gfortran.dg/null_actual_8.f90
new file mode 100644
index 00000000000..d07a14c92ff
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/null_actual_8.f90
@@ -0,0 +1,38 @@
+! { dg-do run }
+!
+! PR 125724
+!
+! Passing NULL() as an actual argument to an allocatable dummy used to ICE in
+! gfc_conv_procedure_call (a gcc_assert that the dummy was optional and not
+! allocatable).  The dummy must be received as an unallocated allocatable.
+
+program p
+  ! Scalar allocatable, INTENT(IN).
+  call s_scalar (null ())
+
+  ! Allocatable array: unallocated on entry, then usable.
+  call s_array (null ())
+
+  ! Optional allocatable: NULL() is present-but-unallocated.
+  call s_opt (null ())
+contains
+  subroutine s_scalar (a)
+    integer, allocatable, intent(in) :: a
+    if (allocated (a)) stop 1
+  end subroutine
+
+  subroutine s_array (a)
+    integer, allocatable :: a(:)
+    if (allocated (a)) stop 2
+    allocate (a(3))
+    a = [1, 2, 3]
+    if (any (a /= [1, 2, 3])) stop 3
+    deallocate (a)
+  end subroutine
+
+  subroutine s_opt (a)
+    integer, allocatable, optional :: a(:)
+    if (.not. present (a)) stop 4
+    if (allocated (a)) stop 5
+  end subroutine
+end program p
-- 
2.56.0

Reply via email to