re PR fortran/48298 ([F03] User-Defined Derived-Type IO (DTIO))
authorJerry DeLisle <jvdelisle@gcc.gnu.org>
Fri, 23 Sep 2016 20:36:21 +0000 (20:36 +0000)
committerJerry DeLisle <jvdelisle@gcc.gnu.org>
Fri, 23 Sep 2016 20:36:21 +0000 (20:36 +0000)
2016-09-23  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

PR libgfortran/48298
* io/inquire.c (inquire_via_unit): Adjust error check for the
two possible internal unit KINDs.
* io/io.h: Adjust defines for is_internal_unit and
is_char4_unit. (gfc_unit): Add internal unit data to structure.
(get_internal_unit): Change declaration to set_internal_unit.
(free_internal_unit): Change name to stash_internal_unit_number.
(get_unique_unit_number): Adjust parameter argument.
Define IOPARM_DT_HAS_UDTIO. (gfc_saved_unit): New structure.
* io/list_read.c (next_char_internal): Use is_char4_unit.
* io/open.c (st_open): Adjust call to get_unique_unit_number.
* io/transfer.c (write_block): Use is_char4_unit.
(data_transfer_init): Update check for unit numbers.
(st_read_done): Free the various allocated memories used for the
internal units and stash the negative unit number and pointer to unit
structure to allow reuse. (st_write_done): Likewise stash the freed
unit.
* io/unit.c: Create a fixed size buffer of 16 gfc_saved_unit's to use
as a stack to save newunit unit numbers and unit structure for reuse.
(get_external_unit): Change name to get_gfc_unit to better
reflect what it does. (find_unit): Change call to get_gfc_unit.
(find_or_create_unit): Likewise. (get_internal_unit): Change
name to set_internal_unit. Move internal unit from the dtp
structure to the gfc_unit structure so that it can be passed to
child I/O statements through the UNIT.
(free_internal_unit): Change name to stash_internal_unit_number.
Push the common.unit number onto the newunit stack, saving it
for possible reuse later. (get_unit): Set the internal unit
KIND. Use get_unique_unit_number to get a negative unit number
for the internal unit. Use get_gfc_unit to get the unit structure
and use set_internal_unit to initialize it.
(init_units): Initialize the newunit stack.
(get_unique_unit_number): Check the stack for an available unit
number and use it. If none there get the next most negative
number. (close_units): Free any unit structures pointed to from the save
stack.

2016-09-23  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

PR fortran/48298
* gfortran.h (gfc_dt): Add *udtio.
* ioparm.def: Add bit IOPARM_dt_f2003 to align with library use of bit
25. Add IOPARM_dt_dtio bit to common flags.
* resolve.c (resolve_transfer): Set dt->udtio to expression.
* io.c (gfc_match_inquire): Adjust error message for internal
unit KIND.
* libgfortran.h: Adjust defines for GFC_INTERNAL_UNIT4,
GFC_INTERNAL_UNIT, and GFC_INVALID_UNIT.
* trans-io.c (build_dt): Set common_unit to reflect the KIND of
the internal unit. Set mask bit for presence of dt->udtio.

2016-09-23  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

PR fortran/48298
* gfortran.dg/negative_unit_check.f90: Update test.
* gfortran.dg/dtio_14.f90: New test.

From-SVN: r240456

17 files changed:
gcc/fortran/ChangeLog
gcc/fortran/gfortran.h
gcc/fortran/io.c
gcc/fortran/ioparm.def
gcc/fortran/libgfortran.h
gcc/fortran/resolve.c
gcc/fortran/trans-io.c
gcc/testsuite/ChangeLog
gcc/testsuite/gfortran.dg/dtio_14.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/negative_unit_check.f90
libgfortran/ChangeLog
libgfortran/io/inquire.c
libgfortran/io/io.h
libgfortran/io/list_read.c
libgfortran/io/open.c
libgfortran/io/transfer.c
libgfortran/io/unit.c

index 7a99c39986f2ad606d5d1fe534fa38fead97e38f..daed721dbadfcbed50ad45ed6d1b1edbbd5313e9 100644 (file)
@@ -1,3 +1,17 @@
+2016-09-23  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
+
+       PR fortran/48298
+       * gfortran.h (gfc_dt): Add *udtio.
+       * ioparm.def: Add bit IOPARM_dt_f2003 to align with library use of bit
+       25. Add IOPARM_dt_dtio bit to common flags.
+       * resolve.c (resolve_transfer): Set dt->udtio to expression.
+       * io.c (gfc_match_inquire): Adjust error message for internal
+       unit KIND.
+       * libgfortran.h: Adjust defines for GFC_INTERNAL_UNIT4,
+       GFC_INTERNAL_UNIT, and GFC_INVALID_UNIT.
+       * trans-io.c (build_dt): Set common_unit to reflect the KIND of
+       the internal unit. Set mask bit for presence of dt->udtio.
+
 2016-09-22  Andre Vehreschild  <vehre@gcc.gnu.org>
 
        * trans-intrinsic.c (gfc_conv_intrinsic_caf_get): Use the old caf-
