diff mbox series

PR fortran/69101 -- IEEE_SELECTED_REAL_KIND part I

Message ID 20190105040702.GA98056@troutmask.apl.washington.edu
State New
Headers show
Series PR fortran/69101 -- IEEE_SELECTED_REAL_KIND part I | expand

Commit Message

Steve Kargl Jan. 5, 2019, 4:07 a.m. UTC
2019-01-04  Steven G. Kargl  <kargl@gcc.gnu.org>

	PR fortran/69101
	* expr.c (gfc_check_init_expr): If errors were emitted during
	simplification of ieee_selected_real_kind(), clear any additional
	errors anre return false.
	* simplify.c (simplify_ieee_selected_real_kind): make
        ieee_selected_real_kind() generic in an initialization expression, and
	check for conformance of actual arguments.

 2019-01-04  Steven G. Kargl  <kargl@gcc.gnu.org>

	PR fortran/69101
	* gfortran.dg/ieee/ieee_7.f90: Remove invalid code.
	* gfortran.dg/ieee/pr69101_1.f90: Check for errors in actual
	arguments of ieee_selected_real_kind().
	* gfortran.dg/ieee/pr69101_2.f90: Test -std=f2003.  RADIX was added
	in F2008.

TL;DR version

The attached patch fixes ieee_selected_real_kind (ISRK) when
used in an initialization expression, where either keywords are
mixed up compared to their position or the actual arguments are
not default integer kind.  It does not fix the use of ISRK in
an ordinary expression.

Test on i586-*-freebsd and x86_64-*-freebsd.  OK to commit?


Long version

ISRK is a function with a generic interface.  It is available from
the ieee_arithmetic intrinsic module.  The problem is that the
Fortran standard has specified a generic interface that is not
expressable in Fortran; and hence, gfortran cannot express it in the
file ieee_arithmetic.F90.  If one thinks about the situation, ISRK
is then neither a module procedure nor an intrinsic subprogram.

This patch expands upon the original simplification process when
an ISRK is seen.  It inspects the actual arguments for keywords
and provides a proper ordering.  It checks that each argument, if
present, is a scalar integer entity, and whether -std=F2008 or
later is in effect.  Because gfortran is performing simplification
in an initialization expression, queuing error messages with gfc_error()
is disabled.  Thus, errors are emitted with gfc_error_now().  If one
or more error is emitted, the portion of the patch in expr.c short
circuits simplification to prevent run-on errors.

It is now possible to do 

   program foo
      use ieee_arithmetic, only : isrk => ieee_selected_real_kind
      integer, parameter :: rknd = isrk(radix=2_1, p=6_8, r=200_2)
      print *, rknd
   end program foo

Note, ISRK must be available from the module to allow the renaming
that occurs above, but ideally gfortran should use its machinery
for intrinsic function.

The patch only fixes the use in initialization expression.  If ISRK
appears in a non-initialization expression, gfortran keels over.
Fixes that is Part II of addressing shortcoming with ISRK.

Finally, things are going to even more interesting when F2018's
ieee_real() and ieee_int() are added to gfortran.

Comments

Thomas Koenig Jan. 5, 2019, 12:23 p.m. UTC | #1
Hi Steve,

thanks for taking this on!

Maybe it would be better to put the checking and argument reordering
into its own routine (something like gfc_check_minloc_maxloc in
check.c) so all three arguments would be present, with an
expression possibly containing NULL, for the later routines.
This could add some clarity and save code duplication later,
when the second part of the patch is done.

However, I do not feel strongly about that. If you feel that your
current approach is better, that is fine by me.

Regards

	Thomas
Steve Kargl Jan. 5, 2019, 5:22 p.m. UTC | #2
On Sat, Jan 05, 2019 at 01:23:14PM +0100, Thomas Koenig wrote:
> 
> Maybe it would be better to put the checking and argument reordering
> into its own routine (something like gfc_check_minloc_maxloc in
> check.c) so all three arguments would be present, with an
> expression possibly containing NULL, for the later routines.
> This could add some clarity and save code duplication later,
> when the second part of the patch is done.
> 
> However, I do not feel strongly about that. If you feel that your
> current approach is better, that is fine by me.
> 

Thanks, Thomas.  I tried a few things and finally landed on
the keyword vs positional handling and argument checking in
simplify_ieee_selected_real_kind.  The actual heavy lifting
comes from gfc_simplify_selected_real_kind where the constant
expressions are extracted and gfortran tries to determine a
valid kind.  I see if I can moving the keyword/positional
and checking into a separate function.

