re PR fortran/45776 (Full implementation of variable definition contexts (and related...
authorDaniel Kraft <d@domob.eu>
Sat, 25 Sep 2010 14:27:20 +0000 (16:27 +0200)
committerDaniel Kraft <domob@gcc.gnu.org>
Sat, 25 Sep 2010 14:27:20 +0000 (16:27 +0200)
2010-09-25  Daniel Kraft  <d@domob.eu>

PR fortran/45776
* gfortran.h (struct gfc_dt): New member `dt_io_kind'.
* io.c (resolve_tag): F2008 check for NEWUNIT and variable
definition checks for NEWUNIT, IOSTAT, SIZE and IOMSG.
(gfc_free_dt): Correctly handle freeing of `dt_io_kind' and
`extra_comma' with changed semantics.
(gfc_resolve_dt): Check variable definitions.
(match_io_element): Remove INTENT and PURE checks here and
initialize code->ext.dt member.
(match_io): Set dt->dt_io_kind.
(gfc_resolve_inquire): Check variable definition for all tags
except UNIT, FILE and ID.
* resolve.c (resolve_transfer): Variable definition check.

2010-09-25  Daniel Kraft  <d@domob.eu>

PR fortran/45776
* gfortran.dg/io_constraints_6.f03: New test.
* gfortran.dg/io_constraints_7.f03: New test.
* gfortran.dg/newunit_2.f90: New test.

From-SVN: r164619

gcc/fortran/ChangeLog
gcc/fortran/gfortran.h
gcc/fortran/io.c
gcc/fortran/resolve.c
gcc/testsuite/ChangeLog
gcc/testsuite/gfortran.dg/io_constraints_6.f03 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/io_constraints_7.f03 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/newunit_2.f90 [new file with mode: 0644]

index 5df77bf70f9488194d99a7d26247d7dead3140cc..40b472080cc59687b987c6d1876b2b094618c753 100644 (file)
@@ -1,3 +1,19 @@
+2010-09-25  Daniel Kraft  <d@domob.eu>
+
+       PR fortran/45776
+       * gfortran.h (struct gfc_dt): New member `dt_io_kind'.
+       * io.c (resolve_tag): F2008 check for NEWUNIT and variable
+       definition checks for NEWUNIT, IOSTAT, SIZE and IOMSG.
+       (gfc_free_dt): Correctly handle freeing of `dt_io_kind' and
+       `extra_comma' with changed semantics.
+       (gfc_resolve_dt): Check variable definitions.
+       (match_io_element): Remove INTENT and PURE checks here and
+       initialize code->ext.dt member.
+       (match_io): Set dt->dt_io_kind.
+       (gfc_resolve_inquire): Check variable definition for all tags
+       except UNIT, FILE and ID.
+       * resolve.c (resolve_transfer): Variable definition check.
+
 2010-09-25  Tobias Burnus  <burnus@net-b.de>
 
        * interface.c (gfc_match_end_interface): Constify char pointer
index 95886cd2c9ac5ba45c15b696a65efb55776f486d..b9c79f26878f764eb98648e4b689208a89eb7425 100644 (file)
@@ -2000,7 +2000,7 @@ typedef struct
 {
   gfc_expr *io_unit, *format_expr, *rec, *advance, *iostat, *size, *iomsg,
           *id, *pos, *asynchronous, *blank, *decimal, *delim, *pad, *round,
-          *sign, *extra_comma;
+          *sign, *extra_comma, *dt_io_kind;
 
   gfc_symbol *namelist;
   /* A format_label of `format_asterisk' indicates the "*" format */
index afbde0210b4618d153def20be256d2fb1cb129b0..e80202fab06273cd8fd031a11f495c40bc573353 100644 (file)
@@ -1505,13 +1505,31 @@ resolve_tag (const io_tag *tag, gfc_expr *e)
        return FAILURE;
     }
 
+  if (tag == &tag_newunit)
+    {
+      if (gfc_notify_std (GFC_STD_F2008, "Fortran 2008: NEWUNIT specifier"
+                         " at %L", &e->where) == FAILURE)
+       return FAILURE;
+    }
+
+  /* NEWUNIT, IOSTAT, SIZE and IOMSG are variable definition contexts.  */
+  if (tag == &tag_newunit || tag == &tag_iostat
+      || tag == &tag_size || tag == &tag_iomsg)
+    {
+      char context[64];
+
+      sprintf (context, _("%s tag"), tag->name);
+      if (gfc_check_vardef_context (e, false, context) == FAILURE)
+       return FAILURE;
+    }
+  
   if (tag == &tag_convert)
     {
       if (gfc_notify_std (GFC_STD_GNU, "Extension: CONVERT tag at %L",
                          &e->where) == FAILURE)
        return FAILURE;
     }
-  
+
   return SUCCESS;
 }
 
@@ -2707,8 +2725,9 @@ gfc_free_dt (gfc_dt *dt)
   gfc_free_expr (dt->round);
   gfc_free_expr (dt->blank);
   gfc_free_expr (dt->decimal);
-  gfc_free_expr (dt->extra_comma);
   gfc_free_expr (dt->pos);
+  gfc_free_expr (dt->dt_io_kind);
+  /* dt->extra_comma is a link to dt_io_kind if it is set.  */
   gfc_free (dt);
 }
 
