re PR fortran/54730 (ICE in gfc_typenode_for_spec, at fortran/trans-types.c:1066)
authorMikael Morin <mikael@gcc.gnu.org>
Sun, 3 Mar 2013 17:34:42 +0000 (17:34 +0000)
committerMikael Morin <mikael@gcc.gnu.org>
Sun, 3 Mar 2013 17:34:42 +0000 (17:34 +0000)
fortran/
PR fortran/54730
* gfortran.h (struct gfc_undo_change_set): New field 'previous'.
(gfc_new_undo_checkpoint, gfc_drop_last_undo_checkpoint,
gfc_restore_last_undo_checkpoint): New prototypes.
* symbol.c (default_undo_chgset_var): Update initialization.
(single_undo_checkpoint_p, gfc_new_undo_checkpoint,
free_undo_change_set_data, pop_undo_change_set,
gfc_drop_last_undo_checkpoint, enforce_single_undo_checkpoint):
New functions.
(save_symbol_data): Handle multiple change sets.  Make sure old_symbol
field's previous value is not overwritten.  Clear gfc_new field.
(restore_old_symbol): Restore previous old_symbol field.
(gfc_restore_last_undo_checkpoint): New function, using body renamed
from gfc_undo_symbols.  Restore the previous change set as current one.
(gfc_undo_symbols): New body.
(gfc_commit_symbols, gfc_commit_symbol, gfc_enforce_clean_symbol_state):
Call enforce_single_undo_checkpoint.
(gfc_symbol_done_2): Ditto.  Free change set data.

From-SVN: r196414

gcc/fortran/ChangeLog
gcc/fortran/gfortran.h
gcc/fortran/symbol.c

index 49d9bdb..c9b0ca6 100644 (file)
@@ -1,5 +1,26 @@
 2013-03-03  Mikael Morin  <mikael@gcc.gnu.org>
 
+       PR fortran/54730
+       * gfortran.h (struct gfc_undo_change_set): New field 'previous'.
+       (gfc_new_undo_checkpoint, gfc_drop_last_undo_checkpoint,
+       gfc_restore_last_undo_checkpoint): New prototypes.
+       * symbol.c (default_undo_chgset_var): Update initialization.
+       (single_undo_checkpoint_p, gfc_new_undo_checkpoint,
+       free_undo_change_set_data, pop_undo_change_set,
+       gfc_drop_last_undo_checkpoint, enforce_single_undo_checkpoint):
+       New functions.
+       (save_symbol_data): Handle multiple change sets.  Make sure old_symbol
+       field's previous value is not overwritten.  Clear gfc_new field.
+       (restore_old_symbol): Restore previous old_symbol field.
+       (gfc_restore_last_undo_checkpoint): New function, using body renamed
+       from gfc_undo_symbols.  Restore the previous change set as current one.
+       (gfc_undo_symbols): New body.
+       (gfc_commit_symbols, gfc_commit_symbol, gfc_enforce_clean_symbol_state):
+       Call enforce_single_undo_checkpoint.
+       (gfc_symbol_done_2): Ditto.  Free change set data.
+
+2013-03-03  Mikael Morin  <mikael@gcc.gnu.org>
+
        * symbol.c (restore_old_symbol): Fix thinko.
 
 2013-03-03  Mikael Morin  <mikael@gcc.gnu.org>
index d6176db..18bbf79 100644 (file)
@@ -1281,6 +1281,7 @@ struct gfc_undo_change_set
 {
   vec<gfc_symbol *> syms;
   vec<gfc_typebound_proc *> tbps;
+  gfc_undo_change_set *previous;
 };
 
 
@@ -2641,6 +2642,9 @@ int gfc_get_sym_tree (const char *, gfc_namespace *, gfc_symtree **, bool);
 int gfc_get_ha_symbol (const char *, gfc_symbol **);
 int gfc_get_ha_sym_tree (const char *, gfc_symtree **);
 
+void gfc_new_undo_checkpoint (gfc_undo_change_set &);
+void gfc_drop_last_undo_checkpoint (void);
+void gfc_restore_last_undo_checkpoint (void);
 void gfc_undo_symbols (void);
 void gfc_commit_symbols (void);
 void gfc_commit_symbol (gfc_symbol *);
