RFC: PR 87923 -- ICE in gfc_widechar_to_char, at fortran/scanner.c:198

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

RFC: PR 87923 -- ICE in gfc_widechar_to_char, at fortran/scanner.c:198

gcc - fortran mailing list
All (plus Tobias, since you authored the testcase referenced below):

During the course of fixing PR 87923
[https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87923] I have decided
the right thing to do is to move much of the constraints verification
from the "matching" phase into the "resolution" phase for I/O
statements (OPEN, CLOSE, WRITE, READ, etc...). The bug described by
the PR occurs because several calls to "compare_to_allowed_values" are
made before checking that the tag expression is in fact a character
string of default kind. Type is usually checked, sometimes multiple
times, but for some tags the kind is not checked. The kind check is
done for all tags in the resolution phase as part of resolve_tag. Most
tags are checked for BT_CHARACTER type during matching, and again
checked for their "tag->type" in resolve_tag (necessarily also
BT_CHARACTER for applicable tags) at resolution time. The duplication
is silly, and these sorts of checks should really be done as part of
resolution rather than matching, since the errors do not affect
_whether_ the statement is matched. Regardless of the constraints
violations, the statement is already unambiguously matched.

Anyway, reorganizing the constraints checking from the matching phase
to the resolution phase goes surprisingly smoothly. However, I'd like
feedback on one issue I've encountered relating to this code snippet
from io.c (check_io_constraints):

 if (dt->asynchronous)
    {
      int num;
      static const char * asynchronous[] = { "YES", "NO", NULL };

      if (!gfc_reduce_init_expr (dt->asynchronous))
        {
          gfc_error ("ASYNCHRONOUS= specifier at %L must be an initialization "
                     "expression", &dt->asynchronous->where);
          return MATCH_ERROR;
        }
[...]

This code was in the matching phase (match_io), but is now called
during the resolution phase (gfc_resolve_dt). One difference between
the two phases is that errors are usually buffered during matching,
but not resolution. The difference can be seen by running the testcase
write_check4.f90, which covers this particular code:

$ tail -n10 write_check4.f90
! Contributed by Tobias Burnus <[hidden email]>
!
  character(2) :: no
  no = "no"
  open (unit=10, asynchronous = no)              ! Ok, it isn't a transfer stmt
  write(*,*, asynchronous="Y"//"E"//trim("S  ")) ! Ok, it is an init expr
  write(*,*, asynchronous=no)  ! { dg-error "must be an initialization
expression" }
  read (*,*, asynchronous="Y"//"e"//trim("S  "))
  read (*,*, asynchronous=no)  ! { dg-error "must be an initialization
expression" }
end

$ gfortran write_check4.f90
write_check4.f90:14:26:

   14 |   write(*,*, asynchronous=no)  ! { dg-error "must be an
initialization expression" }
      |                          1
Error: Parameter ‘no’ at (1) has not been declared or is a variable,
which does not reduce to a constant expression
write_check4.f90:14:26:

   14 |   write(*,*, asynchronous=no)  ! { dg-error "must be an
initialization expression" }
      |                          1
Error: ASYNCHRONOUS= specifier at (1) must be an initialization expression
write_check4.f90:16:26:

   16 |   read (*,*, asynchronous=no)  ! { dg-error "must be an
initialization expression" }
      |                          1
Error: Parameter ‘no’ at (1) has not been declared or is a variable,
which does not reduce to a constant expression
write_check4.f90:16:26:

   16 |   read (*,*, asynchronous=no)  ! { dg-error "must be an
initialization expression" }
      |                          1
Error: ASYNCHRONOUS= specifier at (1) must be an initialization expression


What we see here is that gfc_reduce_init_expr issues an error as to
why the tag value is not a valid initialization expression, followed
by the explicit error for the ASYNCHRONOUS tag. Previously, the error
from gfc_reduce_init_expr was buffered during matching and then
overwritten with the ASYNCHRONOUS error. After moving this code to the
resolution phase, both errors are flushed and show immediately.

My question is this: should I allow both errors to appear, or suppress
one of them? Which one? My gut reaction was to match the current
behavior and suppress the error issued by gfc_reduce_init_expr. Upon a
second thought, I think perhaps it would be helpful to see _why_ the
expression is not an initialization expression, not merely
"ASYNCHRONOUS must be an initialization expression". Thus I can see a
valid argument for any of the three {0|1, 1|0, 1|1} choices. One can
consider other examples which would issue descriptive errors for why
the expression is not an initialization expression. For example, if we
replace "asynchronous=no" with a function call, like
"asynchronous=func()", we see errors like:

   27 | read (*,*, asynchronous=func())  ! { dg-error "must be an
initialization expression" }
      |                        1
Error: Function ‘func’ in initialization expression at (1) must be an
intrinsic function

Thoughts?

---
Fritz Reese