@@ -2719,6 +2738,11 @@ gfc_try
 gfc_resolve_dt (gfc_dt *dt, locus *loc)
 {
   gfc_expr *e;
+  io_kind k;
+
+  /* This is set in any case.  */
+  gcc_assert (dt->dt_io_kind);
+  k = dt->dt_io_kind->value.iokind;
 
   RESOLVE_TAG (&tag_format, dt->format_expr);
   RESOLVE_TAG (&tag_rec, dt->rec);
@@ -2761,16 +2785,13 @@ gfc_resolve_dt (gfc_dt *dt, locus *loc)
             type character, we assume its really the "format" form of the I/O
             statement.  We set the io_unit to the default unit and format to
             the character expression.  See F95 Standard section 9.4.  */
-         io_kind k;
-         k = dt->extra_comma->value.iokind;
          if (e->ts.type == BT_CHARACTER && (k == M_READ || k == M_PRINT))
            {
              dt->format_expr = dt->io_unit;
              dt->io_unit = default_unit (k);
 
-             /* Free this pointer now so that a warning/error is not triggered
-                below for the "Extension".  */
-             gfc_free_expr (dt->extra_comma);
+             /* Nullify this pointer now so that a warning/error is not
+                triggered below for the "Extension".  */
              dt->extra_comma = NULL;
            }
 
@@ -2790,6 +2811,13 @@ gfc_resolve_dt (gfc_dt *dt, locus *loc)
          gfc_error ("Internal unit with vector subscript at %L", &e->where);
          return FAILURE;
        }
+
+      /* If we are writing, make sure the internal unit can be changed.  */
+      gcc_assert (k != M_PRINT);
+      if (k == M_WRITE
+         && gfc_check_vardef_context (e, false, _("internal unit in WRITE"))
+              == FAILURE)
+       return FAILURE;
     }
 
   if (e->rank && e->ts.type != BT_CHARACTER)
@@ -2801,10 +2829,36 @@ gfc_resolve_dt (gfc_dt *dt, locus *loc)
   if (e->expr_type == EXPR_CONSTANT && e->ts.type == BT_INTEGER
       && mpz_sgn (e->value.integer) < 0)
     {
-      gfc_error ("UNIT number in statement at %L must be non-negative", &e->where);
+      gfc_error ("UNIT number in statement at %L must be non-negative",
+                &e->where);
       return FAILURE;
     }
 
