aboutsummaryrefslogtreecommitdiff
path: root/gcc/fortran
diff options
context:
space:
mode:
authorMikael Morin <mikael@gcc.gnu.org>2013-03-03 17:34:42 +0000
committerMikael Morin <mikael@gcc.gnu.org>2013-03-03 17:34:42 +0000
commitab68a73ede62fbcbb3abd07879838ebe81fdd484 (patch)
treeb845cd1c7ec2eb129f2b1ede285390fbfc00a15c /gcc/fortran
parent4ef9b950005a24aab3b0ea7da77f16292ef587b4 (diff)
downloadgcc-ab68a73ede62fbcbb3abd07879838ebe81fdd484.zip
gcc-ab68a73ede62fbcbb3abd07879838ebe81fdd484.tar.gz
gcc-ab68a73ede62fbcbb3abd07879838ebe81fdd484.tar.bz2
re PR fortran/54730 (ICE in gfc_typenode_for_spec, at fortran/trans-types.c:1066)
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
Diffstat (limited to 'gcc/fortran')
-rw-r--r--gcc/fortran/ChangeLog21
-rw-r--r--gcc/fortran/gfortran.h4
-rw-r--r--gcc/fortran/symbol.c168
3 files changed, 184 insertions, 9 deletions
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog
index 49d9bdb..c9b0ca6 100644
--- a/gcc/fortran/ChangeLog
+++ b/gcc/fortran/ChangeLog
@@ -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>
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index d6176db..18bbf79 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -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 *);
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
index 5613086..ef4076d 100644
--- a/gcc/fortran/symbol.c
+++ b/gcc/fortran/symbol.c
@@ -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 ());
}