diff mbox

[fortran,4/5] PR54730 ICE: confused by type-like fonctions: Support multiple change sets.

Message ID 20130219164833.30845.82666@marvin
State New
Headers show

Commit Message

Mikael Morin Feb. 19, 2013, 4:48 p.m. UTC
A new field 'previous' is added to the 'gfc_change_set' structure so that it can
be used as a stack.
New procedures are added to use the new partial undoing feature, namely:
gfc_new_checkpoint, gfc_drop_last_checkpoint and gfc_restore_last_checkpoint.
They will be used in the next patch.
2013-02-15  Mikael Morin  <mikael@gcc.gnu.org>

	PR fortran/54730
	* gfortran.h (struct gfc_change_set): New field 'previous'.
	(gfc_new_checkpoint, gfc_drop_last_checkpoint, gfc_restore_last_checkpoint):
	New prototypes.
	* symbol.c (change_set_var): Update initialization.
	(single_undo_checkpoint_p, gfc_new_checkpoint, free_change_set_data,
	pop_change_set, gfc_drop_last_checkpiont, 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_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): Free change set data.
diff mbox

Patch

diff --git a/gfortran.h b/gfortran.h
index 31b0d42..7a18c6c 100644
--- a/gfortran.h
+++ b/gfortran.h
@@ -1281,6 +1281,7 @@  struct gfc_change_set
 {
   vec<gfc_symbol *> syms;
   vec<gfc_typebound_proc *> tbps;
+  gfc_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_checkpoint (gfc_change_set &);
+void gfc_drop_last_checkpoint (void);
+void gfc_restore_last_checkpoint (void);
 void gfc_undo_symbols (void);
 void gfc_commit_symbols (void);
 void gfc_commit_symbol (gfc_symbol *);
diff --git a/symbol.c b/symbol.c
index e4dbb41..f040431 100644
--- a/symbol.c
+++ b/symbol.c
@@ -99,7 +99,7 @@  gfc_gsymbol *gfc_gsym_root = NULL;
 
 gfc_dt_list *gfc_derived_types;
 
-static gfc_change_set change_set_var = { vNULL, vNULL };
+static gfc_change_set change_set_var = { vNULL, vNULL, NULL };
 static gfc_change_set *changes = &change_set_var;
 
 
@@ -2697,17 +2697,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 (changes == &change_set_var)
+    {
+      gcc_assert (changes->previous == NULL);
+      return true;
+    }
+  else
+    {
+      gcc_assert (changes->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 (changes->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;
 
   changes->syms.safe_push (sym);
 }
@@ -2878,6 +2910,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_checkpoint or gfc_drop_last_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_checkpoint (gfc_change_set &chg_syms)
+{
+  chg_syms.syms = vNULL;
+  chg_syms.tbps = vNULL;
+  chg_syms.previous = changes;
+  changes = &chg_syms;
+}
+
+
 /* Restore previous state of symbol.  Just copy simple stuff.  */
   
 static void
@@ -2932,17 +2980,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_change_set structure.  Doesn't free the
+   structure itself.  */
+
+static void
+free_change_set_data (gfc_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_change_set (gfc_change_set *&cs)
+{
+  free_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_checkpoint (void)
+{
+  gfc_symbol *s, *t;
+  unsigned i, j;
+
+  FOR_EACH_VEC_ELT (changes->syms, i, s)
+    {
+      /* No need to loop in this case.  */
+      if (s->old_symbol == NULL)
+        continue;
+
+      /* Remove the duplicate symbols.  */
+      FOR_EACH_VEC_ELT (changes->previous->syms, j, t)
+	if (t == s)
+	  {
+	    changes->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;
+	  }
+    }
+
+  changes->previous->syms.safe_splice (changes->syms);
+  changes->previous->tbps.safe_splice (changes->tbps);
+
+  pop_change_set (changes);
 }
 
 
-/* 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_checkpoint (void)
 {
   gfc_symbol *p;
   unsigned i;
@@ -3010,6 +3129,30 @@  gfc_undo_symbols (void)
 
   changes->syms.truncate (0);
   changes->tbps.truncate (0);
+
+  if (!single_undo_checkpoint_p ())
+    pop_change_set (changes);
+}
+
+
+/* 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_checkpoint or gfc_restore_last_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_checkpoint ();
 }
 
 
@@ -3050,6 +3193,8 @@  gfc_commit_symbols (void)
   gfc_typebound_proc *tbp;
   unsigned i;
 
+  enforce_single_undo_checkpoint ();
+
   FOR_EACH_VEC_ELT (changes->syms, i, p)
     {
       p->mark = 0;
@@ -3073,6 +3218,8 @@  gfc_commit_symbol (gfc_symbol *sym)
   gfc_symbol *p;
   unsigned i;
 
+  enforce_single_undo_checkpoint ();
+
   FOR_EACH_VEC_ELT (changes->syms, i, p)
     if (p == sym)
       {
@@ -3356,10 +3503,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_change_set_data (*changes);
 }
 
 
@@ -3524,6 +3673,7 @@  gfc_save_all (gfc_namespace *ns)
 void
 gfc_enforce_clean_symbol_state(void)
 {
+  enforce_single_undo_checkpoint ();
   gcc_assert (changes->syms.is_empty ());
 }