+  /* If we are reading and have a namelist, check that all namelist symbols
+     can appear in a variable definition context.  */
+  if (k == M_READ && dt->namelist)
+    {
+      gfc_namelist* n;
+      for (n = dt->namelist->namelist; n; n = n->next)
+       {
+         gfc_expr* e;
+         gfc_try t;
+
+         e = gfc_get_variable_expr (gfc_find_sym_in_symtree (n->sym));
+         t = gfc_check_vardef_context (e, false, NULL);
+         gfc_free_expr (e);
+
+         if (t == FAILURE)
+           {
+             gfc_error ("NAMELIST '%s' in READ statement at %L contains"
+                        " the symbol '%s' which may not appear in a"
+                        " variable definition context",
+                        dt->namelist->name, loc, n->sym->name);
+             return FAILURE;
+           }
+       }
+    }
+
   if (dt->extra_comma
       && gfc_notify_std (GFC_STD_GNU, "Extension: Comma before i/o "
                         "item list at %L", &dt->extra_comma->where) == FAILURE)
@@ -2854,6 +2908,7 @@ gfc_resolve_dt (gfc_dt *dt, locus *loc)
                 &dt->format_label->where);
       return FAILURE;
     }
+
   return SUCCESS;
 }
 
@@ -3012,50 +3067,8 @@ match_io_element (io_kind k, gfc_code **cpp)
                   io_kind_name (k));
     }
 