Long term I think we need to put all functions into the table
of intrinsic subprograms, so that handling these functions is
almost identical to sin(), cos(), etc.  This would then give
the keyword vs positional handling automatically.  ieee module
procedures would be marked in gfc_intrinsic_sym with a bool or
a bit in a bitfield, ie,

   gfc_intrinsic_sym *isym;
   isym->ieee = true;       ! Member of ieee_arthmetic.
or
   isym->ieee = 1;          ! Member of ieee_arithmetic.

'isym->ieee = true' then indicates that gfortran needs to check
if the ieee_arithmetic has been used and is in scope when a procedure
is seen in the Fortran code.  If yes, we have the module procedure.
If no, we have a possibly user-defined procedure that shadows a routine
from an intrinsic module.
Steve Kargl Jan. 5, 2019, 5:43 p.m. UTC | #3
On Sat, Jan 05, 2019 at 09:22:34AM -0800, Steve Kargl wrote:
> On Sat, Jan 05, 2019 at 01:23:14PM +0100, Thomas Koenig wrote:
> > 
> > Maybe it would be better to put the checking and argument reordering
> > into its own routine (something like gfc_check_minloc_maxloc in
> > check.c) so all three arguments would be present, with an
> > expression possibly containing NULL, for the later routines.
> > This could add some clarity and save code duplication later,
> > when the second part of the patch is done.
> > 
> > However, I do not feel strongly about that. If you feel that your
> > current approach is better, that is fine by me.
> > 
> 
> Thanks, Thomas.  I tried a few things and finally landed on
> the keyword vs positional handling and argument checking in
> simplify_ieee_selected_real_kind.  The actual heavy lifting
> comes from gfc_simplify_selected_real_kind where the constant
> expressions are extracted and gfortran tries to determine a
> valid kind.  I see if I can moving the keyword/positional
> and checking into a separate function.
> 

Well, that was quick.  Moving code around is problematic.  The
hijacking of the interface checking causes gfortran to see from
1 to 3 actual arguments.  A simple example is ISRK(r=3.14) (yes,
an invalid call).  This has exact 1 actual argument, namely,
arg=expr->value.function.actual.  So, gfortran see arg->next=NULL
and arg->next->next obvious cannot exist.  The re-ordering would
require allocation of next and next->next to get the number of
actual arguments to match P,R,RADIX, so that we can extract 
pointers to the expressions.  Not worth the effort at the moment.

Of I might change my mind when I try to tackle the hijacking of

  function rknd(n) result r
    use ieee_arithmetic
    integer n
    r = ieee_selected_real_kind(p=n)
  end function rknd
Thomas Koenig Jan. 8, 2019, 9:27 p.m. UTC | #4
Hi Steve,

> Well, that was quick.  Moving code around is problematic.

Thanks for checking. The patch is OK for trunk.

Regards

	Thomas
Steve Kargl Jan. 21, 2019, 1:04 a.m. UTC | #5
On Tue, Jan 08, 2019 at 10:27:25PM +0100, Thomas Koenig wrote:
> Hi Steve,
> 
> > Well, that was quick.  Moving code around is problematic.
> 
> Thanks for checking. The patch is OK for trunk.
> 

Thanks.  I decided to see if long term approach would work.
It almost does.  The attached patch put ieee_selected_real_kind
into the table of intrinsic functions.  It automatically gets
us a generic routine with argument chekcing and simplification.
This just works:

program foo
   use ieee_arithmetic, only : ieee_selected_real_kind
   integer, parameter :: n = ieee_selected_real_kind(6_2)
   i = 6
   print *, n, ieee_selected_real_kind(6_8), ieee_selected_real_kind(i)
end program foo

Now, the downside.  I can't finesse rename on USE.  This does not
work, and I'm stuck at the moment.

subroutine bar
   use ieee_arithmetic, only : isrk => ieee_selected_real_kind
   integer, parameter :: n = isrk(6)
   i = 6
   print *, n, isrk(6), isrk(i)
end subroutine bar

If anyone has an idea, I would be quite happy to hear about it.
Steve Kargl Jan. 21, 2019, 7:02 a.m. UTC | #6
On Sun, Jan 20, 2019 at 05:04:24PM -0800, Steve Kargl wrote:
> On Tue, Jan 08, 2019 at 10:27:25PM +0100, Thomas Koenig wrote:
> > Hi Steve,
> > 
> > > Well, that was quick.  Moving code around is problematic.
> > 
> > Thanks for checking. The patch is OK for trunk.
> > 
> 
> Thanks.  I decided to see if long term approach would work.
> It almost does.  The attached patch put ieee_selected_real_kind
> into the table of intrinsic functions.  It automatically gets
> us a generic routine with argument chekcing and simplification.
> This just works:
> 
> program foo
>    use ieee_arithmetic, only : ieee_selected_real_kind
>    integer, parameter :: n = ieee_selected_real_kind(6_2)
>    i = 6
>    print *, n, ieee_selected_real_kind(6_8), ieee_selected_real_kind(i)
> end program foo
> 
> Now, the downside.  I can't finesse rename on USE.  This does not
> work, and I'm stuck at the moment.
> 
> subroutine bar
>    use ieee_arithmetic, only : isrk => ieee_selected_real_kind
>    integer, parameter :: n = isrk(6)
>    i = 6
>    print *, n, isrk(6), isrk(i)
> end subroutine bar
> 
> If anyone has an idea, I would be quite happy to hear about it.
> 