index 912f5fb368223ac0784f1cce3db8c94b541b807e..1837a53ddb8180b82dd75dfe3ae7b855e179769c 100644 (file)
@@ -2332,7 +2332,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, *dt_io_kind;
+          *sign, *extra_comma, *dt_io_kind, *udtio;
 
   gfc_symbol *namelist;
   /* A format_label of `format_asterisk' indicates the "*" format */
index 53037e22a1bb4738d7d6ab01b644a517a0a6513d..48c15ef55f9bfbb63b0b49ce16e55792a10c0773 100644 (file)
@@ -4256,9 +4256,11 @@ gfc_match_inquire (void)
 
   if (inquire->unit != NULL && inquire->unit->expr_type == EXPR_CONSTANT
       && inquire->unit->ts.type == BT_INTEGER
-      && mpz_get_si (inquire->unit->value.integer) == GFC_INTERNAL_UNIT)
+      && ((mpz_get_si (inquire->unit->value.integer) == GFC_INTERNAL_UNIT4)
+      || (mpz_get_si (inquire->unit->value.integer) == GFC_INTERNAL_UNIT)))
     {
-      gfc_error ("UNIT number in INQUIRE statement at %L can not be -1", &loc);
+      gfc_error ("UNIT number in INQUIRE statement at %L can not "
+                "be %d", &loc, (int) mpz_get_si (inquire->unit->value.integer));
       goto cleanup;
     }
 
index e448a9212794fd7c062b0acfa2ffca7eb06825f9..17b7ac78818878f3aa7979797882c2c5047873af 100644 (file)
@@ -113,3 +113,5 @@ IOPARM (dt,      delim,             1 << 21, char2)
 IOPARM (dt,      pad,          1 << 22, char1)
 IOPARM (dt,      round,                1 << 23, char2)
 IOPARM (dt,      sign,         1 << 24, char1)
+#define IOPARM_dt_f2003                      (1 << 25)
+#define IOPARM_dt_dtio               (1 << 26)
index e913250636700a3ebca5caffc3bbeee16c40c338..cc355086caea23240c4621d69b4d2e7a7e491af0 100644 (file)
@@ -68,10 +68,11 @@ along with GCC; see the file COPYING3.  If not see
                                | GFC_RTCHECK_RECURSION | GFC_RTCHECK_DO \
                                | GFC_RTCHECK_POINTER | GFC_RTCHECK_MEM)
 
-/* Special unit numbers used to convey certain conditions.  Numbers -3
+/* Special unit numbers used to convey certain conditions.  Numbers -4
    thru -9 available.  NEWUNIT values start at -10.  */
-#define GFC_INTERNAL_UNIT -1
-#define GFC_INVALID_UNIT  -2
+#define GFC_INTERNAL_UNIT4 -1    /* KIND=4 Internal Unit.  */
+#define GFC_INTERNAL_UNIT  -2    /* KIND=1 Internal Unit.  */
+#define GFC_INVALID_UNIT   -3
 
 /* Possible values for the CONVERT I/O specifier.  */
 /* Keep in sync with GFC_FLAG_CONVERT_* in gcc/flags.h.  */
index 11b6a14824b81ce6b6b3303922ebbc18ce449433..9998302714a8beacf5b255daf82154cd434eedbc 100644 (file)
@@ -8739,6 +8739,7 @@ resolve_transfer (gfc_code *code)
 
       if (dtio_sub != NULL && exp->expr_type == EXPR_VARIABLE)
        {
+         dt->udtio = exp;
          sym = exp->symtree->n.sym->ns->proc_name;
          /* Check to see if this is a nested DTIO call, with the
             dummy as the io-list object.  */
index 2c843497295703fd9d753a1a3a658a09d2619603..c0559f3623720f6f138330c1bdd9bdf2456a94b1 100644 (file)
@@ -1808,7 +1808,8 @@ build_dt (tree function, gfc_code * code)
          mask |= set_internal_unit (&block, &post_iu_block,
                                     var, dt->io_unit);
          set_parameter_const (&block, var, IOPARM_common_unit,
-                              dt->io_unit->ts.kind == 1 ? 0 : -1);
+                              dt->io_unit->ts.kind == 1 ?
+                               GFC_INTERNAL_UNIT : GFC_INTERNAL_UNIT4);
        }
     }
   else
@@ -1892,6 +1893,9 @@ build_dt (tree function, gfc_code * code)
        mask |= set_parameter_ref (&block, &post_end_block, var,
                                   IOPARM_dt_size, dt->size);
 
