Dear all,

this is the second (and hopefully final) patch to fix this PR for good.
This makes the GNU intrinsics STAT/LSTAT/FSTAT almost generic, with few
restrictions:

- for the VALUES argument we will support only kinds 4 and 8.
  This allows to stay with the current runtime library functions
  in libgfortran.  Other arguments will be converted suitably.

- a STATUS argument shall have a decimal exponent range of at
  least four.  This allows to handle both common errno values
  as well as potential LIBERROR_* from libgfortran.h.

The hard part was getting this optional,intent(out) STATUS
argument right when we want to allow kind conversions,
and the actual argument being an optional dummy.

In its current version, gfc_conv_procedure_call does not seem
to support such trickery, so I wrote a specialized version
handling this.  It is actually more complex than it needs
to be, as the related runtime library functions do not
change their behavior depending on the presence of the
actual argument.  We'll generate a temporary of the needed
kind, pass a suitable pointer, and assign the result only
when it should happen.

(Maybe the required feature is already mostly implemented,
but I just did not see it.)

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

Thanks,
Harald

From 37a01654fba38124b36fe37458d99f373a52d67d Mon Sep 17 00:00:00 2001
From: Harald Anlauf <anl...@gmx.de>
Date: Mon, 8 Sep 2025 21:21:15 +0200
Subject: [PATCH] Fortran: make STAT/LSTAT/FSTAT intrinsics generic [PR82480]

	PR fortran/82480

gcc/fortran/ChangeLog:

	* check.cc (error_unsupported_kind): Helper function to report an
	unsupported kind of an argument.
	(check_minrange4): Helper function to report if an integer variable
	does not have a decimal range of at least four.
	(gfc_check_fstat): Adjust checks for generalization of instrinsic
	function FSTAT.
	(gfc_check_fstat_sub): Likewise for subroutine FSTAT.
	(gfc_check_stat): Likewise for functio STAT.
	(gfc_check_stat_sub): Likewise for subroutine STAT.
	* intrinsic.texi: Document generalized versions of intrinsics
	STAT/LSTAT/FSTAT.
	* iresolve.cc (gfc_resolve_stat): STAT function result shall have
	the same kind as the VALUES argument.
	(gfc_resolve_lstat): Likewise for LSTAT.
	(gfc_resolve_fstat): Likewise for FSTAT.
	(gfc_resolve_stat_sub): Resolve proper library subroutine for STAT.
	(gfc_resolve_lstat_sub): Likewise for LSTAT.
	* trans-decl.cc (gfc_build_intrinsic_function_decls): Declare
	fndecls for required subroutines in runtine library.
	* trans-intrinsic.cc (conv_intrinsic_fstat_lstat_stat_sub): Emit
	runtime wrapper code for the library functions, taking care of
	possible kind conversion of the optional STATUS argument of the
	subroutine versions of the intrinsics.
	(gfc_conv_intrinsic_subroutine): Use it.
	* trans.h (GTY): Declare prototypes.

gcc/testsuite/ChangeLog:

	* gfortran.dg/stat_3.f90: Extend argument checking.
	* gfortran.dg/stat_4.f90: New test.
---
 gcc/fortran/check.cc                 |  52 +++++++++--
 gcc/fortran/intrinsic.texi           |  44 +++++-----
 gcc/fortran/iresolve.cc              |  22 ++---
 gcc/fortran/trans-decl.cc            |  34 ++++++++
 gcc/fortran/trans-intrinsic.cc       | 125 +++++++++++++++++++++++++++
 gcc/fortran/trans.h                  |   6 ++
 gcc/testsuite/gfortran.dg/stat_3.f90 |  13 +++
 gcc/testsuite/gfortran.dg/stat_4.f90 |  94 ++++++++++++++++++++
 8 files changed, 352 insertions(+), 38 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/stat_4.f90

diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc
index 80aac89c333..1f170131ae1 100644
--- a/gcc/fortran/check.cc
+++ b/gcc/fortran/check.cc
@@ -1107,6 +1107,36 @@ kind_value_check (gfc_expr *e, int n, int k)
 }
 
 