-  if (m == MATCH_YES)
-    switch (k)
-      {
-      case M_READ:
-       if (expr->symtree->n.sym->attr.intent == INTENT_IN)
-         {
-           gfc_error ("Variable '%s' in input list at %C cannot be "
-                      "INTENT(IN)", expr->symtree->n.sym->name);
-           m = MATCH_ERROR;
-         }
-
-       if (gfc_pure (NULL)
-           && gfc_impure_variable (expr->symtree->n.sym)
-           && current_dt->io_unit
-           && current_dt->io_unit->ts.type == BT_CHARACTER)
-         {
-           gfc_error ("Cannot read to variable '%s' in PURE procedure at %C",
-                      expr->symtree->n.sym->name);
-           m = MATCH_ERROR;
-         }
-
-       if (gfc_check_do_variable (expr->symtree))
-         m = MATCH_ERROR;
-
-       break;
-
-      case M_WRITE:
-       if (current_dt->io_unit
-           && current_dt->io_unit->ts.type == BT_CHARACTER
-           && gfc_pure (NULL)
-           && current_dt->io_unit->expr_type == EXPR_VARIABLE
-           && gfc_impure_variable (current_dt->io_unit->symtree->n.sym))
-         {
-           gfc_error ("Cannot write to internal file unit '%s' at %C "
-                      "inside a PURE procedure",
-                      current_dt->io_unit->symtree->n.sym->name);
-           m = MATCH_ERROR;
-         }
-
-       break;
-
-      default:
-       break;
-      }
+  if (m == MATCH_YES && k == M_READ && gfc_check_do_variable (expr->symtree))
+    m = MATCH_ERROR;
 
   if (m != MATCH_YES)
     {
@@ -3066,6 +3079,7 @@ match_io_element (io_kind k, gfc_code **cpp)
   cp = gfc_get_code ();
   cp->op = EXEC_TRANSFER;
   cp->expr1 = expr;
+  cp->ext.dt = current_dt;
 
   *cpp = cp;
   return MATCH_YES;
@@ -3657,14 +3671,14 @@ get_io_list:
   /* Used in check_io_constraints, where no locus is available.  */
   spec_end = gfc_current_locus;
 
+  /* Save the IO kind for later use.  */
+  dt->dt_io_kind = gfc_get_iokind_expr (&gfc_current_locus, k);
+
   /* Optional leading comma (non-standard).  We use a gfc_expr structure here
      to save the locus.  This is used later when resolving transfer statements
      that might have a format expression without unit number.  */
   if (!comma_flag && gfc_match_char (',') == MATCH_YES)
-    {
-      /* Save the iokind and locus for later use in resolution.  */
-      dt->extra_comma = gfc_get_iokind_expr (&gfc_current_locus, k);
-    }
+    dt->extra_comma = dt->dt_io_kind;
 
   io_code = NULL;
   if (gfc_match_eos () != MATCH_YES)
@@ -3973,41 +3987,54 @@ gfc_resolve_inquire (gfc_inquire *inquire)
 {
   RESOLVE_TAG (&tag_unit, inquire->unit);
   RESOLVE_TAG (&tag_file, inquire->file);
-  RESOLVE_TAG (&tag_iomsg, inquire->iomsg);
-  RESOLVE_TAG (&tag_iostat, inquire->iostat);
-  RESOLVE_TAG (&tag_exist, inquire->exist);
-  RESOLVE_TAG (&tag_opened, inquire->opened);
-  RESOLVE_TAG (&tag_number, inquire->number);
-  RESOLVE_TAG (&tag_named, inquire->named);
-  RESOLVE_TAG (&tag_name, inquire->name);
-  RESOLVE_TAG (&tag_s_access, inquire->access);
-  RESOLVE_TAG (&tag_sequential, inquire->sequential);
-  RESOLVE_TAG (&tag_direct, inquire->direct);
-  RESOLVE_TAG (&tag_s_form, inquire->form);
-  RESOLVE_TAG (&tag_formatted, inquire->formatted);
-  RESOLVE_TAG (&tag_unformatted, inquire->unformatted);
-  RESOLVE_TAG (&tag_s_recl, inquire->recl);
-  RESOLVE_TAG (&tag_nextrec, inquire->nextrec);
-  RESOLVE_TAG (&tag_s_blank, inquire->blank);
-  RESOLVE_TAG (&tag_s_position, inquire->position);
-  RESOLVE_TAG (&tag_s_action, inquire->action);
-  RESOLVE_TAG (&tag_read, inquire->read);
-  RESOLVE_TAG (&tag_write, inquire->write);
-  RESOLVE_TAG (&tag_readwrite, inquire->readwrite);
-  RESOLVE_TAG (&tag_s_delim, inquire->delim);
-  RESOLVE_TAG (&tag_s_pad, inquire->pad);
-  RESOLVE_TAG (&tag_s_encoding, inquire->encoding);
-  RESOLVE_TAG (&tag_s_round, inquire->round);
-  RESOLVE_TAG (&tag_iolength, inquire->iolength);
-  RESOLVE_TAG (&tag_convert, inquire->convert);
-  RESOLVE_TAG (&tag_strm_out, inquire->strm_pos);
-  RESOLVE_TAG (&tag_s_async, inquire->asynchronous);
-  RESOLVE_TAG (&tag_s_sign, inquire->sign);
-  RESOLVE_TAG (&tag_s_round, inquire->round);
-  RESOLVE_TAG (&tag_pending, inquire->pending);
-  RESOLVE_TAG (&tag_size, inquire->size);
   RESOLVE_TAG (&tag_id, inquire->id);
 
+  /* For INQUIRE, all tags except FILE, ID and UNIT are variable definition
+     contexts.  Thus, use an extended RESOLVE_TAG macro for that.  */
+#define INQUIRE_RESOLVE_TAG(tag, expr) \
+  RESOLVE_TAG (tag, expr); \
+  if (expr) \
+    { \
+      char context[64]; \
+      sprintf (context, _("%s tag with INQUIRE"), (tag)->name); \
+      if (gfc_check_vardef_context ((expr), false, context) == FAILURE) \
+       return FAILURE; \
+    }
+  INQUIRE_RESOLVE_TAG (&tag_iomsg, inquire->iomsg);
+  INQUIRE_RESOLVE_TAG (&tag_iostat, inquire->iostat);
+  INQUIRE_RESOLVE_TAG (&tag_exist, inquire->exist);
+  INQUIRE_RESOLVE_TAG (&tag_opened, inquire->opened);
+  INQUIRE_RESOLVE_TAG (&tag_number, inquire->number);
+  INQUIRE_RESOLVE_TAG (&tag_named, inquire->named);
+  INQUIRE_RESOLVE_TAG (&tag_name, inquire->name);
+  INQUIRE_RESOLVE_TAG (&tag_s_access, inquire->access);
+  INQUIRE_RESOLVE_TAG (&tag_sequential, inquire->sequential);
+  INQUIRE_RESOLVE_TAG (&tag_direct, inquire->direct);
+  INQUIRE_RESOLVE_TAG (&tag_s_form, inquire->form);
+  INQUIRE_RESOLVE_TAG (&tag_formatted, inquire->formatted);
+  INQUIRE_RESOLVE_TAG (&tag_unformatted, inquire->unformatted);
+  INQUIRE_RESOLVE_TAG (&tag_s_recl, inquire->recl);
+  INQUIRE_RESOLVE_TAG (&tag_nextrec, inquire->nextrec);
+  INQUIRE_RESOLVE_TAG (&tag_s_blank, inquire->blank);
+  INQUIRE_RESOLVE_TAG (&tag_s_position, inquire->position);
+  INQUIRE_RESOLVE_TAG (&tag_s_action, inquire->action);
+  INQUIRE_RESOLVE_TAG (&tag_read, inquire->read);
+  INQUIRE_RESOLVE_TAG (&tag_write, inquire->write);
+  INQUIRE_RESOLVE_TAG (&tag_readwrite, inquire->readwrite);
+  INQUIRE_RESOLVE_TAG (&tag_s_delim, inquire->delim);
+  INQUIRE_RESOLVE_TAG (&tag_s_pad, inquire->pad);
+  INQUIRE_RESOLVE_TAG (&tag_s_encoding, inquire->encoding);
+  INQUIRE_RESOLVE_TAG (&tag_s_round, inquire->round);
+  INQUIRE_RESOLVE_TAG (&tag_iolength, inquire->iolength);
+  INQUIRE_RESOLVE_TAG (&tag_convert, inquire->convert);
+  INQUIRE_RESOLVE_TAG (&tag_strm_out, inquire->strm_pos);
+  INQUIRE_RESOLVE_TAG (&tag_s_async, inquire->asynchronous);
+  INQUIRE_RESOLVE_TAG (&tag_s_sign, inquire->sign);
+  INQUIRE_RESOLVE_TAG (&tag_s_round, inquire->round);
+  INQUIRE_RESOLVE_TAG (&tag_pending, inquire->pending);
+  INQUIRE_RESOLVE_TAG (&tag_size, inquire->size);
+#undef INQUIRE_RESOLVE_TAG
+
   if (gfc_reference_st_label (inquire->err, ST_LABEL_TARGET) == FAILURE)
     return FAILURE;
 
index 30ca7ce2181cfd11e2a7d0a7db9e3f713a86e9da..0dce3f86b180d12c2c5f0ea86de5495a840d2063 100644 (file)
@@ -7916,6 +7916,13 @@ resolve_transfer (gfc_code *code)
                      && exp->expr_type != EXPR_FUNCTION))
     return;
 