+      if (dt->udtio)
+       mask |= IOPARM_dt_dtio;
+
       if (dt->namelist)
        {
          if (dt->format_expr || dt->format_label)
index 6ac5436fc2c2c07e5849512516d650feb5c97f9d..09b6599c43a1249e43b1c76c2823c294d272b674 100644 (file)
@@ -1,3 +1,9 @@
+2016-09-23  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
+
+       PR fortran/48298
+       * gfortran.dg/negative_unit_check.f90: Update test.
+       * gfortran.dg/dtio_14.f90: New test.
+  
 2016-09-23  Dominik Vogt  <vogt@linux.vnet.ibm.com>
 
        * gcc.target/s390/hotpatch-compile-1.c: Fixed dg-error test.
diff --git a/gcc/testsuite/gfortran.dg/dtio_14.f90 b/gcc/testsuite/gfortran.dg/dtio_14.f90
new file mode 100644 (file)
index 0000000..16d5b1e
--- /dev/null
@@ -0,0 +1,64 @@
+! { dg-do run }
+!
+! Functional test of User Defined Derived Type IO with typebound bindings
+! This version tests IO to internal character units.
+!
+MODULE p
+  TYPE :: person
+    CHARACTER (LEN=20) :: name
+    INTEGER(4) :: age
+    CONTAINS
+      procedure :: pwf
+      procedure :: prf
+      GENERIC :: WRITE(FORMATTED) => pwf
+      GENERIC :: READ(FORMATTED) => prf
+  END TYPE person
+CONTAINS
+  SUBROUTINE pwf (dtv,unit,iotype,vlist,iostat,iomsg)
+    CLASS(person), INTENT(IN) :: dtv
+    INTEGER, INTENT(IN) :: unit
+    CHARACTER (LEN=*), INTENT(IN) :: iotype
+    INTEGER, INTENT(IN) :: vlist(:)
+    INTEGER, INTENT(OUT) :: iostat
+    CHARACTER (LEN=*), INTENT(INOUT) :: iomsg
+    WRITE(unit, FMT = *, IOSTAT=iostat) dtv%name, dtv%age
+  END SUBROUTINE pwf
+
+  SUBROUTINE prf (dtv,unit,iotype,vlist,iostat,iomsg)
+    CLASS(person), INTENT(INOUT) :: dtv
+    INTEGER, INTENT(IN) :: unit
+    CHARACTER (LEN=*), INTENT(IN) :: iotype
+    INTEGER, INTENT(IN) :: vlist(:)
+    INTEGER, INTENT(OUT) :: iostat
+    CHARACTER (LEN=*), INTENT(INOUT) :: iomsg
+    READ (UNIT = UNIT, FMT = *) dtv%name, dtv%age
+  END SUBROUTINE prf
+END MODULE p
+
+PROGRAM test
+  USE p
+  TYPE (person) :: chairman, answer
+  character(kind=1,len=80) :: str1
+  character(kind=4,len=80) :: str4
+  str1 = ""
+  str4 = 4_""
+  chairman%name="Charlie"
+  chairman%age=62
+  answer = chairman
+! KIND=1 test
+  write (str1, *) chairman
+  if (trim(str1).ne."  Charlie                       62") call abort
+  chairman%name="Bogus"
+  chairman%age=99
+  read (str1, *) chairman
+  if (chairman%name.ne.answer%name) call abort
+  if (chairman%age.ne.answer%age) call abort
+! KIND=4 test
+  write (str4, *) chairman
+  if (trim(str4).ne.4_"  Charlie                       62") call abort
+  chairman%name="Bogus"
+  chairman%age=99
+  read (str4, *) chairman
+  if (chairman%name.ne.answer%name) call abort
+  if (chairman%age.ne.answer%age) call abort
+END PROGRAM test
index 2a1b16c1093a4e118dca6c4816b0a2a89700b9ea..002b5b4ac8279b6114762d3c473525c9be704305 100644 (file)
@@ -2,4 +2,5 @@
 !  Test case from PR61933.
    LOGICAL :: file_exists
    INQUIRE(UNIT=-1,EXIST=file_exists)! { dg-error "can not be -1" }
+   INQUIRE(UNIT=-2,EXIST=file_exists)! { dg-error "can not be -2" }
 END
index 3edd9eda2d5a446cc88f65392fb5f2473f79ee64..f312a066c18da96206094b070da727c0fcd60152 100644 (file)
@@ -1,3 +1,42 @@
+2016-09-23  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
+
+       PR libgfortran/48298
+       * io/inquire.c (inquire_via_unit): Adjust error check for the
+       two possible internal unit KINDs.
+       * io/io.h: Adjust defines for is_internal_unit and
+       is_char4_unit. (gfc_unit): Add internal unit data to structure.
+       (get_internal_unit): Change declaration to set_internal_unit.
+       (free_internal_unit): Change name to stash_internal_unit_number.
+       (get_unique_unit_number): Adjust parameter argument.
+       Define IOPARM_DT_HAS_UDTIO. (gfc_saved_unit): New structure.
+       * io/list_read.c (next_char_internal): Use is_char4_unit.
+       * io/open.c (st_open): Adjust call to get_unique_unit_number.
+       * io/transfer.c (write_block): Use is_char4_unit.
+       (data_transfer_init): Update check for unit numbers.
+       (st_read_done): Free the various allocated memories used for the
+       internal units and stash the negative unit number and pointer to unit
+       structure to allow reuse. (st_write_done): Likewise stash the freed
+       unit.
+       * io/unit.c: Create a fixed size buffer of 16 gfc_saved_unit's to use
+       as a stack to save newunit unit numbers and unit structure for reuse.
+       (get_external_unit): Change name to get_gfc_unit to better
+       reflect what it does. (find_unit): Change call to get_gfc_unit.
+       (find_or_create_unit): Likewise. (get_internal_unit): Change
+       name to set_internal_unit. Move internal unit from the dtp
+       structure to the gfc_unit structure so that it can be passed to
+       child I/O statements through the UNIT.
+       (free_internal_unit): Change name to stash_internal_unit_number.
+       Push the common.unit number onto the newunit stack, saving it
+       for possible reuse later. (get_unit): Set the internal unit
+       KIND. Use get_unique_unit_number to get a negative unit number
+       for the internal unit. Use get_gfc_unit to get the unit structure
+       and use set_internal_unit to initialize it.
+       (init_units): Initialize the newunit stack.
+       (get_unique_unit_number): Check the stack for an available unit
+       number and use it. If none there get the next most negative
+       number. (close_units): Free any unit structures pointed to from the save
+       stack.
+
 2016-09-21  Janne Blomqvist  <jb@gcc.gnu.org>
 
        * intrinsics/random.c (getosrandom): Use rand_s() on
index ae5ba622592cd256c76afe6f169dfa2d7bfe2923..2bb518b69c7fdfeb5f3724f6b544dfb80be2e545 100644 (file)
@@ -41,7 +41,7 @@ inquire_via_unit (st_parameter_inquire *iqp, gfc_unit * u)
   const char *p;
   GFC_INTEGER_4 cf = iqp->common.flags;
 
-  if (iqp->common.unit == GFC_INTERNAL_UNIT)
+  if (iqp->common.unit == GFC_INTERNAL_UNIT || iqp->common.unit == GFC_INTERNAL_UNIT4)
     generate_error (&iqp->common, LIBERROR_INQUIRE_INTERNAL_UNIT, NULL);
 
   if ((cf & IOPARM_INQUIRE_HAS_EXIST) != 0)
index ff75741effda84083c09d873e6e3bd3e54440be9..87c35583754729dd424962573c52d5b97dacccff 100644 (file)
@@ -69,11 +69,11 @@ internal_proto(old_locale_lock);
 
 #define is_array_io(dtp) ((dtp)->internal_unit_desc)
 
-#define is_internal_unit(dtp) ((dtp)->u.p.unit_is_internal)
+#define is_internal_unit(dtp) ((dtp)->u.p.current_unit->internal_unit_kind)
 
 #define is_stream_io(dtp) ((dtp)->u.p.current_unit->flags.access == ACCESS_STREAM)
 
-#define is_char4_unit(dtp) ((dtp)->u.p.unit_is_internal && (dtp)->common.unit)
+#define is_char4_unit(dtp) ((dtp)->u.p.current_unit->internal_unit_kind == 4)
 
 /* The array_loop_spec contains the variables for the loops over index ranges
    that are encountered.  */
@@ -409,6 +409,7 @@ st_parameter_inquire;
 #define IOPARM_DT_HAS_ROUND                    (1 << 23)
 #define IOPARM_DT_HAS_SIGN                     (1 << 24)
 #define IOPARM_DT_HAS_F2003                     (1 << 25)
+#define IOPARM_DT_HAS_UDTIO                     (1 << 26)
 /* Internal use bit.  */
 #define IOPARM_DT_IONML_SET                    (1u << 31)
 
@@ -640,12 +641,24 @@ typedef struct gfc_unit
   int (*next_char_fn_ptr) (st_parameter_dt *);
   void (*push_char_fn_ptr) (st_parameter_dt *, int);
 
+  /* Internal unit char string data.  */
+  char * internal_unit;
+  gfc_charlen_type internal_unit_len;
+  gfc_array_char *string_unit_desc;
+  int internal_unit_kind;
+
   /* DTIO Parent/Child procedure, 0 = parent, >0 = child level.  */
   int child_dtio;
   int last_char;
 }
 gfc_unit;
 
