diff mbox

PR fortran/77978 -- STOP code fixes

Message ID 20161015002453.GA53147@troutmask.apl.washington.edu
State New
Headers show

Commit Message

Steve Kargl Oct. 15, 2016, 12:24 a.m. UTC
The attach patch fixes a number of shortcomings with
STOP codes in gfortran.  The updated comment in the
code nicely summarizes the problem.

 /* Match a number or character constant after an (ERROR) STOP or PAUSE
-   statement.  */
+   statement.  The requirements for a stop-code differs in the standards.
+
+   Fortran 95 has
+
+   R840 stop-stmt  is STOP [ stop-code ]
+   R841 stop-code  is scalar-char-constant
+                   or digit [ digit [ digit [ digit [ digit ] ] ] ]
+
+   Fortran 2003 is the same as Fortran 95 except R840 and R841 are now
+   R849 and R850.
+
+   Fortran 2008 has
+
+   R855 stop-stmt     is STOP [ stop-code ]
+   R856 allstop-stmt  is ALL STOP [ stop-code ]
+   R857 stop-code     is scalar-default-char-constant-expr
+                      or scalar-int-constant-expr
+*/

So, the F95/2003 "digit [...]" is not a scalar-int-constant-expr.
It sort of looks like a statement label, but of course it is not
a statement label as the stop code does label anything.  Currently,
gfortran parses "digit [...]" as an expression.  I've added the
necessary checking that "digit [...]" is valid with one exception. 
For the code 

  program foo
    stop merge(667, 668, .true.)
  end 

gfortran with either -std=f95 or -std=f2003 should reject 
this code.  My patch does not fix this issue, because it
would (1) require a complete rewrite of gfc_match_stopcode
(which I am not willing to do) and (2) it simply is a vastly
unimportant corner case that gives the desired behavior.

A second issue raised by John in PR fortran/77978 is that
for F95/2003, the following is valid free-form source code:

  program foo
    stop666
  end

but is invalid F2008.  The patch fixes this bug, too.

OK to commit?

2016-10-XX  Steven G. Kargl  <kargl@gcc.gnu.org>

	PR fortran/77978
	* match.c (gfc_match_stopcode): Fix error reporting for several
	deficiencies in matching STOP codes.
 
2016-10-XX  Steven G. Kargl  <kargl@gcc.gnu.org>

	PR fortran/77978
	* gfortran.dg/pr77978_1.f90: New test.
	* gfortran.dg/pr77978_2.f90: Ditto.
	* gfortran.dg/pr77978_3.f90: Ditto.

Comments

Steve Kargl Oct. 15, 2016, 12:32 a.m. UTC | #1
On Fri, Oct 14, 2016 at 05:24:53PM -0700, Steve Kargl wrote:
> For the code 
> 
>   program foo
>     stop merge(667, 668, .true.)
>   end 
> 
> gfortran with either -std=f95 or -std=f2003 should reject 
> this code.  My patch does not fix this issue, because it
> would (1) require a complete rewrite of gfc_match_stopcode
> (which I am not willing to do) and (2) it simply is a vastly
> unimportant corner case that gives the desired behavior.
> 

I take it back.  It is sort of accidently fixed with a
completely unintelligent error for -std=f95.  It is
accepted for -std=f2003.  Either way I have no intention
of fixing this usage.

troutmask:sgk[492] gfc7 -c -std=f2003 a.f90
troutmask:sgk[493] gfc7 -c -std=f95 a.f90
a.f90:2:6:

   stop merge(667,668,.true.)
      1
Error: Fortran 2003: Elemental function as initialization expression
with non-integer/non-character arguments at (1)
diff mbox

Patch

Index: gcc/fortran/match.c
===================================================================
--- gcc/fortran/match.c	(revision 241074)
+++ gcc/fortran/match.c	(working copy)
@@ -2732,7 +2732,24 @@  gfc_match_cycle (void)
 
 
 /* Match a number or character constant after an (ERROR) STOP or PAUSE
-   statement.  */
+   statement.  The requirements for a stop-code differs in the standards.
+
+   Fortran 95 has
+
+   R840 stop-stmt  is STOP [ stop-code ]
+   R841 stop-code  is scalar-char-constant
+                   or digit [ digit [ digit [ digit [ digit ] ] ] ]
+
+   Fortran 2003 is the same as Fortran 95 except R840 and R841 are now
+   R849 and R850.
+
+   Fortran 2008 has
+
+   R855 stop-stmt     is STOP [ stop-code ]
+   R856 allstop-stmt  is ALL STOP [ stop-code ]
+   R857 stop-code     is scalar-default-char-constant-expr
+                      or scalar-int-constant-expr
+*/
 
 static match
 gfc_match_stopcode (gfc_statement st)
@@ -2740,6 +2757,27 @@  gfc_match_stopcode (gfc_statement st)
   gfc_expr *e;
   match m;
 