index 5613086..ef4076d 100644 (file)
@@ -99,7 +99,7 @@ gfc_gsymbol *gfc_gsym_root = NULL;
 
 gfc_dt_list *gfc_derived_types;
 
-static gfc_undo_change_set default_undo_chgset_var = { vNULL, vNULL };
+static gfc_undo_change_set default_undo_chgset_var = { vNULL, vNULL, NULL };
 static gfc_undo_change_set *latest_undo_chgset = &default_undo_chgset_var;
 
 
@@ -2698,17 +2698,49 @@ gfc_find_symbol (const char *name, gfc_namespace *ns, int parent_flag,
 }
 
 
+/* Tells whether there is only one set of changes in the stack.  */
+
+static bool
+single_undo_checkpoint_p (void)
+{
+  if (latest_undo_chgset == &default_undo_chgset_var)
+    {
+      gcc_assert (latest_undo_chgset->previous == NULL);
+      return true;
+    }
+  else
+    {
+      gcc_assert (latest_undo_chgset->previous != NULL);
+      return false;
+    }
+}
+
 /* Save symbol with the information necessary to back it out.  */
 
 static void
 save_symbol_data (gfc_symbol *sym)
 {
+  gfc_symbol *s;
+  unsigned i;
 
-  if (sym->gfc_new || sym->old_symbol != NULL)
+  if (!single_undo_checkpoint_p ())
+    {
+      /* If there is more than one change set, look for the symbol in the
+         current one.  If it is found there, we can reuse it.  */
+      FOR_EACH_VEC_ELT (latest_undo_chgset->syms, i, s)
+       if (s == sym)
+         {
+           gcc_assert (sym->gfc_new || sym->old_symbol != NULL);
+           return;
+         }
+    }
+  else if (sym->gfc_new || sym->old_symbol != NULL)
     return;
 
-  sym->old_symbol = XCNEW (gfc_symbol);
-  *(sym->old_symbol) = *sym;
+  s = XCNEW (gfc_symbol);
+  *s = *sym;
+  sym->old_symbol = s;
+  sym->gfc_new = 0;
 
   latest_undo_chgset->syms.safe_push (sym);
 }
@@ -2879,6 +2911,22 @@ find_common_symtree (gfc_symtree *st, gfc_common_head *head)
 }
 
 
+/* Clear the given storage, and make it the current change set for registering
+   changed symbols.  Its contents are freed after a call to
+   gfc_restore_last_undo_checkpoint or gfc_drop_last_undo_checkpoint, but
+   it is up to the caller to free the storage itself.  It is usually a local
+   variable, so there is nothing to do anyway.  */
+
+void
+gfc_new_undo_checkpoint (gfc_undo_change_set &chg_syms)
+{
+  chg_syms.syms = vNULL;
+  chg_syms.tbps = vNULL;
+  chg_syms.previous = latest_undo_chgset;
+  latest_undo_chgset = &chg_syms;
+}
+
+
 /* Restore previous state of symbol.  Just copy simple stuff.  */
   
 static void
@@ -2933,17 +2981,88 @@ restore_old_symbol (gfc_symbol *p)
       p->formal = old->formal;
     }
 