+typedef struct gfc_saved_unit
+{
+  GFC_INTEGER_4 unit_number;
+  gfc_unit *unit;
+}
+gfc_saved_unit;
 
 /* unit.c */
 
@@ -663,11 +676,11 @@ internal_proto(unit_lock);
 extern int close_unit (gfc_unit *);
 internal_proto(close_unit);
 
-extern gfc_unit *get_internal_unit (st_parameter_dt *);
-internal_proto(get_internal_unit);
+extern gfc_unit *set_internal_unit (st_parameter_dt *, gfc_unit *, int);
+internal_proto(set_internal_unit);
 
-extern void free_internal_unit (st_parameter_dt *);
-internal_proto(free_internal_unit);
+extern void stash_internal_unit (st_parameter_dt *);
+internal_proto(stash_internal_unit);
 
 extern gfc_unit *find_unit (int);
 internal_proto(find_unit);
@@ -687,7 +700,7 @@ internal_proto (finish_last_advance_record);
 extern int unit_truncate (gfc_unit *, gfc_offset, st_parameter_common *);
 internal_proto (unit_truncate);
 
-extern GFC_INTEGER_4 get_unique_unit_number (st_parameter_open *);
+extern GFC_INTEGER_4 get_unique_unit_number (st_parameter_common *);
 internal_proto(get_unique_unit_number);
 
 /* open.c */
