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