[PATCH,fortran] Relax restrictions on KIND in SELECT CASE constructs

Steve Kargl sgk@troutmask.apl.washington.edu
Sat Jan 1 21:24:00 GMT 2005


On Tue, Dec 28, 2004 at 11:09:02AM -0800, Steve Kargl wrote:
> On Mon, Dec 27, 2004 at 09:27:23AM -0800, Steve Kargl wrote:
> > On Mon, Dec 27, 2004 at 01:46:16PM +0000, Paul Brook wrote:
> > > > 2004-12-17  Steven G. Kargl  <kargls@comcast.net>
> > > >
> > > >      * resolve.c (validate_case_label_expr): Fix handling of mismatched
> > > > KINDs.
> > > 
> > > No.
> > 
> > Okay, I'll open a PR about this problem.  I have no idea 
> > how to fix this at the moment.
> > 
> 
> I just re-read Pauls original reply and I think his suggestion 
> is incorrect with respect to 7.2.3 of the standard.  I deleted
> his email so, I'm reproducing it here:
> 
> > program foo
> >   integer i
> >   select case (i)
> >   case (0_4)
> >   case (1_8)
> >   end select
> > end program
> > 
> > Gives:
> > 
> >       SELECT CASE __convert_i4_i8[[((i))]]
> >       CASE (0 0)
> >       CASE (1_8 1_8)
> >       END SELECT
> > 
> > Which is obviously wrong.
> > 
> > We should probably convert all cases to the type of the select
> > expression.  Out of range values should either error, or warn
> > and remove that case.
> 
> I think this is wrong because we need to convert "i == 1_8" to the 
> KIND of i + 1_8 per 7.2.3, which has the kind of 1_8.  The passage
> from the standard is
> 
>   In the numeric relational operation
>          x1 rel-op x2
>   if the types or kind type parameters of x1 and x2 differ, their values
>   are converted to the type and kind type parameter of the expression
>   x1 + x2 before evaluation.
> 
> Since the conversion must be performed before evaluation, we may now
> conclude that in principle there are no out-of-range values.  Note,
> this does not mean that certain cases are not unreachable.  For example,
> 
>    integer*1 i        ! kind = 1, -128 <= i < 127
>    select case (i)    ! need to convert i to kind = 4.
>    case (1)           ! kind = 4, reachable
>    case (200)         ! kind = 4, unreachable because of range of i
>    end select 
> 
> The question becomes more complicated with a slight variation of 
> Paul's example.
> 
>    integer*1 i        ! kind = 1, -128 <= i < 127
>    select case (i)    !
>    case (1_4)         ! kind = 4, reachable
>    case (2_8)         ! kind = 8, reachable
>    end select 
> 
> Do we convert i and 1_4 to kind = 8?  Do we convert i to kind = 4
> in the comparison with 1_4 and convert it to kind = 8 in the 
> comparison with 2_8?   It is probably safe to convert everything to 
> the largest kind, because the smaller kind are proper subsets
> of the larger kinds. 
> 

With the overwhelming support of the gfortran community,
I've implemented the first choice.  The attached patch
is the result of this effort and the 3 additional test
case do what I expect.  These test cases should be committed
along with the test cases posted here:
http://gcc.gnu.org/ml/fortran/2004-12/msg00260.html

This patch walks the list of case values/ranges and
adjusts the kind of case expression if needed.  It
then adjusts the kind of the case/values/ranges
during validation.  As per 7.2.3, no warning of the
kind conversion are necesssary, so I did not issue
any warnings.

Bootstrapped and regression tested on i386-*-freebsd6.0.

Additional test cases are provided.
Please commit.

2005-01-01  Steven G. Kargl  <kargls@comcast.net>

      PR 19168
      *resolve.c (check_case_overlap): Typo in comment
      (validate_case_label_expr):  Fix up kinds of case values
      (resolve_select): Properly handle kind mismatches.

-- 
Steve
-------------- next part --------------
Index: resolve.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/fortran/resolve.c,v
retrieving revision 1.24
diff -u -b -u -b -B -r1.24 resolve.c
--- resolve.c	15 Dec 2004 03:56:05 -0000	1.24
+++ resolve.c	1 Jan 2005 21:11:46 -0000
@@ -2614,7 +2579,7 @@
 	  /* Count this merge.  */
 	  nmerges++;
 
-	  /* Cut the list in two pieces by steppin INSIZE places
+	  /* Cut the list in two pieces by stepping INSIZE places
              forward in the list, starting from P.  */
 	  psize = 0;
 	  q = p;
@@ -2711,32 +2676,39 @@
 }
 
 