+  /* If we are reading, the variable will be changed.  Note that
+     code->ext.dt may be NULL if the TRANSFER is related to
+     an INQUIRE statement -- but in this case, we are not reading, either.  */
+  if (code->ext.dt && code->ext.dt->dt_io_kind->value.iokind == M_READ
+      && gfc_check_vardef_context (exp, false, _("item in READ")) == FAILURE)
+    return;
+
   sym = exp->symtree->n.sym;
   ts = &sym->ts;
 
index 3815b943618ec9f0a8c86847757082af1f369254..6a65c793d11b428180d3ddf3d9b183df242de2da 100644 (file)
@@ -1,3 +1,10 @@
+2010-09-25  Daniel Kraft  <d@domob.eu>
+
+       PR fortran/45776
+       * gfortran.dg/io_constraints_6.f03: New test.
+       * gfortran.dg/io_constraints_7.f03: New test.
+       * gfortran.dg/newunit_2.f90: New test.
+
 2010-09-24  Steven G. Kargl  < kargl@gcc.gnu.org>
 
        * testsuite/gfortran.dg/operator_c1202.f90: New test.
diff --git a/gcc/testsuite/gfortran.dg/io_constraints_6.f03 b/gcc/testsuite/gfortran.dg/io_constraints_6.f03
new file mode 100644 (file)
index 0000000..d0484f5
--- /dev/null
@@ -0,0 +1,40 @@
+! { dg-do compile }
+
+! PR fortran/45776
+! Variable definition context checks related to IO.
+
+! Contributed by Daniel Kraft, d@domob.eu.
+
+module m
+  implicit none
+
+  integer, protected :: a
+  character(len=128), protected :: str
+end module m
+
+program main
+  use :: m
+  integer, parameter :: b = 42
+  integer :: x
+  character(len=128) :: myStr
+
+  namelist /definable/ x, myStr
+  namelist /undefinable/ x, a
+
+  ! These are invalid.
+  read (myStr, *) a ! { dg-error "variable definition context" }
+  read (myStr, *) x, b ! { dg-error "variable definition context" }
+  write (str, *) 5 ! { dg-error "variable definition context" }
+  read (*, nml=undefinable) ! { dg-error "contains the symbol 'a' which may not" }
+
+  ! These are ok.
+  read (str, *) x
+  write (myStr, *) a
+  write (myStr, *) b
+  print *, a, b
+  write (*, nml=undefinable)
+  read (*, nml=definable)
+  write (*, nml=definable)
+end program main
+
+! { dg-final { cleanup-modules "m" } }
diff --git a/gcc/testsuite/gfortran.dg/io_constraints_7.f03 b/gcc/testsuite/gfortran.dg/io_constraints_7.f03
new file mode 100644 (file)
index 0000000..4d18491
--- /dev/null
@@ -0,0 +1,37 @@
+! { dg-do compile }
+
+! PR fortran/45776
+! Variable definition context checks related to IO.
+
+! Contributed by Daniel Kraft, d@domob.eu.
+
+module m
+  implicit none
+  integer, protected :: a
+  character(len=128), protected :: msg
+end module m
+
+program main
+  use :: m
+  integer :: x
+  logical :: bool
+
+  write (*, iostat=a) 42 ! { dg-error "variable definition context" }
+  write (*, iomsg=msg) 42 ! { dg-error "variable definition context" }
+  read (*, '(I2)', advance='no', size=a) x ! { dg-error "variable definition context" }
+
+  ! These are ok.
+  inquire (unit=a)
+  inquire (file=msg, id=a, pending=bool)
+  inquire (file=msg)
+
+  ! These not, but list is not extensive.
+  inquire (unit=1, number=a) ! { dg-error "variable definition context" }
+  inquire (unit=1, encoding=msg) ! { dg-error "variable definition context" }
+  inquire (unit=1, formatted=msg) ! { dg-error "variable definition context" }
+
+  open (newunit=a, file="foo") ! { dg-error "variable definition context" }
+  close (unit=a)
+end program main
+
+! { dg-final { cleanup-modules "m" } }
diff --git a/gcc/testsuite/gfortran.dg/newunit_2.f90 b/gcc/testsuite/gfortran.dg/newunit_2.f90
new file mode 100644 (file)
index 0000000..b0f797a
--- /dev/null
@@ -0,0 +1,15 @@
+! { dg-do compile }
+! { dg-options "-std=f95" }
+
+! PR40008 F2008: Add NEWUNIT= for OPEN statement 
+! Check for rejection with pre-F2008 standard.
+
+! Contributed by Daniel Kraft, d@domob.eu.
+
+program main
+  character(len=25) :: str
+  integer(1) :: myunit
+
+  open (newunit=myunit, file="some_file") ! { dg-error "Fortran 2008" }
+  close (unit=myunit)
+end program main