+/* Error message for an actual argument with an unsupported kind value.  */
+
+static void
+error_unsupported_kind (gfc_expr *e, int n)
+{
+  gfc_error ("Not supported: %qs argument of %qs intrinsic at %L with kind %d",
+	     gfc_current_intrinsic_arg[n]->name,
+	     gfc_current_intrinsic, &e->where, e->ts.kind);
+  return;
+}
+
+
+/* Check if the decimal exponent range of an integer variable is at least four
+   so that it is large enough to e.g. hold errno values and the values of
+   LIBERROR_* from libgfortran.h.  */
+
+static bool
+check_minrange4 (gfc_expr *e, int n)
+{
+  if (e->ts.kind >= 2)
+    return true;
+
+  gfc_error ("%qs argument of %qs intrinsic at %L must have "
+	     "a decimal exponent range of at least four",
+	     gfc_current_intrinsic_arg[n]->name,
+	     gfc_current_intrinsic, &e->where);
+  return false;
+}
+
+
 /* Make sure an expression is a variable.  */
 
 static bool
@@ -6574,10 +6604,15 @@ gfc_check_fstat (gfc_expr *unit, gfc_expr *values)
   if (!scalar_check (unit, 0))
     return false;
 
-  if (!type_check (values, 1, BT_INTEGER)
-      || !kind_value_check (unit, 0, gfc_default_integer_kind))
+  if (!type_check (values, 1, BT_INTEGER))
     return false;
 
+  if (values->ts.kind != 4 && values->ts.kind != 8)
+    {
+      error_unsupported_kind (values, 1);
+      return false;
+    }
+
   if (!array_check (values, 1))
     return false;
 
@@ -6601,7 +6636,7 @@ gfc_check_fstat_sub (gfc_expr *unit, gfc_expr *values, gfc_expr *status)
     return true;
 
   if (!type_check (status, 2, BT_INTEGER)
-      || !kind_value_check (status, 2, gfc_default_integer_kind))
+      || !check_minrange4 (status, 2))
     return false;
 
   if (!scalar_check (status, 2))
@@ -6654,10 +6689,15 @@ gfc_check_stat (gfc_expr *name, gfc_expr *values)
   if (!kind_value_check (name, 0, gfc_default_character_kind))
     return false;
 
-  if (!type_check (values, 1, BT_INTEGER)
-      || !kind_value_check (values, 1, gfc_default_integer_kind))
+  if (!type_check (values, 1, BT_INTEGER))
     return false;
 
+  if (values->ts.kind != 4 && values->ts.kind != 8)
+    {
+      error_unsupported_kind (values, 1);
+      return false;
+    }
+
   if (!array_check (values, 1))
     return false;
 
@@ -6681,7 +6721,7 @@ gfc_check_stat_sub (gfc_expr *name, gfc_expr *values, gfc_expr *status)
     return true;
 
   if (!type_check (status, 2, BT_INTEGER)
-      || !kind_value_check (status, 2, gfc_default_integer_kind))
+      || !check_minrange4 (status, 2))
     return false;
 
   if (!scalar_check (status, 2))
diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi
index 6b9f4cd809a..9012c2a5746 100644
--- a/gcc/fortran/intrinsic.texi
+++ b/gcc/fortran/intrinsic.texi
@@ -6992,7 +6992,7 @@ GNU extension
 @end multitable
 
 @item @emph{Description}:
-@code{FSTAT} is identical to @ref{STAT}, except that information about an 
+@code{FSTAT} is identical to @ref{STAT}, except that information about an
 already opened file is obtained.
 
 The elements in @code{VALUES} are the same as described by @ref{STAT}.
@@ -7007,9 +7007,9 @@ Subroutine, function
 @multitable @columnfractions .15 .70
 @item @var{UNIT}   @tab An open I/O unit number of type @code{INTEGER}.
 @item @var{VALUES} @tab The type shall be @code{INTEGER, DIMENSION(13)}
-of the default kind.
+of either kind 4 or kind 8.
 @item @var{STATUS} @tab (Optional) status flag of type @code{INTEGER}
-of the default kind.
+of kind 2 or larger.
 Returns 0 on success and a system specific error code otherwise.
 @end multitable
 
@@ -10314,9 +10314,9 @@ Subroutine, function
 @item @var{NAME}   @tab The type shall be @code{CHARACTER} of the default
 kind, a valid path within the file system.
 @item @var{VALUES} @tab The type shall be @code{INTEGER, DIMENSION(13)}
-of the default kind.
+of either kind 4 or kind 8.
 @item @var{STATUS} @tab (Optional) status flag of type @code{INTEGER}