index a42f12b72692a360741ad21c74bb816e2de5e934..f258c9d92499b787adfb2592f8f07daac8e59768 100644 (file)
@@ -267,7 +267,7 @@ next_char_internal (st_parameter_dt *dtp)
 
   /* Get the next character and handle end-of-record conditions.  */
 
-  if (dtp->common.unit) /* Check for kind=4 internal unit.  */
+  if (is_char4_unit(dtp)) /* Check for kind=4 internal unit.  */
    length = sread (dtp->u.p.current_unit->s, &c, 1);
   else
    {
@@ -390,7 +390,7 @@ eat_spaces (st_parameter_dt *dtp)
       gfc_offset offset = stell (dtp->u.p.current_unit->s);
       gfc_offset i;
 
-      if (dtp->common.unit) /* kind=4 */
+      if (is_char4_unit(dtp)) /* kind=4 */
        {
          for (i = 0; i < dtp->u.p.current_unit->bytes_left; i++)
            {
index d159189818539783eb8717d2744e6b9cb0365921..d074b020d8113c8d858ce3e0cbcf3f874162b7eb 100644 (file)
@@ -812,7 +812,7 @@ st_open (st_parameter_open *opp)
   if ((opp->common.flags & IOPARM_LIBRETURN_MASK) == IOPARM_LIBRETURN_OK)
     {
       if ((opp->common.flags & IOPARM_OPEN_HAS_NEWUNIT))
-       opp->common.unit = get_unique_unit_number(opp);
+       opp->common.unit = get_unique_unit_number(&opp->common);
       else if (opp->common.unit < 0)
        {
          u = find_unit (opp->common.unit);
index 98072d0b889de12823c3f81ea3831a1aeaaa986a..6009c123d71b8f5c50bfa7b0ae1d678cac0aef18 100644 (file)
@@ -737,7 +737,7 @@ write_block (st_parameter_dt *dtp, int length)
 
   if (is_internal_unit (dtp))
     {
-      if (dtp->common.unit) /* char4 internel unit.  */
+      if (is_char4_unit(dtp)) /* char4 internel unit.  */
        {
          gfc_char4_t *dest4;
          dest4 = mem_alloc_w4 (dtp->u.p.current_unit->s, &length);
@@ -2606,7 +2606,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        st_parameter_open opp;
        unit_convert conv;
 
-      if (dtp->common.unit < 0)
+      if (dtp->common.unit < 0 && !is_internal_unit (dtp))
        {
          close_unit (dtp->u.p.current_unit);
          dtp->u.p.current_unit = NULL;
@@ -3943,18 +3943,34 @@ st_read_done (st_parameter_dt *dtp)
 {
   finalize_transfer (dtp);
 
-  if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
-    {
-      free_format_data (dtp->u.p.fmt);
-      free_format (dtp);
-    }
-
   free_ionml (dtp);
 
-  if (dtp->u.p.current_unit != NULL)
-    unlock_unit (dtp->u.p.current_unit);
-
-  free_internal_unit (dtp);
+  /* If this is a parent READ statement we do not need to retain the
+     internal unit structure for child use.  Free it and stash the unit
+     number for reuse.  */
+  if (dtp->u.p.current_unit != NULL
+      && dtp->u.p.current_unit->child_dtio == 0)
+    {
+      if (is_internal_unit (dtp) &&
+         (dtp->common.flags & IOPARM_DT_HAS_UDTIO) == 0)
+        {
+         free (dtp->u.p.current_unit->filename);
+         dtp->u.p.current_unit->filename = NULL;
+         free_format_hash_table (dtp->u.p.current_unit);
+         free (dtp->u.p.current_unit->s);
+         dtp->u.p.current_unit->s = NULL;
+         if (dtp->u.p.current_unit->ls)
+           free (dtp->u.p.current_unit->ls);
+         dtp->u.p.current_unit->ls = NULL;
+         stash_internal_unit (dtp);
+       }
+      if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
+       {
+         free_format_data (dtp->u.p.fmt);
+         free_format (dtp);
+       }
+      unlock_unit (dtp->u.p.current_unit);
+    }
 
   library_end ();
 }
@@ -3977,43 +3993,55 @@ st_write_done (st_parameter_dt *dtp)
 {
   finalize_transfer (dtp);
 
-  /* Deal with endfile conditions associated with sequential files.  */
-
   if (dtp->u.p.current_unit != NULL
-      && dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL
       && dtp->u.p.current_unit->child_dtio == 0)
-    switch (dtp->u.p.current_unit->endfile)
-      {
-      case AT_ENDFILE:         /* Remain at the endfile record.  */
-       break;
-
-      case AFTER_ENDFILE:
-       dtp->u.p.current_unit->endfile = AT_ENDFILE;    /* Just at it now.  */
-       break;
-
-      case NO_ENDFILE:
-       /* Get rid of whatever is after this record.  */
-        if (!is_internal_unit (dtp))
-          unit_truncate (dtp->u.p.current_unit,
-                         stell (dtp->u.p.current_unit->s),
-                         &dtp->common);
-       dtp->u.p.current_unit->endfile = AT_ENDFILE;
-       break;
-      }
-
-  if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
     {
-      free_format_data (dtp->u.p.fmt);
-      free_format (dtp);
-    }
+      /* Deal with endfile conditions associated with sequential files.  */
+      if (dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL)
+       switch (dtp->u.p.current_unit->endfile)
+         {
+         case AT_ENDFILE:              /* Remain at the endfile record.  */
+           break;
 
-  free_ionml (dtp);
+         case AFTER_ENDFILE:
+           dtp->u.p.current_unit->endfile = AT_ENDFILE; /* Just at it now.  */
+           break;
 
-  if (dtp->u.p.current_unit != NULL)
-    unlock_unit (dtp->u.p.current_unit);
+         case NO_ENDFILE:
+           /* Get rid of whatever is after this record.  */
+           if (!is_internal_unit (dtp))
+             unit_truncate (dtp->u.p.current_unit,
+                            stell (dtp->u.p.current_unit->s),
+                            &dtp->common);
+           dtp->u.p.current_unit->endfile = AT_ENDFILE;
+           break;
+         }
 
-  free_internal_unit (dtp);
+      free_ionml (dtp);
 
+      /* If this is a parent WRITE statement we do not need to retain the
+        internal unit structure for child use.  Free it and stash the
+        unit number for reuse.  */
+      if (is_internal_unit (dtp) &&
+         (dtp->common.flags & IOPARM_DT_HAS_UDTIO) == 0)
+       {
+         free (dtp->u.p.current_unit->filename);
+         dtp->u.p.current_unit->filename = NULL;
+         free_format_hash_table (dtp->u.p.current_unit);
+         free (dtp->u.p.current_unit->s);
+         dtp->u.p.current_unit->s = NULL;
+         if (dtp->u.p.current_unit->ls)
+           free (dtp->u.p.current_unit->ls);
+         dtp->u.p.current_unit->ls = NULL;
+         stash_internal_unit (dtp);
+       }
+      if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
+       {
+         free_format_data (dtp->u.p.fmt);
+         free_format (dtp);
+       }
+      unlock_unit (dtp->u.p.current_unit);
+    }
   library_end ();
 }
 
index fde9ac752d42cdc295ee55d173882909d0d76c2d..274b24b686eab0a18d476412604c7085f8bc2e76 100644 (file)
@@ -72,8 +72,15 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 /* Unit number to be assigned when NEWUNIT is used in an OPEN statement.  */
 #define GFC_FIRST_NEWUNIT -10
+#define NEWUNIT_STACK_SIZE 16
 static GFC_INTEGER_4 next_available_newunit = GFC_FIRST_NEWUNIT;
 
+/* A stack to save previously used newunit-assigned unit numbers to
+   allow them to be reused without reallocating the gfc_unit structure
+   which is still in the treap.  */
+static gfc_saved_unit newunit_stack[NEWUNIT_STACK_SIZE];
+static int newunit_tos = 0; /* Index to Top of Stack.  */
+
 #define CACHE_SIZE 3
 static gfc_unit *unit_cache[CACHE_SIZE];
 gfc_offset max_offset;
@@ -294,12 +301,12 @@ delete_unit (gfc_unit * old)
 }
 
 
-/* get_external_unit()-- Given an integer, return a pointer to the unit
+/* get_gfc_unit()-- Given an integer, return a pointer to the unit
  * structure.  Returns NULL if the unit does not exist,
  * otherwise returns a locked unit. */
 
 static gfc_unit *
-get_external_unit (int n, int do_create)
+get_gfc_unit (int n, int do_create)
 {
   gfc_unit *p;
   int c, created = 0;
@@ -361,6 +368,7 @@ found:
       inc_waiting_locked (p);
     }
 
+
   __gthread_mutex_unlock (&unit_lock);
 
   if (p != NULL && (p->child_dtio == 0))
@@ -384,14 +392,14 @@ found:
 gfc_unit *
 find_unit (int n)
 {
-  return get_external_unit (n, 0);
+  return get_gfc_unit (n, 0);
 }
 
 
 gfc_unit *
 find_or_create_unit (int n)
 {
-  return get_external_unit (n, 1);
+  return get_gfc_unit (n, 1);
 }
 
 
@@ -426,31 +434,14 @@ is_trim_ok (st_parameter_dt *dtp)
 
 
 gfc_unit *
-get_internal_unit (st_parameter_dt *dtp)
+set_internal_unit (st_parameter_dt *dtp, gfc_unit *iunit, int kind)
 {
-  gfc_unit * iunit;
   gfc_offset start_record = 0;
 
-  /* Allocate memory for a unit structure.  */
-
-  iunit = xcalloc (1, sizeof (gfc_unit));
-
-#ifdef __GTHREAD_MUTEX_INIT
-  {
-    __gthread_mutex_t tmp = __GTHREAD_MUTEX_INIT;
-    iunit->lock = tmp;
-  }
-#else
-  __GTHREAD_MUTEX_INIT_FUNCTION (&iunit->lock);
-#endif
-  __gthread_mutex_lock (&iunit->lock);
-
   iunit->recl = dtp->internal_unit_len;
-
-  /* For internal units we set the unit number to -1.
-     Otherwise internal units can be mistaken for a pre-connected unit or
-     some other file I/O unit.  */
-  iunit->unit_number = -1;
+  iunit->internal_unit = dtp->internal_unit;
+  iunit->internal_unit_len = dtp->internal_unit_len;
+  iunit->internal_unit_kind = kind;
 
   /* As an optimization, adjust the unit record length to not
      include trailing blanks. This will not work under certain conditions
@@ -458,14 +449,14 @@ get_internal_unit (st_parameter_dt *dtp)
   if (dtp->u.p.mode == READING && is_trim_ok (dtp))
     {
       int len;
-      if (dtp->common.unit == 0)
-         len = string_len_trim (dtp->internal_unit_len,
-                                                  dtp->internal_unit);
+      if (kind == 1)
+         len = string_len_trim (iunit->internal_unit_len,
+                                                  iunit->internal_unit);
       else
-         len = string_len_trim_char4 (dtp->internal_unit_len,
-                             (const gfc_char4_t*) dtp->internal_unit);
-      dtp->internal_unit_len = len;
-      iunit->recl = dtp->internal_unit_len;
+         len = string_len_trim_char4 (iunit->internal_unit_len,
+                             (const gfc_char4_t*) iunit->internal_unit);
+      iunit->internal_unit_len = len;
+      iunit->recl = iunit->internal_unit_len;
     }
 
   /* Set up the looping specification from the array descriptor, if any.  */
@@ -475,22 +466,19 @@ get_internal_unit (st_parameter_dt *dtp)
       iunit->rank = GFC_DESCRIPTOR_RANK (dtp->internal_unit_desc);
       iunit->ls = (array_loop_spec *)
        xmallocarray (iunit->rank, sizeof (array_loop_spec));
-      dtp->internal_unit_len *=
+      iunit->internal_unit_len *=
        init_loop_spec (dtp->internal_unit_desc, iunit->ls, &start_record);
 
       start_record *= iunit->recl;
     }
 
   /* Set initial values for unit parameters.  */
-  if (dtp->common.unit)
-    {
-      iunit->s = open_internal4 (dtp->internal_unit - start_record,
-                                dtp->internal_unit_len, -start_record);
-      fbuf_init (iunit, 256);
-    }
+  if (kind == 4)
+    iunit->s = open_internal4 (iunit->internal_unit - start_record,
+                                iunit->internal_unit_len, -start_record);
   else
-    iunit->s = open_internal (dtp->internal_unit - start_record,
-                             dtp->internal_unit_len, -start_record);
+    iunit->s = open_internal (iunit->internal_unit - start_record,
+                             iunit->internal_unit_len, -start_record);
 
   iunit->bytes_left = iunit->recl;
   iunit->last_record=0;
@@ -522,33 +510,22 @@ get_internal_unit (st_parameter_dt *dtp)
   dtp->u.p.pending_spaces = 0;
   dtp->u.p.max_pos = 0;
   dtp->u.p.at_eof = 0;
-
-  /* This flag tells us the unit is assigned to internal I/O.  */
-
-  dtp->u.p.unit_is_internal = 1;
-
   return iunit;
 }
 
 
-/* free_internal_unit()-- Free memory allocated for internal units if any.  */
+/* stash_internal_unit()-- Push the internal unit number onto the
+   avaialble stack.  */
 void
-free_internal_unit (st_parameter_dt *dtp)
+stash_internal_unit (st_parameter_dt *dtp)
 {
-  if (!is_internal_unit (dtp))
-    return;
-
-  if (unlikely (is_char4_unit (dtp)))
-    fbuf_destroy (dtp->u.p.current_unit);
-
-  if (dtp->u.p.current_unit != NULL)
-    {
-      free (dtp->u.p.current_unit->ls);
-
-      free (dtp->u.p.current_unit->s);
-
-      destroy_unit_mutex (dtp->u.p.current_unit);
-    }
+  __gthread_mutex_lock (&unit_lock);
+  newunit_tos++;
+  if (newunit_tos >= NEWUNIT_STACK_SIZE)
+    internal_error (&dtp->common, "stash_internal_unit(): Stack Size Exceeded");
+  newunit_stack[newunit_tos].unit_number = dtp->common.unit;
+  newunit_stack[newunit_tos].unit = dtp->u.p.current_unit;
+  __gthread_mutex_unlock (&unit_lock);
 }
 
 
@@ -559,16 +536,51 @@ free_internal_unit (st_parameter_dt *dtp)
 gfc_unit *
 get_unit (st_parameter_dt *dtp, int do_create)
 {
+  gfc_unit * unit;
 
   if ((dtp->common.flags & IOPARM_DT_HAS_INTERNAL_UNIT) != 0)
-    return get_internal_unit (dtp);
+    {
+      int kind;
+      if (dtp->common.unit == GFC_INTERNAL_UNIT)
+        kind = 1;
+      else if (dtp->common.unit == GFC_INTERNAL_UNIT4)
+        kind = 4;
+      else
+       internal_error (&dtp->common, "get_unit(): Bad internal unit KIND");
 
+      if ((dtp->common.flags & IOPARM_DT_HAS_UDTIO) != 0)
+       {
+         dtp->u.p.unit_is_internal = 1;
+         dtp->common.unit = get_unique_unit_number (&dtp->common);
+         unit = get_gfc_unit (dtp->common.unit, do_create);
+         set_internal_unit (dtp, unit, kind);
+         fbuf_init (unit, 128);
+         return unit;
+       }
+      else
+       {
+         if (newunit_tos)
+           {
+             dtp->common.unit = newunit_stack[newunit_tos].unit_number;
+             unit = newunit_stack[newunit_tos--].unit;
+             unit->fbuf->act = unit->fbuf->pos = 0;
+           }
+         else
+           {
+             dtp->common.unit = get_unique_unit_number (&dtp->common);
+             unit = xcalloc (1, sizeof (gfc_unit));
+             fbuf_init (unit, 128);
+           }
+         set_internal_unit (dtp, unit, kind);
+         return unit;
+       }
+    }
   /* Has to be an external unit.  */
-
   dtp->u.p.unit_is_internal = 0;
+  dtp->internal_unit = NULL;
   dtp->internal_unit_desc = NULL;
-
-  return get_external_unit (dtp->common.unit, do_create);
+  unit = get_gfc_unit (dtp->common.unit, do_create);
+  return unit;
 }
 
 
@@ -687,6 +699,10 @@ init_units (void)
   max_offset = 0;
   for (i = 0; i < sizeof (max_offset) * 8 - 1; i++)
     max_offset = max_offset + ((gfc_offset) 1 << i);
+
+  /* Initialize the newunit stack.  */
+  memset (newunit_stack, 0, NEWUNIT_STACK_SIZE * sizeof(gfc_saved_unit));
+  newunit_tos = 0;
 }
 
 
@@ -765,6 +781,13 @@ close_units (void)
     close_unit_1 (unit_root, 1);
   __gthread_mutex_unlock (&unit_lock);
 
+  while (newunit_tos != 0)
+    if (newunit_stack[newunit_tos].unit)
+      {
+       fbuf_destroy (newunit_stack[newunit_tos].unit);
+       free (newunit_stack[newunit_tos].unit->s);
+       free (newunit_stack[newunit_tos--].unit);
+      }
 #ifdef HAVE_FREELOCALE
   freelocale (c_locale);
 #endif
@@ -862,9 +885,10 @@ finish_last_advance_record (gfc_unit *u)
   fbuf_flush (u, u->mode);
 }
 
-/* Assign a negative number for NEWUNIT in OPEN statements.  */
+/* Assign a negative number for NEWUNIT in OPEN statements or for
+   internal units.  */
 GFC_INTEGER_4
-get_unique_unit_number (st_parameter_open *opp)
+get_unique_unit_number (st_parameter_common *common)
 {
   GFC_INTEGER_4 num;
 
@@ -875,11 +899,10 @@ get_unique_unit_number (st_parameter_open *opp)
   num = next_available_newunit--;
   __gthread_mutex_unlock (&unit_lock);
 #endif
-
   /* Do not allow NEWUNIT numbers to wrap.  */
   if (num > GFC_FIRST_NEWUNIT)
     {
-      generate_error (&opp->common, LIBERROR_INTERNAL, "NEWUNIT exhausted");
+      generate_error (common, LIBERROR_INTERNAL, "NEWUNIT exhausted");
       return 0;
     }
   return num;