Well, I found a way to at least deal with the renaming issue.
It is ugly, and won't be pursued.
diff mbox series

Patch

Index: gcc/fortran/expr.c
===================================================================
--- gcc/fortran/expr.c	(revision 267591)
+++ gcc/fortran/expr.c	(working copy)
@@ -2768,11 +2768,20 @@  gfc_check_init_expr (gfc_expr *e)
 	  mod = sym->generic->sym->from_intmod;
 	if (mod == INTMOD_IEEE_ARITHMETIC || mod == INTMOD_IEEE_EXCEPTIONS)
 	  {
+	    int ecnt;
+
 	    gfc_expr *new_expr = gfc_simplify_ieee_functions (e);
 	    if (new_expr)
 	      {
 		gfc_replace_expr (e, new_expr);
 		t = true;
+		break;
+	      }
+	    gfc_get_errors (NULL, &ecnt);
+	    if (ecnt > 0)
+ 	      {
+		t = false;
+		gfc_clear_error ();
 		break;
 	      }
 	  }
Index: gcc/fortran/simplify.c
===================================================================
--- gcc/fortran/simplify.c	(revision 267591)
+++ gcc/fortran/simplify.c	(working copy)
@@ -8521,25 +8521,129 @@  gfc_simplify_compiler_version (void)
 
 /* Simplification routines for intrinsics of IEEE modules.  */
 
+/* IEEE_SELECTED_REAL_KIND is generic, but cannot be specified in the
+   intrinsic module ieee_arithmetic.mod due to ambiguous interfaces.  When
+   it appears in an initialization expression, the checking must be done
+   here.  */
+
 gfc_expr *
 simplify_ieee_selected_real_kind (gfc_expr *expr)
 {
-  gfc_actual_arglist *arg;
-  gfc_expr *p = NULL, *q = NULL, *rdx = NULL;
+  gfc_actual_arglist *arg0, *arg1, *arg2;
+  gfc_expr *p, *r, *rdx;
 
-  arg = expr->value.function.actual;
-  p = arg->expr;
-  if (arg->next)
+  arg1 = arg2 = NULL;
+  p = r = rdx = NULL;
+
+  arg0 = expr->value.function.actual;
+  if (arg0->next)
+    { 
+      arg1 = arg0->next;
+      if (arg1->next) arg2 = arg1->next;
+    }
+
+  if (!arg0->expr && arg1 && !arg1->expr && arg2 && !arg2->expr)
+   {
+      gfc_error_now ("At least one of P, R, or RADIX must be present in "
+		     "IEEE_SELECTED_REAL_KIND at %C");
+      gfc_clear_error ();
+      return NULL;
+   }
+
+  /* Look at first argument.  */
+  if (!arg0->name || strcmp(arg0->name, "p") == 0)
+    p = arg0->expr;
+  else if (strcmp(arg0->name, "r") == 0)
+    r = arg0->expr;
+  else if (strcmp(arg0->name, "radix") == 0)
+    rdx = arg0->expr;
+  else
+    goto invalid;
+
+  /* Look at second argument, if it exists.  */
+  if (arg1)
     {
-      q = arg->next->expr;
-      if (arg->next->next)
-	rdx = arg->next->next->expr;
+      if (!arg1->name || strcmp(arg1->name, "r") == 0)
+ 	{
+	  if (r)
+	    return NULL;
+	  r = arg1->expr;
+	}
+      else if (strcmp(arg1->name, "p") == 0)
+	{
+	  if (p)
+	    return NULL;
+	  p = arg1->expr;
+	}
+      else if (strcmp(arg1->name, "radix") == 0)
+	{
+	  if (rdx)
+	    return NULL;
+	  rdx = arg1->expr;
+	}
+      else
+	goto invalid;
     }
 
-  /* Currently, if IEEE is supported and this module is built, it means
-     all our floating-point types conform to IEEE. Hence, we simply handle
-     IEEE_SELECTED_REAL_KIND like SELECTED_REAL_KIND.  */
-  return gfc_simplify_selected_real_kind (p, q, rdx);
+  /* Look at third argument, if it exists.  */
+  if (arg2)
+    {
+      if (!arg2->name || strcmp(arg2->name, "radix") == 0)
+	{
+	  if (rdx)
+	    return NULL;
+	  rdx = arg2->expr;
+	}
+      else if (strcmp(arg2->name, "p") == 0)
+	{
+	  if (p)
+	    return NULL;
+	  p = arg2->expr;
+	}
+      else if (strcmp(arg2->name, "r") == 0)
+	{
+	  if (r)
+	    return NULL;
+	  r = arg2->expr;
+	}
+      else
+	goto invalid;
+    }
+
+  /* Check for scalar integer expressions.  */
+  if (p && (p->ts.type != BT_INTEGER || p->rank != 0))
+    goto mismatch;
+
+  if (r && (r->ts.type != BT_INTEGER || r->rank != 0))
+    goto mismatch;
+
+  if (rdx)
+    {
+      if (GFC_STD_F2008 & ~gfc_option.allow_std)
+	{
+	  gfc_error_now ("Fortran 2008: RADIX argument specified in "
+			 "IEEE_SELECTED_REAL_KIND at %C");
+	  return NULL;
+	}
+
+      if (rdx->ts.type != BT_INTEGER || rdx->rank != 0)
+	goto mismatch;
+    }
+
+  return gfc_simplify_selected_real_kind (p, r, rdx);
+
+mismatch:
+
+  gfc_error_now ("Scalar integer argument expected in "
+		 "IEEE_SELECTED_REAL_KIND at %C");
+  return NULL;
+
+invalid:
+
+  /* This should be unreachable.  */
+  gfc_fatal_error ("Invalid uses of IEEE_SELECTED_REAL_KIND at %C");
+  return NULL;
+
 }
 
 gfc_expr *