-of the default kind.
+of kind 2 or larger.
 Returns 0 on success and a system specific error code otherwise.
 @end multitable
 
@@ -14440,28 +14440,28 @@ GNU extension
 @end multitable
 
 @item @emph{Description}:
-This function returns information about a file. No permissions are required on 
-the file itself, but execute (search) permission is required on all of the 
+This function returns information about a file. No permissions are required on
+the file itself, but execute (search) permission is required on all of the
 directories in path that lead to the file.
 
 The elements that are obtained and stored in the array @code{VALUES}:
 @multitable @columnfractions .15 .70
-@item @code{VALUES(1)}   @tab  Device ID 
-@item @code{VALUES(2)}   @tab  Inode number 
-@item @code{VALUES(3)}   @tab  File mode 
-@item @code{VALUES(4)}   @tab  Number of links 
-@item @code{VALUES(5)}   @tab  Owner's uid 
-@item @code{VALUES(6)}   @tab  Owner's gid 
-@item @code{VALUES(7)}   @tab  ID of device containing directory entry for file (0 if not available) 
-@item @code{VALUES(8)}   @tab  File size (bytes) 
-@item @code{VALUES(9)}   @tab  Last access time 
-@item @code{VALUES(10)}  @tab  Last modification time 
-@item @code{VALUES(11)}  @tab  Last file status change time 
-@item @code{VALUES(12)}  @tab  Preferred I/O block size (-1 if not available) 
+@item @code{VALUES(1)}   @tab  Device ID
+@item @code{VALUES(2)}   @tab  Inode number
+@item @code{VALUES(3)}   @tab  File mode
+@item @code{VALUES(4)}   @tab  Number of links
+@item @code{VALUES(5)}   @tab  Owner's uid
+@item @code{VALUES(6)}   @tab  Owner's gid
+@item @code{VALUES(7)}   @tab  ID of device containing directory entry for file (0 if not available)
+@item @code{VALUES(8)}   @tab  File size (bytes)
+@item @code{VALUES(9)}   @tab  Last access time
+@item @code{VALUES(10)}  @tab  Last modification time
+@item @code{VALUES(11)}  @tab  Last file status change time
+@item @code{VALUES(12)}  @tab  Preferred I/O block size (-1 if not available)
 @item @code{VALUES(13)}  @tab  Number of blocks allocated (-1 if not available)
 @end multitable
 
-Not all these elements are relevant on all systems. 
+Not all these elements are relevant on all systems.
 If an element is not relevant, it is returned as 0.
 If the value of an element would overflow the range of default integer,
 a -1 is returned instead.
@@ -14477,9 +14477,9 @@ Subroutine, function
 @item @var{NAME}   @tab The type shall be @code{CHARACTER}, of the
 default kind and a valid path within the file system.
 @item @var{VALUES} @tab The type shall be @code{INTEGER, DIMENSION(13)}
-of the default kind.
+of either kind 4 or kind 8.
 @item @var{STATUS} @tab (Optional) status flag of type @code{INTEGER}
-of the default kind.
+of kind 2 or larger.
 Returns 0 on success and a system specific error code otherwise.
 @end multitable
 
diff --git a/gcc/fortran/iresolve.cc b/gcc/fortran/iresolve.cc
index da354ab5056..a821332ecb2 100644
--- a/gcc/fortran/iresolve.cc
+++ b/gcc/fortran/iresolve.cc
@@ -3000,30 +3000,28 @@ gfc_resolve_sqrt (gfc_expr *f, gfc_expr *x)
 /* Resolve the g77 compatibility function STAT AND FSTAT.  */
 
 void
-gfc_resolve_stat (gfc_expr *f, gfc_expr *n ATTRIBUTE_UNUSED,
-		  gfc_expr *a ATTRIBUTE_UNUSED)
+gfc_resolve_stat (gfc_expr *f, gfc_expr *n ATTRIBUTE_UNUSED, gfc_expr *a)
 {
   f->ts.type = BT_INTEGER;
-  f->ts.kind = gfc_default_integer_kind;
+  f->ts.kind = a->ts.kind;
   f->value.function.name = gfc_get_string (PREFIX ("stat_i%d"), f->ts.kind);
 }
 
 
 void