-  free (p->old_symbol);
-  p->old_symbol = NULL;
+  p->old_symbol = old->old_symbol;
+  free (old);
+}
+
+
+/* Frees the internal data of a gfc_undo_change_set structure.  Doesn't free
+   the structure itself.  */
+
+static void
+free_undo_change_set_data (gfc_undo_change_set &cs)
+{
+  cs.syms.release ();
+  cs.tbps.release ();
+}
+
+
+/* Given a change set pointer, free its target's contents and update it with
+   the address of the previous change set.  Note that only the contents are
+   freed, not the target itself (the contents' container).  It is not a problem
+   as the latter will be a local variable usually.  */
+
+static void
+pop_undo_change_set (gfc_undo_change_set *&cs)
+{
+  free_undo_change_set_data (*cs);
+  cs = cs->previous;
+}
+
+
+static void free_old_symbol (gfc_symbol *sym);
+
+
+/* Merges the current change set into the previous one.  The changes themselves
+   are left untouched; only one checkpoint is forgotten.  */
+
+void
+gfc_drop_last_undo_checkpoint (void)
+{
+  gfc_symbol *s, *t;
+  unsigned i, j;
+
+  FOR_EACH_VEC_ELT (latest_undo_chgset->syms, i, s)
+    {
+      /* No need to loop in this case.  */
+      if (s->old_symbol == NULL)
+        continue;
+
+      /* Remove the duplicate symbols.  */
+      FOR_EACH_VEC_ELT (latest_undo_chgset->previous->syms, j, t)
+       if (t == s)
+         {
+           latest_undo_chgset->previous->syms.unordered_remove (j);
+
+           /* S->OLD_SYMBOL is the backup symbol for S as it was at the
+              last checkpoint.  We drop that checkpoint, so S->OLD_SYMBOL
+              shall contain from now on the backup symbol for S as it was
+              at the checkpoint before.  */
+           if (s->old_symbol->gfc_new)
+             {
+               gcc_assert (s->old_symbol->old_symbol == NULL);
+               s->gfc_new = s->old_symbol->gfc_new;
+               free_old_symbol (s);
+             }
+           else
+             restore_old_symbol (s->old_symbol);
+           break;
+         }
+    }
+
+  latest_undo_chgset->previous->syms.safe_splice (latest_undo_chgset->syms);
+  latest_undo_chgset->previous->tbps.safe_splice (latest_undo_chgset->tbps);
+
+  pop_undo_change_set (latest_undo_chgset);
 }
 
 
-/* Undoes all the changes made to symbols in the current statement.
+/* Undoes all the changes made to symbols since the previous checkpoint.
    This subroutine is made simpler due to the fact that attributes are
    never removed once added.  */
 
 void
-gfc_undo_symbols (void)
+gfc_restore_last_undo_checkpoint (void)
 {
   gfc_symbol *p;
   unsigned i;
@@ -3011,6 +3130,30 @@ gfc_undo_symbols (void)
 
   latest_undo_chgset->syms.truncate (0);
   latest_undo_chgset->tbps.truncate (0);
+
+  if (!single_undo_checkpoint_p ())
+    pop_undo_change_set (latest_undo_chgset);
+}
+
+
+/* Makes sure that there is only one set of changes; in other words we haven't
+   forgotten to pair a call to gfc_new_checkpoint with a call to either
+   gfc_drop_last_undo_checkpoint or gfc_restore_last_undo_checkpoint.  */
+
+static void
+enforce_single_undo_checkpoint (void)
+{
+  gcc_checking_assert (single_undo_checkpoint_p ());
+}
+
+
+/* Undoes all the changes made to symbols in the current statement.  */
+
+void
+gfc_undo_symbols (void)
+{
+  enforce_single_undo_checkpoint ();
+  gfc_restore_last_undo_checkpoint ();
 }
 
 
@@ -3051,6 +3194,8 @@ gfc_commit_symbols (void)
   gfc_typebound_proc *tbp;
   unsigned i;
 
+  enforce_single_undo_checkpoint ();
+
   FOR_EACH_VEC_ELT (latest_undo_chgset->syms, i, p)
     {
       p->mark = 0;
@@ -3074,6 +3219,8 @@ gfc_commit_symbol (gfc_symbol *sym)
   gfc_symbol *p;
   unsigned i;
 
+  enforce_single_undo_checkpoint ();
+
   FOR_EACH_VEC_ELT (latest_undo_chgset->syms, i, p)
     if (p == sym)
       {
@@ -3357,10 +3504,12 @@ gfc_symbol_init_2 (void)
 void
 gfc_symbol_done_2 (void)
 {
-
   gfc_free_namespace (gfc_current_ns);
   gfc_current_ns = NULL;
   gfc_free_dt_list ();
+
+  enforce_single_undo_checkpoint ();
+  free_undo_change_set_data (*latest_undo_chgset);
 }
 
 
@@ -3525,6 +3674,7 @@ gfc_save_all (gfc_namespace *ns)
 void
 gfc_enforce_clean_symbol_state(void)
 {
+  enforce_single_undo_checkpoint ();
   gcc_assert (latest_undo_chgset->syms.is_empty ());
 }