+  /* The default selected Standards. */
+  int std = GFC_STD_GNU | GFC_STD_LEGACY | GFC_STD_F77 | GFC_STD_F95
+	  | GFC_STD_F95_OBS | GFC_STD_F95_DEL | GFC_STD_F2003
+	  | GFC_STD_F2008 | GFC_STD_F2008_OBS | GFC_STD_F2008_TS;
+
+  if (gfc_current_form != FORM_FIXED)
+    {
+      char c;
+
+      c = gfc_peek_ascii_char ();
+
+      if (c != ' '
+	  && gfc_option.allow_std != std
+	  && (gfc_option.allow_std & GFC_STD_F2008))
+	{
+	  gfc_error ("Blank required in %s statement near %C",
+		     gfc_ascii_statement (st));
+	  return MATCH_ERROR;
+	}
+    }
+
   e = NULL;
 
   if (gfc_match_eos () != MATCH_YES)
@@ -2785,6 +2823,15 @@  gfc_match_stopcode (gfc_statement st)
 
   if (e != NULL)
     {
+      gfc_simplify_expr (e, 0);
+
+      if (e->expr_type != EXPR_CONSTANT)
+	{
+	  gfc_error ("STOP code at %L must be a constant expression",
+		     &e->where);
+	  goto cleanup;
+	}
+
       if (!(e->ts.type == BT_CHARACTER || e->ts.type == BT_INTEGER))
 	{
 	  gfc_error ("STOP code at %L must be either INTEGER or CHARACTER type",
@@ -2794,8 +2841,7 @@  gfc_match_stopcode (gfc_statement st)
 
       if (e->rank != 0)
 	{
-	  gfc_error ("STOP code at %L must be scalar",
-		     &e->where);
+	  gfc_error ("STOP code at %L must be scalar", &e->where);
 	  goto cleanup;
 	}
 
@@ -2807,12 +2853,35 @@  gfc_match_stopcode (gfc_statement st)
 	  goto cleanup;
 	}
 
-      if (e->ts.type == BT_INTEGER
-	  && e->ts.kind != gfc_default_integer_kind)
+      if (e->ts.type == BT_INTEGER)
 	{
-	  gfc_error ("STOP code at %L must be default integer KIND=%d",
-		     &e->where, (int) gfc_default_integer_kind);
-	  goto cleanup;
+	  if (e->ts.kind != gfc_default_integer_kind)
+	    {
+	      gfc_error ("STOP code at %L must be default integer KIND=%d",
+			 &e->where, (int) gfc_default_integer_kind);
+	      goto cleanup;
+	    }
+
+	  if (gfc_option.allow_std != std
+	      && (((gfc_option.allow_std & GFC_STD_F95)
+		   || (gfc_option.allow_std & GFC_STD_F2003))
+		  && !(gfc_option.allow_std & GFC_STD_F2008)))
+	    {
+	      int n;
+	      n = mpz_get_si (e->value.integer);
+	      if (n < 0)
+		{
+		  gfc_error ("STOP code at %L cannot be negative", &e->where);
+		  goto cleanup;
+		}
+
+	      if (n > 99999)
+		{
+		  gfc_error ("STOP code at %L contains too many digits",
+			     &e->where);
+		  goto cleanup;
+		}
+	    }
 	}
     }
 
Index: gcc/testsuite/gfortran.dg/pr77978_1.f90
===================================================================
--- gcc/testsuite/gfortran.dg/pr77978_1.f90	(nonexistent)
+++ gcc/testsuite/gfortran.dg/pr77978_1.f90	(working copy)
@@ -0,0 +1,23 @@ 
+! { dg-do compile }
+! { dg-options "-std=f95" }
+subroutine a1
+  integer, parameter :: i = -666
+  stop i ! { dg-error "cannot be negative" }
+end subroutine a1
+
+subroutine a2
+  stop -666 ! { dg-error "cannot be negative" }
+end subroutine a2
+
+subroutine a3
+  integer, parameter :: i = 123456
+  stop i ! { dg-error "too many digits" }
+end subroutine a3
+
+subroutine a4
+  stop 123456 ! { dg-error "too many digits" }
+end subroutine a4
+
+subroutine a5
+  stop merge(667,668,.true.) 
+end subroutine a5
Index: gcc/testsuite/gfortran.dg/pr77978_2.f90
===================================================================
--- gcc/testsuite/gfortran.dg/pr77978_2.f90	(nonexistent)
+++ gcc/testsuite/gfortran.dg/pr77978_2.f90	(working copy)
@@ -0,0 +1,5 @@ 
+! { dg-do compile }
+! { dg-options "-std=f2008" }
+subroutine a1
+  stop666 ! { dg-error "Blank required in STOP" }
+end subroutine a1
Index: gcc/testsuite/gfortran.dg/pr77978_3.f90
===================================================================
--- gcc/testsuite/gfortran.dg/pr77978_3.f90	(nonexistent)
+++ gcc/testsuite/gfortran.dg/pr77978_3.f90	(working copy)
@@ -0,0 +1,23 @@ 
+! { dg-do compile }
+! { dg-options "-std=f2008" }
+subroutine a1
+  integer, parameter :: i = -666
+  stop i
+end subroutine a1
+
+subroutine a2
+  stop -666
+end subroutine a2
+
+subroutine a3
+  integer, parameter :: i = 123456
+  stop i
+end subroutine a3
+
+subroutine a4
+  stop 123456
+end subroutine a4
+
+subroutine a5
+  stop merge(667,668,.true.) 
+end subroutine a5