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 <[email protected]>
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 --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 ());
}