-gfc_resolve_lstat (gfc_expr *f, gfc_expr *n ATTRIBUTE_UNUSED,
-		   gfc_expr *a ATTRIBUTE_UNUSED)
+gfc_resolve_lstat (gfc_expr *f, gfc_expr *n ATTRIBUTE_UNUSED, gfc_expr *a)
 {
   f->ts.type = BT_INTEGER;
-  f->ts.kind = gfc_default_integer_kind;
+  f->ts.kind = a->ts.kind;
   f->value.function.name = gfc_get_string (PREFIX ("lstat_i%d"), f->ts.kind);
 }
 
 
 void
-gfc_resolve_fstat (gfc_expr *f, gfc_expr *n, gfc_expr *a ATTRIBUTE_UNUSED)
+gfc_resolve_fstat (gfc_expr *f, gfc_expr *n, gfc_expr *a)
 {
   f->ts.type = BT_INTEGER;
-  f->ts.kind = gfc_default_integer_kind;
+  f->ts.kind = a->ts.kind;
   if (n->ts.kind != f->ts.kind)
     gfc_convert_type (n, &f->ts, 2);
 
@@ -4159,7 +4157,9 @@ void
 gfc_resolve_stat_sub (gfc_code *c)
 {
   const char *name;
-  name = gfc_get_string (PREFIX ("stat_i%d_sub"), gfc_default_integer_kind);
+  gfc_typespec *ts;
+  ts = &c->ext.actual->next->expr->ts;
+  name = gfc_get_string (PREFIX ("stat_i%d_sub"), ts->kind);
   c->resolved_sym = gfc_get_intrinsic_sub_symbol (name);
 }
 
@@ -4168,7 +4168,9 @@ void
 gfc_resolve_lstat_sub (gfc_code *c)
 {
   const char *name;
-  name = gfc_get_string (PREFIX ("lstat_i%d_sub"), gfc_default_integer_kind);
+  gfc_typespec *ts;
+  ts = &c->ext.actual->next->expr->ts;
+  name = gfc_get_string (PREFIX ("lstat_i%d_sub"), ts->kind);
   c->resolved_sym = gfc_get_intrinsic_sub_symbol (name);
 }
 
diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
index b077cee86a3..f03144f9427 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -225,6 +225,12 @@ tree gfor_fndecl_iargc;
 tree gfor_fndecl_kill;
 tree gfor_fndecl_kill_sub;
 tree gfor_fndecl_is_contiguous0;
+tree gfor_fndecl_fstat_i4_sub;
+tree gfor_fndecl_fstat_i8_sub;
+tree gfor_fndecl_lstat_i4_sub;
+tree gfor_fndecl_lstat_i8_sub;
+tree gfor_fndecl_stat_i4_sub;
+tree gfor_fndecl_stat_i8_sub;
 
 
 /* Intrinsic functions implemented in Fortran.  */
@@ -3910,6 +3916,34 @@ gfc_build_intrinsic_function_decls (void)
 	gfc_int4_type_node, 1, pvoid_type_node);
   DECL_PURE_P (gfor_fndecl_is_contiguous0) = 1;
   TREE_NOTHROW (gfor_fndecl_is_contiguous0) = 1;
+
+  gfor_fndecl_fstat_i4_sub = gfc_build_library_function_decl (
+	get_identifier (PREFIX ("fstat_i4_sub")), void_type_node,
+	3, gfc_pint4_type_node, gfc_pint4_type_node, gfc_pint4_type_node);
+
+  gfor_fndecl_fstat_i8_sub = gfc_build_library_function_decl (
+	get_identifier (PREFIX ("fstat_i8_sub")), void_type_node,
+	3, gfc_pint8_type_node, gfc_pint8_type_node, gfc_pint8_type_node);
+
+  gfor_fndecl_lstat_i4_sub = gfc_build_library_function_decl (
+	get_identifier (PREFIX ("lstat_i4_sub")), void_type_node,
+	4, pchar_type_node, gfc_pint4_type_node, gfc_pint4_type_node,
+	gfc_charlen_type_node);
+
+  gfor_fndecl_lstat_i8_sub = gfc_build_library_function_decl (
+	get_identifier (PREFIX ("lstat_i8_sub")), void_type_node,
+	4, pchar_type_node, gfc_pint8_type_node, gfc_pint8_type_node,
+	gfc_charlen_type_node);
+
+  gfor_fndecl_stat_i4_sub = gfc_build_library_function_decl (
+	get_identifier (PREFIX ("stat_i4_sub")), void_type_node,
+	4, pchar_type_node, gfc_pint4_type_node, gfc_pint4_type_node,
+	gfc_charlen_type_node);
+
+  gfor_fndecl_stat_i8_sub = gfc_build_library_function_decl (
+	get_identifier (PREFIX ("stat_i8_sub")), void_type_node,
+	4, pchar_type_node, gfc_pint8_type_node, gfc_pint8_type_node,
+	gfc_charlen_type_node);
 }
 
 
diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index e720b42355f..b6691f58bee 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -5871,6 +5871,125 @@ gfc_conv_intrinsic_findloc (gfc_se *se, gfc_expr *expr)
 
 }
 
+/* Emit code for fstat, lstat and stat intrinsic subroutines.  */
+
+static tree
+conv_intrinsic_fstat_lstat_stat_sub (gfc_code *code)
+{
+  stmtblock_t block;
+  gfc_se se, se_stat;
+  tree unit;
+  tree name, slen;
+  tree vals;
+  tree arg3 = NULL_TREE;
+  tree stat = NULL_TREE ;
+  tree present = NULL_TREE;
+  tree tmp;
+  int kind;
+
+  gfc_init_block (&block);
+  gfc_init_se (&se, NULL);
+
+  switch (code->resolved_isym->id)
+    {
+    case GFC_ISYM_FSTAT:
+      /* Deal with the UNIT argument.  */
+      gfc_conv_expr (&se, code->ext.actual->expr);
+      gfc_add_block_to_block (&block, &se.pre);
+      unit = gfc_evaluate_now (se.expr, &block);
+      unit = gfc_build_addr_expr (NULL_TREE, unit);
+      gfc_add_block_to_block (&block, &se.post);
+      break;
+
+    case GFC_ISYM_LSTAT:
+    case GFC_ISYM_STAT:
+      /* Deal with the NAME argument.  */
+      gfc_conv_expr (&se, code->ext.actual->expr);
+      gfc_conv_string_parameter (&se);
+      gfc_add_block_to_block (&block, &se.pre);
+      name = se.expr;
+      slen = se.string_length;
+      gfc_add_block_to_block (&block, &se.post);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Deal with the VALUES argument.  */
+  gfc_init_se (&se, NULL);
+  gfc_conv_expr_descriptor (&se, code->ext.actual->next->expr);
+  vals = gfc_build_addr_expr (NULL_TREE, se.expr);
+  gfc_add_block_to_block (&block, &se.pre);
+  gfc_add_block_to_block (&block, &se.post);
+  kind = code->ext.actual->next->expr->ts.kind;
+
+  /* Deal with an optional STATUS.  */
+  if (code->ext.actual->next->next->expr)
+    {
+      gfc_init_se (&se_stat, NULL);
+      gfc_conv_expr (&se_stat, code->ext.actual->next->next->expr);
+      stat = gfc_create_var (gfc_get_int_type (kind), "_stat");
+      arg3 = gfc_build_addr_expr (NULL_TREE, stat);
+
+      /* Handle case of status being an optional dummy.  */
+      gfc_symbol *sym = code->ext.actual->next->next->expr->symtree->n.sym;
+      if (sym->attr.dummy && sym->attr.optional)
+	{
+	  present = gfc_conv_expr_present (sym);
+	  arg3 = fold_build3_loc (input_location, COND_EXPR,
+				  TREE_TYPE (arg3), present, arg3,
+				  fold_convert (TREE_TYPE (arg3),
+						null_pointer_node));
+	}
+    }
+
+  /* Call library function depending on KIND of VALUES argument.  */
+  switch (code->resolved_isym->id)
+    {
+    case GFC_ISYM_FSTAT:
+      tmp = (kind == 4 ? gfor_fndecl_fstat_i4_sub : gfor_fndecl_fstat_i8_sub);
+      break;
+    case GFC_ISYM_LSTAT:
+      tmp = (kind == 4 ? gfor_fndecl_lstat_i4_sub : gfor_fndecl_lstat_i8_sub);
+      break;
+    case GFC_ISYM_STAT:
+      tmp = (kind == 4 ? gfor_fndecl_stat_i4_sub : gfor_fndecl_stat_i8_sub);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  if (code->resolved_isym->id == GFC_ISYM_FSTAT)
+    tmp = build_call_expr_loc (input_location, tmp, 3, unit, vals,
+			       stat ? arg3 : null_pointer_node);
+  else
+    tmp = build_call_expr_loc (input_location, tmp, 4, name, vals,
+			       stat ? arg3 : null_pointer_node, slen);
+  gfc_add_expr_to_block (&block, tmp);
+
+  /* Handle kind conversion of status.  */
+  if (stat && stat != se_stat.expr)
+    {
+      stmtblock_t block2;
+
+      gfc_init_block (&block2);
+      gfc_add_modify (&block2, se_stat.expr,
+		      fold_convert (TREE_TYPE (se_stat.expr), stat));
+
+      if (present)
+	{
+	  tmp = build3_v (COND_EXPR, present, gfc_finish_block (&block2),
+			  build_empty_stmt (input_location));
+	  gfc_add_expr_to_block (&block, tmp);
+	}
+      else
+	gfc_add_block_to_block (&block, &block2);
+    }
+
+  return gfc_finish_block (&block);
+}
+
 /* Emit code for minval or maxval intrinsic.  There are many different cases
    we need to handle.  For performance reasons we sometimes create two
    loops instead of one, where the second one is much simpler.
@@ -13352,6 +13471,12 @@ gfc_conv_intrinsic_subroutine (gfc_code *code)
       res = conv_intrinsic_free (code);
       break;
 
+    case GFC_ISYM_FSTAT:
+    case GFC_ISYM_LSTAT:
+    case GFC_ISYM_STAT:
+      res = conv_intrinsic_fstat_lstat_stat_sub (code);
+      break;
+
     case GFC_ISYM_RANDOM_INIT:
       res = conv_intrinsic_random_init (code);
       break;
diff --git a/gcc/fortran/trans.h b/gcc/fortran/trans.h
index 55541845a6d..1d04b22abc8 100644
--- a/gcc/fortran/trans.h
+++ b/gcc/fortran/trans.h
@@ -983,6 +983,12 @@ extern GTY(()) tree gfor_fndecl_iargc;
 extern GTY(()) tree gfor_fndecl_kill;
 extern GTY(()) tree gfor_fndecl_kill_sub;
 extern GTY(()) tree gfor_fndecl_is_contiguous0;
+extern GTY(()) tree gfor_fndecl_fstat_i4_sub;
+extern GTY(()) tree gfor_fndecl_fstat_i8_sub;
+extern GTY(()) tree gfor_fndecl_lstat_i4_sub;
+extern GTY(()) tree gfor_fndecl_lstat_i8_sub;
+extern GTY(()) tree gfor_fndecl_stat_i4_sub;
+extern GTY(()) tree gfor_fndecl_stat_i8_sub;
 
 /* Implemented in Fortran.  */
 extern GTY(()) tree gfor_fndecl_sc_kind;
diff --git a/gcc/testsuite/gfortran.dg/stat_3.f90 b/gcc/testsuite/gfortran.dg/stat_3.f90
index 93ec1836a9a..9bfff1eeb7d 100644
--- a/gcc/testsuite/gfortran.dg/stat_3.f90
+++ b/gcc/testsuite/gfortran.dg/stat_3.f90
@@ -44,3 +44,16 @@ subroutine sub2 ()
   call lstat (name, status=perr, values= pbuf)
   call fstat (unit, status=perr, values= pbuf)
 end
+
+subroutine sub3 ()
+  implicit none
+  integer(1)          :: ierr1, unit1 = 10
+  integer(2)          :: buff2(13)
+  integer(4)          :: buff4(13)
+  integer(8)          :: buff8(13)
+  character(len=32)   :: name = "/etc/passwd"
+  ierr1 = stat (name,values=buff2)               ! { dg-error "with kind 2" }
+  call fstat (unit1, values=buff2)               ! { dg-error "with kind 2" }
+  call fstat (unit1, values=buff4, status=ierr1) ! { dg-error "at least four" }
+  call lstat (name,  values=buff8, status=ierr1) ! { dg-error "at least four" }
+end
diff --git a/gcc/testsuite/gfortran.dg/stat_4.f90 b/gcc/testsuite/gfortran.dg/stat_4.f90
new file mode 100644
index 00000000000..c2d36ffc819
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/stat_4.f90
@@ -0,0 +1,94 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original" }
+! PR fortran/82480 - make STAT/LSTAT/FSTAT generic
+
+subroutine fstat_sub_wrapper (unit, values8, status, opt_status4, opt_status8)
+  implicit none
+  integer(1), intent(in)            :: unit
+  integer(8), intent(out)           :: values8(:)
+  integer(2), intent(out)           :: status
+  integer(4), intent(out), optional :: opt_status4
+  integer(8), intent(out), optional :: opt_status8
+  call fstat (unit, values8, status)
+  call fstat (unit, values8, opt_status4)
+  call fstat (unit, values8, opt_status8)
+end
+
+subroutine stat_sub_wrapper (name, values4, status, opt_status4, opt_status8)
+  implicit none
+  character(*), intent(in)            :: name
+  integer(4),   intent(out)           :: values4(:)
+  integer(2),   intent(out)           :: status
+  integer(4),   intent(out), optional :: opt_status4
+  integer(8),   intent(out), optional :: opt_status8
+  call stat  (name, values4, status)
+  call lstat (name, values4, status)
+  call stat  (name, values4, opt_status4)
+  call lstat (name, values4, opt_status4)
+  call stat  (name, values4, opt_status8)
+  call lstat (name, values4, opt_status8)
+end
+
+subroutine sub1 ()
+  implicit none
+  character(len=32)   :: name = "/etc/passwd"
+  integer(1)          :: unit1 = 10
+  integer(4)          :: unit4 = 10, buff4(13)
+  integer(8)          :: unit8 = 10, buff8(13)
+  integer             :: ierr
+  ierr = fstat (unit1, values=buff4)
+  ierr = fstat (unit1, values=buff8)
+  ierr = fstat (unit4, values=buff4)
+  ierr = fstat (unit4, values=buff8)
+  ierr = fstat (unit8, values=buff4)
+  ierr = fstat (unit8, values=buff8)
+  ierr = stat  (name,  values=buff4)
+  ierr = stat  (name,  values=buff8)
+  ierr = lstat (name,  values=buff4)
+  ierr = lstat (name,  values=buff8)
+end
+
+subroutine sub2 ()
+  implicit none
+  integer(2)          :: ierr2, unit2 = 10
+  integer(4)          :: ierr4, unit4 = 10, buff4(13)
+  integer(8)          :: ierr8, unit8 = 10, buff8(13)
+  character(len=32)   :: name = "/etc/passwd"
+  call fstat (unit2, values=buff4)
+  call fstat (unit2, values=buff8)
+  call fstat (unit4, values=buff4)
+  call fstat (unit4, values=buff8)
+  call fstat (unit8, values=buff4)
+  call fstat (unit8, values=buff8)
+  call stat  (name,  values=buff4)
+  call lstat (name,  values=buff4)
+  call stat  (name,  values=buff8)
+  call lstat (name,  values=buff8)
+  call fstat (unit4, values=buff4, status=ierr2)
+  call fstat (unit4, values=buff4, status=ierr4)
+  call fstat (unit4, values=buff4, status=ierr8)
+  call fstat (unit4, values=buff8, status=ierr2)
+  call fstat (unit4, values=buff8, status=ierr4)
+  call fstat (unit4, values=buff8, status=ierr8)
+  call stat  (name,  values=buff4, status=ierr4)
+  call lstat (name,  values=buff4, status=ierr4)
+  call stat  (name,  values=buff4, status=ierr8)
+  call lstat (name,  values=buff4, status=ierr8)
+  call stat  (name,  values=buff8, status=ierr4)
+  call lstat (name,  values=buff8, status=ierr4)
+end
+
+! { dg-final { scan-tree-dump-times "_gfortran_fstat_i4_sub" 6 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_lstat_i4_sub" 6 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_stat_i4_sub" 6 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_fstat_i8_sub" 9 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_lstat_i8_sub" 2 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_stat_i8_sub" 2 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_fstat_i4 " 3 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_fstat_i8 " 3 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_lstat_i4 " 1 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_lstat_i8 " 1 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_stat_i4 " 1 "original" } }
+! { dg-final { scan-tree-dump-times "_gfortran_stat_i8 " 1 "original" } }
+! { dg-final { scan-tree-dump-times "opt_status4" 11 "original" } }
+! { dg-final { scan-tree-dump-times "opt_status8" 11 "original" } }
-- 
2.51.0

Reply via email to