Index: gcc/testsuite/gfortran.dg/ieee/ieee_7.f90
===================================================================
--- gcc/testsuite/gfortran.dg/ieee/ieee_7.f90	(revision 267591)
+++ gcc/testsuite/gfortran.dg/ieee/ieee_7.f90	(working copy)
@@ -11,7 +11,6 @@ 
 
   ! Test IEEE_SELECTED_REAL_KIND in specification expressions
 
-  integer(kind=ieee_selected_real_kind()) :: i1
   integer(kind=ieee_selected_real_kind(10)) :: i2
   integer(kind=ieee_selected_real_kind(10,10)) :: i3
   integer(kind=ieee_selected_real_kind(10,10,2)) :: i4
@@ -19,7 +18,6 @@ 
   ! Test IEEE_SELECTED_REAL_KIND
 
   if (ieee_support_datatype(0.)) then
-    if (ieee_selected_real_kind() /= kind(0.)) STOP 1
     if (ieee_selected_real_kind(0) /= kind(0.)) STOP 2
     if (ieee_selected_real_kind(0,0) /= kind(0.)) STOP 3
     if (ieee_selected_real_kind(0,0,2) /= kind(0.)) STOP 4
Index: gcc/testsuite/gfortran.dg/ieee/pr69101_1.f90
===================================================================
--- gcc/testsuite/gfortran.dg/ieee/pr69101_1.f90	(nonexistent)
+++ gcc/testsuite/gfortran.dg/ieee/pr69101_1.f90	(working copy)
@@ -0,0 +1,10 @@ 
+! { dg-do compile }
+program foo
+   use ieee_arithmetic
+   implicit none
+   integer, parameter :: n(3) = [ 1, 2, 3]
+   integer, parameter :: i1 = ieee_selected_real_kind()        ! { dg-error "must be present in" }
+   integer, parameter :: j2 = ieee_selected_real_kind(p=6,p=7) ! { dg-error "has already appeared in" }
+   integer, parameter :: j3 = ieee_selected_real_kind(r=7.)    ! { dg-error "Scalar integer argument" }
+   integer, parameter :: j4 = ieee_selected_real_kind(n)       ! { dg-error "Scalar integer argument" }
+end program foo
Index: gcc/testsuite/gfortran.dg/ieee/pr69101_2.f90
===================================================================
--- gcc/testsuite/gfortran.dg/ieee/pr69101_2.f90	(nonexistent)
+++ gcc/testsuite/gfortran.dg/ieee/pr69101_2.f90	(working copy)
@@ -0,0 +1,7 @@ 
+! { dg-do compile }
+! { dg-additional-options "-std=f2003" }
+program foo
+   use ieee_arithmetic
+   implicit none
+   integer, parameter :: jr = ieee_selected_real_kind(radix=2) ! { dg-error "RADIX argument" }
+end program foo