From ce7199b16872b3014be68744329a8f19ddd64b05 Mon Sep 17 00:00:00 2001
From: Harald Anlauf <anlauf@gmx.de>
Date: Thu, 29 Feb 2024 21:43:53 +0100
Subject: [PATCH] Fortran: improve checks of NULL without MOLD as actual
 argument [PR104819]

gcc/fortran/ChangeLog:

	PR fortran/104819
	* check.cc (gfc_check_null): Handle nested NULL()s.
	(is_c_interoperable): Check for MOLD argument of NULL() as part of
	the interoperability check.
	* interface.cc (gfc_compare_actual_formal): Extend checks for NULL()
	actual arguments for presence of MOLD argument when required by
	Interp J3/22-146.

gcc/testsuite/ChangeLog:

	PR fortran/104819
	* gfortran.dg/pr101329.f90: Adjust testcase to conform to interp.
	* gfortran.dg/null_actual_4.f90: New test.
---
 gcc/fortran/check.cc                        |  5 ++-
 gcc/fortran/interface.cc                    | 30 ++++++++++++++++++
 gcc/testsuite/gfortran.dg/null_actual_4.f90 | 35 +++++++++++++++++++++
 gcc/testsuite/gfortran.dg/pr101329.f90      |  4 +--
 4 files changed, 71 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/null_actual_4.f90

diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc
index d661cf37f01..db74dcf3f40 100644
--- a/gcc/fortran/check.cc
+++ b/gcc/fortran/check.cc
@@ -4384,6 +4384,9 @@ gfc_check_null (gfc_expr *mold)
   if (mold == NULL)
     return true;

+  if (mold->expr_type == EXPR_NULL)
+    return true;
+
   if (!variable_check (mold, 0, true))
     return false;

@@ -5216,7 +5219,7 @@ is_c_interoperable (gfc_expr *expr, const char **msg, bool c_loc, bool c_f_ptr)
 {
   *msg = NULL;

-  if (expr->expr_type == EXPR_NULL)
+  if (expr->expr_type == EXPR_NULL && expr->ts.type == BT_UNKNOWN)
     {
       *msg = "NULL() is not interoperable";
       return false;
diff --git a/gcc/fortran/interface.cc b/gcc/fortran/interface.cc
index 231f2f252af..64b90550be2 100644
--- a/gcc/fortran/interface.cc
+++ b/gcc/fortran/interface.cc
@@ -3296,6 +3296,36 @@ gfc_compare_actual_formal (gfc_actual_arglist **ap, gfc_formal_arglist *formal,
 	  && a->expr->ts.type != BT_ASSUMED)
 	gfc_find_vtab (&a->expr->ts);

+      /* Interp J3/22-146:
+	 "If the context of the reference to NULL is an <actual argument>
+	 corresponding to an <assumed-rank> dummy argument, MOLD shall be
+	 present."  */
+      if (a->expr->expr_type == EXPR_NULL
+	  && a->expr->ts.type == BT_UNKNOWN
+	  && f->sym->as
+	  && f->sym->as->type == AS_ASSUMED_RANK)
+	{
+	  gfc_error ("Intrinsic %<NULL()%> without %<MOLD%> argument at %L "
+		     "passed to assumed-rank dummy %qs",
+		     &a->expr->where, f->sym->name);
+	  ok = false;
+	  goto match;
+	}
+
+      if (a->expr->expr_type == EXPR_NULL
+	  && a->expr->ts.type == BT_UNKNOWN
+	  && f->sym->ts.type == BT_CHARACTER
+	  && !f->sym->ts.deferred
+	  && f->sym->ts.u.cl
+	  && f->sym->ts.u.cl->length == NULL)
+	{
+	  gfc_error ("Intrinsic %<NULL()%> without %<MOLD%> argument at %L "
+		     "passed to assumed-length dummy %qs",
+		     &a->expr->where, f->sym->name);
+	  ok = false;
+	  goto match;
+	}
+
       if (a->expr->expr_type == EXPR_NULL
 	  && ((f->sym->ts.type != BT_CLASS && !f->sym->attr.pointer
 	       && (f->sym->attr.allocatable || !f->sym->attr.optional
diff --git a/gcc/testsuite/gfortran.dg/null_actual_4.f90 b/gcc/testsuite/gfortran.dg/null_actual_4.f90
new file mode 100644
index 00000000000..e03d5c8f7de
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/null_actual_4.f90
@@ -0,0 +1,35 @@
+! { dg-do compile }
+! PR fortran/104819
+!
+! Reject NULL without MOLD as actual to an assumed-rank dummy.
+! See also interpretation request at
+! https://j3-fortran.org/doc/year/22/22-101r1.txt
+!
+! Test nested NULL()
+
+program p
+  implicit none
+  integer, pointer :: a, a3(:,:,:)
+  character(10), pointer :: c
+
+  call foo (a)
+  call foo (a3)
+  call foo (null (a))
+  call foo (null (a3))
+  call foo (null (null (a)))  ! Valid: nested NULL()s
+  call foo (null (null (a3))) ! Valid: nested NULL()s
+  call foo (null ())          ! { dg-error "passed to assumed-rank dummy" }
+
+  call str (null (c))
+  call str (null (null (c)))
+  call str (null ())          ! { dg-error "passed to assumed-length dummy" }
+contains
+  subroutine foo (x)
+    integer, pointer, intent(in) :: x(..)
+    print *, rank (x)
+  end
+
+  subroutine str (x)
+    character(len=*), pointer, intent(in) :: x
+  end
+end
diff --git a/gcc/testsuite/gfortran.dg/pr101329.f90 b/gcc/testsuite/gfortran.dg/pr101329.f90
index b82210d4e28..aca171bd4f8 100644
--- a/gcc/testsuite/gfortran.dg/pr101329.f90
+++ b/gcc/testsuite/gfortran.dg/pr101329.f90
@@ -8,6 +8,6 @@ program p
   integer(c_int64_t), pointer :: ip8
   print *, c_sizeof (c_null_ptr) ! valid
   print *, c_sizeof (null ())    ! { dg-error "is not interoperable" }
-  print *, c_sizeof (null (ip4)) ! { dg-error "is not interoperable" }
-  print *, c_sizeof (null (ip8)) ! { dg-error "is not interoperable" }
+  print *, c_sizeof (null (ip4)) ! valid
+  print *, c_sizeof (null (ip8)) ! valid
 end
--
2.35.3