-/* Check to see if an expression is suitable for use in a CASE
-   statement.  Makes sure that all case expressions are scalar
-   constants of the same type/kind.  Return FAILURE if anything
-   is wrong.  */
+/* Check to see if an expression is suitable for use in a CASE statement.
+   Makes sure that all case expressions are scalar constants of the same
+   type.  Return FAILURE if anything is wrong.  */
 
 static try
 validate_case_label_expr (gfc_expr * e, gfc_expr * case_expr)
 {
-  gfc_typespec case_ts = case_expr->ts;
 
   if (e == NULL) return SUCCESS;
 
-  if (e->ts.type != case_ts.type)
+  if (e->ts.type != case_expr->ts.type)
     {
       gfc_error ("Expression in CASE statement at %L must be of type %s",
-		 &e->where, gfc_basic_typename (case_ts.type));
+		 &e->where, gfc_basic_typename (case_expr->ts.type));
       return FAILURE;
     }
 
-  if (e->ts.kind != case_ts.kind)
+  /* C805 (R808) For a given case-construct, each case-value shall be of
+     the same type as case-expr.  For character type, length differences
+     are allowed, but the kind type parameters shall be the same.  */
+
+  if (case_expr->ts.type == BT_CHARACTER && e->ts.kind != case_expr->ts.kind)
     {
       gfc_error("Expression in CASE statement at %L must be kind %d",
-                &e->where, case_ts.kind);
+                &e->where, case_expr->ts.kind);
       return FAILURE;
     }
 
+  /* Convert the case value kind to that of case expression kind, if needed.
+     FIXME:  Should a warning be issued?  */
+  if (e->ts.kind != case_expr->ts.kind)
+    gfc_convert_type_warn (e, &case_expr->ts, 2, 0);
+
   if (e->rank != 0)
     {
       gfc_error ("Expression in CASE statement at %L must be scalar",
@@ -2819,6 +2791,40 @@
       return;
     }
 
+  /* PR 19168 has a long discussion concerning a mismatch of the kinds
+     of the SELECT CASE expression and its CASE values.  Walk the lists
+     of case values, and if we find a mismatch, promote case_expr to
+     the appropriate kind.  */
+
+  if (type == BT_LOGICAL || type == BT_INTEGER)
+    {
+      for (body = code->block; body; body = body->block)
+	{
+	  /* Walk the case label list.  */
+	  for (cp = body->ext.case_list; cp; cp = cp->next)
+	    {
+	      /* Intercept the DEFAULT case.  It does not have a kind.  */
+	      if (cp->low == NULL && cp->high == NULL)
+		continue;
+
+	      /* Unreachable case ranges are discarded, so ignore.  */	
+	      if (cp->low != NULL && cp->high != NULL
+		  && cp->low != cp->high
+		  && gfc_compare_expr (cp->low, cp->high) > 0)
+		continue;
+
+	      /* FIXME: Should a warning be issued?  */
+	      if (cp->low != NULL
+		  && case_expr->ts.kind != gfc_kind_max(case_expr, cp->low))
+		gfc_convert_type_warn (case_expr, &cp->low->ts, 2, 0);
+
+	      if (cp->high != NULL
+		  && case_expr->ts.kind != gfc_kind_max(case_expr, cp->high))
+ 		gfc_convert_type_warn (case_expr, &cp->high->ts, 2, 0);
+	    }
+	 }
+    }
+
   /* Assume there is no DEFAULT case.  */
   default_case = NULL;
   head = tail = NULL;
-------------- next part --------------
program select_7
  integer*1 i          ! kind = 1, -128 <= i < 127
  do i = 1, 4
    select case (i)     
    case (1_4)         ! kind = 4, reachable
      if (i /=  1_4) call abort
    case (2_8)         ! kind = 8, reachable
      if (i /= 2_8) call abort
    end select
  end do
end program select_7
!
!  Used with other Fortran 95 compilers
!
!subroutine abort
!  print *, 'Abort'
!  stop
!end subroutine abort
-------------- next part --------------
program select_8
  integer*1 i        ! kind = 1, -128 <= i < 127
  do i = 1, 4
    select case (i)    ! need to convert i to kind = 4.
    case (1)           ! kind = 4, reachable
      if (i /=  1) call abort
    case (200)         ! kind = 4, unreachable because of range of i
      if (i /= 200) call abort
    end select
  end do
end program select_8
!
!  Used with other Fortran 95 compilers
!
!subroutine abort
!  print *, 'Abort'
!  stop
!end subroutine abort
-------------- next part --------------
program select_9
  integer i
  do i = 1, 4
    select case (i)
    case (1_1)
      if (i /= 1) call abort
    case (2_2)
      if (i /= 2) call abort
    case (3)
      if (i /= 3) call abort
    case (4_8)
      if (i /= 4) call abort
    end select
  end do
end program select_9
!
!  Used with other Fortran 95 compilers
!
!subroutine abort
!  print *, 'Abort'
!  stop
!end subroutine abort


More information about the Fortran mailing list