summaryrefslogtreecommitdiff
path: root/gcc/fortran/symbol.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/fortran/symbol.c')
-rw-r--r--gcc/fortran/symbol.c2417
1 files changed, 2417 insertions, 0 deletions
diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c
new file mode 100644
index 00000000000..1bf32b241e7
--- /dev/null
+++ b/gcc/fortran/symbol.c
@@ -0,0 +1,2417 @@
+/* Maintain binary trees of symbols.
+ Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Contributed by Andy Vaught
+
+This file is part of GNU G95.
+
+GNU G95 is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU G95 is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU G95; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+
+#include "config.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "gfortran.h"
+#include "parse.h"
+
+/* Strings for all symbol attributes. We use these for dumping the
+ parse tree, in error messages, and also when reading and writing
+ modules. */
+
+const mstring flavors[] =
+{
+ minit ("UNKNOWN-FL", FL_UNKNOWN), minit ("PROGRAM", FL_PROGRAM),
+ minit ("BLOCK-DATA", FL_BLOCK_DATA), minit ("MODULE", FL_MODULE),
+ minit ("VARIABLE", FL_VARIABLE), minit ("PARAMETER", FL_PARAMETER),
+ minit ("LABEL", FL_LABEL), minit ("PROCEDURE", FL_PROCEDURE),
+ minit ("DERIVED", FL_DERIVED), minit ("NAMELIST", FL_NAMELIST),
+ minit (NULL, -1)
+};
+
+const mstring procedures[] =
+{
+ minit ("UNKNOWN-PROC", PROC_UNKNOWN),
+ minit ("MODULE-PROC", PROC_MODULE),
+ minit ("INTERNAL-PROC", PROC_INTERNAL),
+ minit ("DUMMY-PROC", PROC_DUMMY),
+ minit ("INTRINSIC-PROC", PROC_INTRINSIC),
+ minit ("EXTERNAL-PROC", PROC_EXTERNAL),
+ minit ("STATEMENT-PROC", PROC_ST_FUNCTION),
+ minit (NULL, -1)
+};
+
+const mstring intents[] =
+{
+ minit ("UNKNOWN-INTENT", INTENT_UNKNOWN),
+ minit ("IN", INTENT_IN),
+ minit ("OUT", INTENT_OUT),
+ minit ("INOUT", INTENT_INOUT),
+ minit (NULL, -1)
+};
+
+const mstring access_types[] =
+{
+ minit ("UNKNOWN-ACCESS", ACCESS_UNKNOWN),
+ minit ("PUBLIC", ACCESS_PUBLIC),
+ minit ("PRIVATE", ACCESS_PRIVATE),
+ minit (NULL, -1)
+};
+
+const mstring ifsrc_types[] =
+{
+ minit ("UNKNOWN", IFSRC_UNKNOWN),
+ minit ("DECL", IFSRC_DECL),
+ minit ("BODY", IFSRC_IFBODY),
+ minit ("USAGE", IFSRC_USAGE)
+};
+
+
+/* This is to make sure the backend generates setup code in the correct
+ order. */
+
+static int next_dummy_order = 1;
+
+
+gfc_namespace *gfc_current_ns;
+
+static gfc_symbol *changed_syms = NULL;
+
+
+/*********** IMPLICIT NONE and IMPLICIT statement handlers ***********/
+
+/* The following static variables hold the default types set by
+ IMPLICIT statements. We have to store kind information because of
+ IMPLICIT DOUBLE PRECISION statements. IMPLICIT NONE stores a
+ BT_UNKNOWN into all elements. The arrays of flags indicate whether
+ a particular element has been explicitly set or not. */
+
+static gfc_typespec new_ts[GFC_LETTERS];
+static int new_flag[GFC_LETTERS];
+
+
+/* Handle a correctly parsed IMPLICIT NONE. */
+
+void
+gfc_set_implicit_none (void)
+{
+ int i;
+
+ for (i = 'a'; i <= 'z'; i++)
+ {
+ gfc_clear_ts (&gfc_current_ns->default_type[i - 'a']);
+ gfc_current_ns->set_flag[i - 'a'] = 1;
+ }
+}
+
+
+/* Sets the implicit types parsed by gfc_match_implicit(). */
+
+void
+gfc_set_implicit (void)
+{
+ int i;
+
+ for (i = 0; i < GFC_LETTERS; i++)
+ if (new_flag[i])
+ {
+ gfc_current_ns->default_type[i] = new_ts[i];
+ gfc_current_ns->set_flag[i] = 1;
+ }
+}
+
+
+/* Wipe anything a previous IMPLICIT statement may have tried to do. */
+void gfc_clear_new_implicit (void)
+{
+ int i;
+
+ for (i = 0; i < GFC_LETTERS; i++)
+ {
+ gfc_clear_ts (&new_ts[i]);
+ if (new_flag[i])
+ new_flag[i] = 0;
+ }
+}
+
+
+/* Prepare for a new implicit range. Sets flags in new_flag[] and
+ copies the typespec to new_ts[]. */
+
+try gfc_add_new_implicit_range (int c1, int c2, gfc_typespec * ts)
+{
+ int i;
+
+ c1 -= 'a';
+ c2 -= 'a';
+
+ for (i = c1; i <= c2; i++)
+ {
+ if (new_flag[i])
+ {
+ gfc_error ("Letter '%c' already set in IMPLICIT statement at %C",
+ i + 'A');
+ return FAILURE;
+ }
+
+ new_ts[i] = *ts;
+ new_flag[i] = 1;
+ }
+
+ return SUCCESS;
+}
+
+
+/* Add a matched implicit range for gfc_set_implicit(). An implicit
+ statement has been fully matched at this point. We now need to
+ check if merging the new implicit types back into the existing
+ types will work. */
+
+try
+gfc_merge_new_implicit (void)
+{
+ int i;
+
+ for (i = 0; i < GFC_LETTERS; i++)
+ if (new_flag[i])
+ {
+ if (gfc_current_ns->set_flag[i])
+ {
+ gfc_error ("Letter %c already has an IMPLICIT type at %C",
+ i + 'A');
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+/* Given a symbol, return a pointer to the typespec for it's default
+ type. */
+
+gfc_typespec *
+gfc_get_default_type (gfc_symbol * sym, gfc_namespace * ns)
+{
+ char letter;
+
+ letter = sym->name[0];
+ if (letter < 'a' || letter > 'z')
+ gfc_internal_error ("gfc_get_default_type(): Bad symbol");
+
+ if (ns == NULL)
+ ns = gfc_current_ns;
+
+ return &ns->default_type[letter - 'a'];
+}
+
+
+/* Given a pointer to a symbol, set its type according to the first
+ letter of its name. Fails if the letter in question has no default
+ type. */
+
+try
+gfc_set_default_type (gfc_symbol * sym, int error_flag, gfc_namespace * ns)
+{
+ gfc_typespec *ts;
+
+ if (sym->ts.type != BT_UNKNOWN)
+ gfc_internal_error ("gfc_set_default_type(): symbol already has a type");
+
+ ts = gfc_get_default_type (sym, ns);
+
+ if (ts->type == BT_UNKNOWN)
+ {
+ if (error_flag)
+ gfc_error ("Symbol '%s' at %L has no IMPLICIT type", sym->name,
+ &sym->declared_at);
+
+ return FAILURE;
+ }
+
+ sym->ts = *ts;
+ sym->attr.implicit_type = 1;
+
+ return SUCCESS;
+}
+
+
+/******************** Symbol attribute stuff *********************/
+
+/* This is a generic conflict-checker. We do this to avoid having a
+ single conflict in two places. */
+
+#define conf(a, b) if (attr->a && attr->b) { a1 = a; a2 = b; goto conflict; }
+#define conf2(a) if (attr->a) { a2 = a; goto conflict; }
+
+static try
+check_conflict (symbol_attribute * attr, locus * where)
+{
+ static const char *dummy = "DUMMY", *save = "SAVE", *pointer = "POINTER",
+ *target = "TARGET", *external = "EXTERNAL", *intent = "INTENT",
+ *intrinsic = "INTRINSIC", *allocatable = "ALLOCATABLE",
+ *elemental = "ELEMENTAL", *private = "PRIVATE", *recursive = "RECURSIVE",
+ *in_common = "COMMON", *result = "RESULT", *in_namelist = "NAMELIST",
+ *public = "PUBLIC", *optional = "OPTIONAL", *entry = "ENTRY",
+ *function = "FUNCTION", *subroutine = "SUBROUTINE",
+ *dimension = "DIMENSION";
+
+ const char *a1, *a2;
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ if (attr->pointer && attr->intent != INTENT_UNKNOWN)
+ {
+ a1 = pointer;
+ a2 = intent;
+ goto conflict;
+ }
+
+ /* Check for attributes not allowed in a BLOCK DATA. */
+ if (gfc_current_state () == COMP_BLOCK_DATA)
+ {
+ a1 = NULL;
+
+ if (attr->allocatable)
+ a1 = allocatable;
+ if (attr->external)
+ a1 = external;
+ if (attr->optional)
+ a1 = optional;
+ if (attr->access == ACCESS_PRIVATE)
+ a1 = private;
+ if (attr->access == ACCESS_PUBLIC)
+ a1 = public;
+ if (attr->intent != INTENT_UNKNOWN)
+ a1 = intent;
+
+ if (a1 != NULL)
+ {
+ gfc_error
+ ("%s attribute not allowed in BLOCK DATA program unit at %L", a1,
+ where);
+ return FAILURE;
+ }
+ }
+
+ conf (dummy, save);
+ conf (pointer, target);
+ conf (pointer, external);
+ conf (pointer, intrinsic);
+ conf (target, external);
+ conf (target, intrinsic);
+ conf (external, dimension); /* See Fortran 95's R504. */
+
+ conf (external, intrinsic);
+ conf (allocatable, pointer);
+ conf (allocatable, dummy); /* TODO: Allowed in Fortran 200x. */
+ conf (allocatable, function); /* TODO: Allowed in Fortran 200x. */
+ conf (allocatable, result); /* TODO: Allowed in Fortran 200x. */
+ conf (elemental, recursive);
+
+ conf (in_common, dummy);
+ conf (in_common, allocatable);
+ conf (in_common, result);
+ conf (dummy, result);
+
+ conf (in_namelist, pointer);
+ conf (in_namelist, allocatable);
+
+ conf (entry, result);
+
+ conf (function, subroutine);
+
+ a1 = gfc_code2string (flavors, attr->flavor);
+
+ if (attr->in_namelist
+ && attr->flavor != FL_VARIABLE
+ && attr->flavor != FL_UNKNOWN)
+ {
+
+ a2 = in_namelist;
+ goto conflict;
+ }
+
+ switch (attr->flavor)
+ {
+ case FL_PROGRAM:
+ case FL_BLOCK_DATA:
+ case FL_MODULE:
+ case FL_LABEL:
+ conf2 (dummy);
+ conf2 (save);
+ conf2 (pointer);
+ conf2 (target);
+ conf2 (external);
+ conf2 (intrinsic);
+ conf2 (allocatable);
+ conf2 (result);
+ conf2 (in_namelist);
+ conf2 (optional);
+ conf2 (function);
+ conf2 (subroutine);
+ break;
+
+ case FL_VARIABLE:
+ case FL_NAMELIST:
+ break;
+
+ case FL_PROCEDURE:
+ conf2 (intent);
+
+ if (attr->subroutine)
+ {
+ conf2(save);
+ conf2(pointer);
+ conf2(target);
+ conf2(allocatable);
+ conf2(result);
+ conf2(in_namelist);
+ conf2(function);
+ }
+
+ switch (attr->proc)
+ {
+ case PROC_ST_FUNCTION:
+ conf2 (in_common);
+ break;
+
+ case PROC_MODULE:
+ conf2 (dummy);
+ break;
+
+ case PROC_DUMMY:
+ conf2 (result);
+ conf2 (in_common);
+ conf2 (save);
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case FL_DERIVED:
+ conf2 (dummy);
+ conf2 (save);
+ conf2 (pointer);
+ conf2 (target);
+ conf2 (external);
+ conf2 (intrinsic);
+ conf2 (allocatable);
+ conf2 (optional);
+ conf2 (entry);
+ conf2 (function);
+ conf2 (subroutine);
+
+ if (attr->intent != INTENT_UNKNOWN)
+ {
+ a2 = intent;
+ goto conflict;
+ }
+ break;
+
+ case FL_PARAMETER:
+ conf2 (external);
+ conf2 (intrinsic);
+ conf2 (optional);
+ conf2 (allocatable);
+ conf2 (function);
+ conf2 (subroutine);
+ conf2 (entry);
+ conf2 (pointer);
+ conf2 (target);
+ conf2 (dummy);
+ conf2 (in_common);
+ break;
+
+ default:
+ break;
+ }
+
+ return SUCCESS;
+
+conflict:
+ gfc_error ("%s attribute conflicts with %s attribute at %L", a1, a2, where);
+ return FAILURE;
+}
+
+#undef conf
+#undef conf2
+
+
+/* Mark a symbol as referenced. */
+
+void
+gfc_set_sym_referenced (gfc_symbol * sym)
+{
+ if (sym->attr.referenced)
+ return;
+
+ sym->attr.referenced = 1;
+
+ /* Remember which order dummy variables are accessed in. */
+ if (sym->attr.dummy)
+ sym->dummy_order = next_dummy_order++;
+}
+
+
+/* Common subroutine called by attribute changing subroutines in order
+ to prevent them from changing a symbol that has been
+ use-associated. Returns zero if it is OK to change the symbol,
+ nonzero if not. */
+
+static int
+check_used (symbol_attribute * attr, locus * where)
+{
+
+ if (attr->use_assoc == 0)
+ return 0;
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ gfc_error ("Cannot change attributes of USE-associated symbol at %L",
+ where);
+
+ return 1;
+}
+
+
+/* Used to prevent changing the attributes of a symbol after it has been
+ used. This check is only done from dummy variable as only these can be
+ used in specification expressions. Applying this to all symbols causes
+ error when we reach the body of a contained function. */
+
+static int
+check_done (symbol_attribute * attr, locus * where)
+{
+
+ if (!(attr->dummy && attr->referenced))
+ return 0;
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ gfc_error ("Cannot change attributes of symbol at %L"
+ " after it has been used", where);
+
+ return 1;
+}
+
+
+/* Generate an error because of a duplicate attribute. */
+
+static void
+duplicate_attr (const char *attr, locus * where)
+{
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ gfc_error ("Duplicate %s attribute specified at %L", attr, where);
+}
+
+
+try
+gfc_add_allocatable (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->allocatable)
+ {
+ duplicate_attr ("ALLOCATABLE", where);
+ return FAILURE;
+ }
+
+ attr->allocatable = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_dimension (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->dimension)
+ {
+ duplicate_attr ("DIMENSION", where);
+ return FAILURE;
+ }
+
+ attr->dimension = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_external (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->external)
+ {
+ duplicate_attr ("EXTERNAL", where);
+ return FAILURE;
+ }
+
+ attr->external = 1;
+
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_intrinsic (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->intrinsic)
+ {
+ duplicate_attr ("INTRINSIC", where);
+ return FAILURE;
+ }
+
+ attr->intrinsic = 1;
+
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_optional (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->optional)
+ {
+ duplicate_attr ("OPTIONAL", where);
+ return FAILURE;
+ }
+
+ attr->optional = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_pointer (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ attr->pointer = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_result (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ attr->result = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_save (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where))
+ return FAILURE;
+
+ if (gfc_pure (NULL))
+ {
+ gfc_error
+ ("SAVE attribute at %L cannot be specified in a PURE procedure",
+ where);
+ return FAILURE;
+ }
+
+ if (attr->save)
+ {
+ duplicate_attr ("SAVE", where);
+ return FAILURE;
+ }
+
+ attr->save = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_saved_common (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where))
+ return FAILURE;
+
+ if (attr->saved_common)
+ {
+ duplicate_attr ("SAVE", where);
+ return FAILURE;
+ }
+
+ attr->saved_common = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_target (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->target)
+ {
+ duplicate_attr ("TARGET", where);
+ return FAILURE;
+ }
+
+ attr->target = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_dummy (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where))
+ return FAILURE;
+
+ /* Duplicate dummy arguments are allow due to ENTRY statements. */
+ attr->dummy = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_common (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ attr->common = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_in_common (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ /* Duplicate attribute already checked for. */
+ attr->in_common = 1;
+ if (check_conflict (attr, where) == FAILURE)
+ return FAILURE;
+
+ if (attr->flavor == FL_VARIABLE)
+ return SUCCESS;
+
+ return gfc_add_flavor (attr, FL_VARIABLE, where);
+}
+
+
+try
+gfc_add_in_namelist (symbol_attribute * attr, locus * where)
+{
+
+ attr->in_namelist = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_sequence (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where))
+ return FAILURE;
+
+ attr->sequence = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_elemental (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ attr->elemental = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_pure (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ attr->pure = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_recursive (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ attr->recursive = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_entry (symbol_attribute * attr, locus * where)
+{
+
+ if (check_used (attr, where))
+ return FAILURE;
+
+ if (attr->entry)
+ {
+ duplicate_attr ("ENTRY", where);
+ return FAILURE;
+ }
+
+ attr->entry = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_function (symbol_attribute * attr, locus * where)
+{
+
+ if (attr->flavor != FL_PROCEDURE
+ && gfc_add_flavor (attr, FL_PROCEDURE, where) == FAILURE)
+ return FAILURE;
+
+ attr->function = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_subroutine (symbol_attribute * attr, locus * where)
+{
+
+ if (attr->flavor != FL_PROCEDURE
+ && gfc_add_flavor (attr, FL_PROCEDURE, where) == FAILURE)
+ return FAILURE;
+
+ attr->subroutine = 1;
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_generic (symbol_attribute * attr, locus * where)
+{
+
+ if (attr->flavor != FL_PROCEDURE
+ && gfc_add_flavor (attr, FL_PROCEDURE, where) == FAILURE)
+ return FAILURE;
+
+ attr->generic = 1;
+ return check_conflict (attr, where);
+}
+
+
+/* Flavors are special because some flavors are not what fortran
+ considers attributes and can be reaffirmed multiple times. */
+
+try
+gfc_add_flavor (symbol_attribute * attr, sym_flavor f, locus * where)
+{
+
+ if ((f == FL_PROGRAM || f == FL_BLOCK_DATA || f == FL_MODULE
+ || f == FL_PARAMETER || f == FL_LABEL || f == FL_DERIVED
+ || f == FL_NAMELIST) && check_used (attr, where))
+ return FAILURE;
+
+ if (attr->flavor == f && f == FL_VARIABLE)
+ return SUCCESS;
+
+ if (attr->flavor != FL_UNKNOWN)
+ {
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ gfc_error ("%s attribute conflicts with %s attribute at %L",
+ gfc_code2string (flavors, attr->flavor),
+ gfc_code2string (flavors, f), where);
+
+ return FAILURE;
+ }
+
+ attr->flavor = f;
+
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_procedure (symbol_attribute * attr, procedure_type t, locus * where)
+{
+
+ if (check_used (attr, where) || check_done (attr, where))
+ return FAILURE;
+
+ if (attr->flavor != FL_PROCEDURE
+ && gfc_add_flavor (attr, FL_PROCEDURE, where) == FAILURE)
+ return FAILURE;
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ if (attr->proc != PROC_UNKNOWN)
+ {
+ gfc_error ("%s procedure at %L is already %s %s procedure",
+ gfc_code2string (procedures, t), where,
+ gfc_article (gfc_code2string (procedures, attr->proc)),
+ gfc_code2string (procedures, attr->proc));
+
+ return FAILURE;
+ }
+
+ attr->proc = t;
+
+ /* Statement functions are always scalar and functions. */
+ if (t == PROC_ST_FUNCTION
+ && ((!attr->function && gfc_add_function (attr, where) == FAILURE)
+ || attr->dimension))
+ return FAILURE;
+
+ return check_conflict (attr, where);
+}
+
+
+try
+gfc_add_intent (symbol_attribute * attr, sym_intent intent, locus * where)
+{
+
+ if (check_used (attr, where))
+ return FAILURE;
+
+ if (attr->intent == INTENT_UNKNOWN)
+ {
+ attr->intent = intent;
+ return check_conflict (attr, where);
+ }
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ gfc_error ("INTENT (%s) conflicts with INTENT(%s) at %L",
+ gfc_intent_string (attr->intent),
+ gfc_intent_string (intent), where);
+
+ return FAILURE;
+}
+
+
+/* No checks for use-association in public and private statements. */
+
+try
+gfc_add_access (symbol_attribute * attr, gfc_access access, locus * where)
+{
+
+ if (attr->access == ACCESS_UNKNOWN)
+ {
+ attr->access = access;
+ return check_conflict (attr, where);
+ }
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+ gfc_error ("ACCESS specification at %L was already specified", where);
+
+ return FAILURE;
+}
+
+
+try
+gfc_add_explicit_interface (gfc_symbol * sym, ifsrc source,
+ gfc_formal_arglist * formal, locus * where)
+{
+
+ if (check_used (&sym->attr, where))
+ return FAILURE;
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ if (sym->attr.if_source != IFSRC_UNKNOWN
+ && sym->attr.if_source != IFSRC_DECL)
+ {
+ gfc_error ("Symbol '%s' at %L already has an explicit interface",
+ sym->name, where);
+ return FAILURE;
+ }
+
+ sym->formal = formal;
+ sym->attr.if_source = source;
+
+ return SUCCESS;
+}
+
+
+/* Add a type to a symbol. */
+
+try
+gfc_add_type (gfc_symbol * sym, gfc_typespec * ts, locus * where)
+{
+ sym_flavor flavor;
+
+/* TODO: This is legal if it is reaffirming an implicit type.
+ if (check_done (&sym->attr, where))
+ return FAILURE;*/
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+
+ if (sym->ts.type != BT_UNKNOWN)
+ {
+ gfc_error ("Symbol '%s' at %L already has basic type of %s", sym->name,
+ where, gfc_basic_typename (sym->ts.type));
+ return FAILURE;
+ }
+
+ flavor = sym->attr.flavor;
+
+ if (flavor == FL_PROGRAM || flavor == FL_BLOCK_DATA || flavor == FL_MODULE
+ || flavor == FL_LABEL || (flavor == FL_PROCEDURE
+ && sym->attr.subroutine)
+ || flavor == FL_DERIVED || flavor == FL_NAMELIST)
+ {
+ gfc_error ("Symbol '%s' at %L cannot have a type", sym->name, where);
+ return FAILURE;
+ }
+
+ sym->ts = *ts;
+ return SUCCESS;
+}
+
+
+/* Clears all attributes. */
+
+void
+gfc_clear_attr (symbol_attribute * attr)
+{
+
+ attr->allocatable = 0;
+ attr->dimension = 0;
+ attr->external = 0;
+ attr->intrinsic = 0;
+ attr->optional = 0;
+ attr->pointer = 0;
+ attr->save = 0;
+ attr->target = 0;
+ attr->dummy = 0;
+ attr->common = 0;
+ attr->result = 0;
+ attr->entry = 0;
+ attr->data = 0;
+ attr->use_assoc = 0;
+ attr->in_namelist = 0;
+
+ attr->in_common = 0;
+ attr->saved_common = 0;
+ attr->function = 0;
+ attr->subroutine = 0;
+ attr->generic = 0;
+ attr->implicit_type = 0;
+ attr->sequence = 0;
+ attr->elemental = 0;
+ attr->pure = 0;
+ attr->recursive = 0;
+
+ attr->access = ACCESS_UNKNOWN;
+ attr->intent = INTENT_UNKNOWN;
+ attr->flavor = FL_UNKNOWN;
+ attr->proc = PROC_UNKNOWN;
+ attr->if_source = IFSRC_UNKNOWN;
+}
+
+
+/* Check for missing attributes in the new symbol. Currently does
+ nothing, but it's not clear that it is unnecessary yet. */
+
+try
+gfc_missing_attr (symbol_attribute * attr ATTRIBUTE_UNUSED,
+ locus * where ATTRIBUTE_UNUSED)
+{
+
+ return SUCCESS;
+}
+
+
+/* Copy an attribute to a symbol attribute, bit by bit. Some
+ attributes have a lot of side-effects but cannot be present given
+ where we are called from, so we ignore some bits. */
+
+try
+gfc_copy_attr (symbol_attribute * dest, symbol_attribute * src, locus * where)
+{
+
+ if (src->allocatable && gfc_add_allocatable (dest, where) == FAILURE)
+ goto fail;
+
+ if (src->dimension && gfc_add_dimension (dest, where) == FAILURE)
+ goto fail;
+ if (src->optional && gfc_add_optional (dest, where) == FAILURE)
+ goto fail;
+ if (src->pointer && gfc_add_pointer (dest, where) == FAILURE)
+ goto fail;
+ if (src->save && gfc_add_save (dest, where) == FAILURE)
+ goto fail;
+ if (src->target && gfc_add_target (dest, where) == FAILURE)
+ goto fail;
+ if (src->dummy && gfc_add_dummy (dest, where) == FAILURE)
+ goto fail;
+ if (src->common && gfc_add_common (dest, where) == FAILURE)
+ goto fail;
+ if (src->result && gfc_add_result (dest, where) == FAILURE)
+ goto fail;
+ if (src->entry)
+ dest->entry = 1;
+
+ if (src->in_namelist && gfc_add_in_namelist (dest, where) == FAILURE)
+ goto fail;
+
+ if (src->in_common && gfc_add_in_common (dest, where) == FAILURE)
+ goto fail;
+ if (src->saved_common && gfc_add_saved_common (dest, where) == FAILURE)
+ goto fail;
+
+ if (src->generic && gfc_add_generic (dest, where) == FAILURE)
+ goto fail;
+ if (src->function && gfc_add_function (dest, where) == FAILURE)
+ goto fail;
+ if (src->subroutine && gfc_add_subroutine (dest, where) == FAILURE)
+ goto fail;
+
+ if (src->sequence && gfc_add_sequence (dest, where) == FAILURE)
+ goto fail;
+ if (src->elemental && gfc_add_elemental (dest, where) == FAILURE)
+ goto fail;
+ if (src->pure && gfc_add_pure (dest, where) == FAILURE)
+ goto fail;
+ if (src->recursive && gfc_add_recursive (dest, where) == FAILURE)
+ goto fail;
+
+ if (src->flavor != FL_UNKNOWN
+ && gfc_add_flavor (dest, src->flavor, where) == FAILURE)
+ goto fail;
+
+ if (src->intent != INTENT_UNKNOWN
+ && gfc_add_intent (dest, src->intent, where) == FAILURE)
+ goto fail;
+
+ if (src->access != ACCESS_UNKNOWN
+ && gfc_add_access (dest, src->access, where) == FAILURE)
+ goto fail;
+
+ if (gfc_missing_attr (dest, where) == FAILURE)
+ goto fail;
+
+ /* The subroutines that set these bits also cause flavors to be set,
+ and that has already happened in the original, so don't let to
+ happen again. */
+ if (src->external)
+ dest->external = 1;
+ if (src->intrinsic)
+ dest->intrinsic = 1;
+
+ return SUCCESS;
+
+fail:
+ return FAILURE;
+}
+
+
+/************** Component name management ************/
+
+/* Component names of a derived type form their own little namespaces
+ that are separate from all other spaces. The space is composed of
+ a singly linked list of gfc_component structures whose head is
+ located in the parent symbol. */
+
+
+/* Add a component name to a symbol. The call fails if the name is
+ already present. On success, the component pointer is modified to
+ point to the additional component structure. */
+
+try
+gfc_add_component (gfc_symbol * sym, const char *name, gfc_component ** component)
+{
+ gfc_component *p, *tail;
+
+ tail = NULL;
+
+ for (p = sym->components; p; p = p->next)
+ {
+ if (strcmp (p->name, name) == 0)
+ {
+ gfc_error ("Component '%s' at %C already declared at %L",
+ name, &p->loc);
+ return FAILURE;
+ }
+
+ tail = p;
+ }
+
+ /* Allocate new component */
+ p = gfc_get_component ();
+
+ if (tail == NULL)
+ sym->components = p;
+ else
+ tail->next = p;
+
+ strcpy (p->name, name);
+ p->loc = *gfc_current_locus ();
+
+ *component = p;
+ return SUCCESS;
+}
+
+
+/* Recursive function to switch derived types of all symbol in a
+ namespace. */
+
+static void
+switch_types (gfc_symtree * st, gfc_symbol * from, gfc_symbol * to)
+{
+ gfc_symbol *sym;
+
+ if (st == NULL)
+ return;
+
+ sym = st->n.sym;
+ if (sym->ts.type == BT_DERIVED && sym->ts.derived == from)
+ sym->ts.derived = to;
+
+ switch_types (st->left, from, to);
+ switch_types (st->right, from, to);
+}
+
+
+/* This subroutine is called when a derived type is used in order to
+ make the final determination about which version to use. The
+ standard requires that a type be defined before it is 'used', but
+ such types can appear in IMPLICIT statements before the actual
+ definition. 'Using' in this context means declaring a variable to
+ be that type or using the type constructor.
+
+ If a type is used and the components haven't been defined, then we
+ have to have a derived type in a parent unit. We find the node in
+ the other namespace and point the symtree node in this namespace to
+ that node. Further reference to this name point to the correct
+ node. If we can't find the node in a parent namespace, then have
+ an error.
+
+ This subroutine takes a pointer to a symbol node and returns a
+ pointer to the translated node or NULL for an error. Usually there
+ is no translation and we return the node we were passed. */
+
+static gfc_symtree *
+gfc_use_ha_derived (gfc_symbol * sym)
+{
+ gfc_symbol *s, *p;
+ gfc_typespec *t;
+ gfc_symtree *st;
+ int i;
+
+ if (sym->ns->parent == NULL)
+ goto bad;
+
+ if (gfc_find_symbol (sym->name, sym->ns->parent, 1, &s))
+ {
+ gfc_error ("Symbol '%s' at %C is ambiguous", sym->name);
+ return NULL;
+ }
+
+ if (s == NULL || s->attr.flavor != FL_DERIVED)
+ goto bad;
+
+ /* Get rid of symbol sym, translating all references to s. */
+ for (i = 0; i < GFC_LETTERS; i++)
+ {
+ t = &sym->ns->default_type[i];
+ if (t->derived == sym)
+ t->derived = s;
+ }
+
+ st = gfc_find_symtree (sym->ns->sym_root, sym->name);
+ st->n.sym = s;
+
+ s->refs++;
+
+ /* Unlink from list of modified symbols. */
+ if (changed_syms == sym)
+ changed_syms = sym->tlink;
+ else
+ for (p = changed_syms; p; p = p->tlink)
+ if (p->tlink == sym)
+ {
+ p->tlink = sym->tlink;
+ break;
+ }
+
+ switch_types (sym->ns->sym_root, sym, s);
+
+ /* TODO: Also have to replace sym -> s in other lists like
+ namelists, common lists and interface lists. */
+ gfc_free_symbol (sym);
+
+ return st;
+
+bad:
+ gfc_error ("Derived type '%s' at %C is being used before it is defined",
+ sym->name);
+ return NULL;
+}
+
+
+gfc_symbol *
+gfc_use_derived (gfc_symbol * sym)
+{
+ gfc_symtree *st;
+
+ if (sym->components != NULL)
+ return sym; /* Already defined */
+
+ st = gfc_use_ha_derived (sym);
+ if (st)
+ return st->n.sym;
+ else
+ return NULL;
+}
+
+
+/* Given a derived type node and a component name, try to locate the
+ component structure. Returns the NULL pointer if the component is
+ not found or the components are private. */
+
+gfc_component *
+gfc_find_component (gfc_symbol * sym, const char *name)
+{
+ gfc_component *p;
+
+ if (name == NULL)
+ return NULL;
+
+ sym = gfc_use_derived (sym);
+
+ if (sym == NULL)
+ return NULL;
+
+ for (p = sym->components; p; p = p->next)
+ if (strcmp (p->name, name) == 0)
+ break;
+
+ if (p == NULL)
+ gfc_error ("'%s' at %C is not a member of the '%s' structure",
+ name, sym->name);
+ else
+ {
+ if (sym->attr.use_assoc && sym->component_access == ACCESS_PRIVATE)
+ {
+ gfc_error ("Component '%s' at %C is a PRIVATE component of '%s'",
+ name, sym->name);
+ p = NULL;
+ }
+ }
+
+ return p;
+}
+
+
+/* Given a symbol, free all of the component structures and everything
+ they point to. */
+
+static void
+free_components (gfc_component * p)
+{
+ gfc_component *q;
+
+ for (; p; p = q)
+ {
+ q = p->next;
+
+ gfc_free_array_spec (p->as);
+ gfc_free_expr (p->initializer);
+
+ gfc_free (p);
+ }
+}
+
+
+/* Set component attributes from a standard symbol attribute
+ structure. */
+
+void
+gfc_set_component_attr (gfc_component * c, symbol_attribute * attr)
+{
+
+ c->dimension = attr->dimension;
+ c->pointer = attr->pointer;
+}
+
+
+/* Get a standard symbol attribute structure given the component
+ structure. */
+
+void
+gfc_get_component_attr (symbol_attribute * attr, gfc_component * c)
+{
+
+ gfc_clear_attr (attr);
+ attr->dimension = c->dimension;
+ attr->pointer = c->pointer;
+}
+
+
+/******************** Statement label management ********************/
+
+/* Free a single gfc_st_label structure, making sure the list is not
+ messed up. This function is called only when some parse error
+ occurs. */
+
+void
+gfc_free_st_label (gfc_st_label * l)
+{
+
+ if (l == NULL)
+ return;
+
+ if (l->prev)
+ (l->prev->next = l->next);
+
+ if (l->next)
+ (l->next->prev = l->prev);
+
+ if (l->format != NULL)
+ gfc_free_expr (l->format);
+ gfc_free (l);
+}
+
+/* Free a whole list of gfc_st_label structures. */
+
+static void
+free_st_labels (gfc_st_label * l1)
+{
+ gfc_st_label *l2;
+
+ for (; l1; l1 = l2)
+ {
+ l2 = l1->next;
+ if (l1->format != NULL)
+ gfc_free_expr (l1->format);
+ gfc_free (l1);
+ }
+}
+
+
+/* Given a label number, search for and return a pointer to the label
+ structure, creating it if it does not exist. */
+
+gfc_st_label *
+gfc_get_st_label (int labelno)
+{
+ gfc_st_label *lp;
+
+ /* First see if the label is already in this namespace. */
+ for (lp = gfc_current_ns->st_labels; lp; lp = lp->next)
+ if (lp->value == labelno)
+ break;
+ if (lp != NULL)
+ return lp;
+
+ lp = gfc_getmem (sizeof (gfc_st_label));
+
+ lp->value = labelno;
+ lp->defined = ST_LABEL_UNKNOWN;
+ lp->referenced = ST_LABEL_UNKNOWN;
+
+ lp->prev = NULL;
+ lp->next = gfc_current_ns->st_labels;
+ if (gfc_current_ns->st_labels)
+ gfc_current_ns->st_labels->prev = lp;
+ gfc_current_ns->st_labels = lp;
+
+ return lp;
+}
+
+
+/* Called when a statement with a statement label is about to be
+ accepted. We add the label to the list of the current namespace,
+ making sure it hasn't been defined previously and referenced
+ correctly. */
+
+void
+gfc_define_st_label (gfc_st_label * lp, gfc_sl_type type, locus * label_locus)
+{
+ int labelno;
+
+ labelno = lp->value;
+
+ if (lp->defined != ST_LABEL_UNKNOWN)
+ gfc_error ("Duplicate statement label %d at %L and %L", labelno,
+ &lp->where, label_locus);
+ else
+ {
+ lp->where = *label_locus;
+
+ switch (type)
+ {
+ case ST_LABEL_FORMAT:
+ if (lp->referenced == ST_LABEL_TARGET)
+ gfc_error ("Label %d at %C already referenced as branch target",
+ labelno);
+ else
+ lp->defined = ST_LABEL_FORMAT;
+
+ break;
+
+ case ST_LABEL_TARGET:
+ if (lp->referenced == ST_LABEL_FORMAT)
+ gfc_error ("Label %d at %C already referenced as a format label",
+ labelno);
+ else
+ lp->defined = ST_LABEL_TARGET;
+
+ break;
+
+ default:
+ lp->defined = ST_LABEL_BAD_TARGET;
+ lp->referenced = ST_LABEL_BAD_TARGET;
+ }
+ }
+}
+
+
+/* Reference a label. Given a label and its type, see if that
+ reference is consistent with what is known about that label,
+ updating the unknown state. Returns FAILURE if something goes
+ wrong. */
+
+try
+gfc_reference_st_label (gfc_st_label * lp, gfc_sl_type type)
+{
+ gfc_sl_type label_type;
+ int labelno;
+ try rc;
+
+ if (lp == NULL)
+ return SUCCESS;
+
+ labelno = lp->value;
+
+ if (lp->defined != ST_LABEL_UNKNOWN)
+ label_type = lp->defined;
+ else
+ {
+ label_type = lp->referenced;
+ lp->where = *gfc_current_locus ();
+ }
+
+ if (label_type == ST_LABEL_FORMAT && type == ST_LABEL_TARGET)
+ {
+ gfc_error ("Label %d at %C previously used as a FORMAT label", labelno);
+ rc = FAILURE;
+ goto done;
+ }
+
+ if ((label_type == ST_LABEL_TARGET || label_type == ST_LABEL_BAD_TARGET)
+ && type == ST_LABEL_FORMAT)
+ {
+ gfc_error ("Label %d at %C previously used as branch target", labelno);
+ rc = FAILURE;
+ goto done;
+ }
+
+ lp->referenced = type;
+ rc = SUCCESS;
+
+done:
+ return rc;
+}
+
+
+/************** Symbol table management subroutines ****************/
+
+/* Basic details: Fortran 95 requires a potentially unlimited number
+ of distinct namespaces when compiling a program unit. This case
+ occurs during a compilation of internal subprograms because all of
+ the internal subprograms must be read before we can start
+ generating code for the host.
+
+ Given the tricky nature of the fortran grammar, we must be able to
+ undo changes made to a symbol table if the current interpretation
+ of a statement is found to be incorrect. Whenever a symbol is
+ looked up, we make a copy of it and link to it. All of these
+ symbols are kept in a singly linked list so that we can commit or
+ undo the changes at a later time.
+
+ A symtree may point to a symbol node outside of it's namespace. In
+ this case, that symbol has been used as a host associated variable
+ at some previous time. */
+
+/* Allocate a new namespace structure. */
+
+gfc_namespace *
+gfc_get_namespace (gfc_namespace * parent)
+{
+ gfc_namespace *ns;
+ gfc_typespec *ts;
+ gfc_intrinsic_op in;
+ int i;
+
+ ns = gfc_getmem (sizeof (gfc_namespace));
+ ns->sym_root = NULL;
+ ns->uop_root = NULL;
+ ns->default_access = ACCESS_UNKNOWN;
+ ns->parent = parent;
+
+ for (in = GFC_INTRINSIC_BEGIN; in != GFC_INTRINSIC_END; in++)
+ ns->operator_access[in] = ACCESS_UNKNOWN;
+
+ /* Initialize default implicit types. */
+ for (i = 'a'; i <= 'z'; i++)
+ {
+ ns->set_flag[i - 'a'] = 0;
+ ts = &ns->default_type[i - 'a'];
+
+ if (ns->parent != NULL)
+ {
+ /* Copy parent settings */
+ *ts = ns->parent->default_type[i - 'a'];
+ continue;
+ }
+
+ if (gfc_option.flag_implicit_none != 0)
+ {
+ gfc_clear_ts (ts);
+ continue;
+ }
+
+ if ('i' <= i && i <= 'n')
+ {
+ ts->type = BT_INTEGER;
+ ts->kind = gfc_default_integer_kind ();
+ }
+ else
+ {
+ ts->type = BT_REAL;
+ ts->kind = gfc_default_real_kind ();
+ }
+ }
+
+ return ns;
+}
+
+
+/* Comparison function for symtree nodes. */
+
+static int
+compare_symtree (void * _st1, void * _st2)
+{
+ gfc_symtree *st1, *st2;
+
+ st1 = (gfc_symtree *) _st1;
+ st2 = (gfc_symtree *) _st2;
+
+ return strcmp (st1->name, st2->name);
+}
+
+
+/* Allocate a new symtree node and associate it with the new symbol. */
+
+gfc_symtree *
+gfc_new_symtree (gfc_symtree ** root, const char *name)
+{
+ gfc_symtree *st;
+
+ st = gfc_getmem (sizeof (gfc_symtree));
+ strcpy (st->name, name);
+
+ gfc_insert_bbt (root, st, compare_symtree);
+ return st;
+}
+
+
+/* Delete a symbol from the tree. Does not free the symbol itself! */
+
+static void
+delete_symtree (gfc_symtree ** root, const char *name)
+{
+ gfc_symtree st, *st0;
+
+ st0 = gfc_find_symtree (*root, name);
+
+ strcpy (st.name, name);
+ gfc_delete_bbt (root, &st, compare_symtree);
+
+ gfc_free (st0);
+}
+
+
+/* Given a root symtree node and a name, try to find the symbol within
+ the namespace. Returns NULL if the symbol is not found. */
+
+gfc_symtree *
+gfc_find_symtree (gfc_symtree * st, const char *name)
+{
+ int c;
+
+ while (st != NULL)
+ {
+ c = strcmp (name, st->name);
+ if (c == 0)
+ return st;
+
+ st = (c < 0) ? st->left : st->right;
+ }
+
+ return NULL;
+}
+
+
+/* Given a name find a user operator node, creating it if it doesn't
+ exist. These are much simpler than symbols because they can't be
+ ambiguous with one another. */
+
+gfc_user_op *
+gfc_get_uop (const char *name)
+{
+ gfc_user_op *uop;
+ gfc_symtree *st;
+
+ st = gfc_find_symtree (gfc_current_ns->uop_root, name);
+ if (st != NULL)
+ return st->n.uop;
+
+ st = gfc_new_symtree (&gfc_current_ns->uop_root, name);
+
+ uop = st->n.uop = gfc_getmem (sizeof (gfc_user_op));
+ strcpy (uop->name, name);
+ uop->access = ACCESS_UNKNOWN;
+ uop->ns = gfc_current_ns;
+
+ return uop;
+}
+
+
+/* Given a name find the user operator node. Returns NULL if it does
+ not exist. */
+
+gfc_user_op *
+gfc_find_uop (const char *name, gfc_namespace * ns)
+{
+ gfc_symtree *st;
+
+ if (ns == NULL)
+ ns = gfc_current_ns;
+
+ st = gfc_find_symtree (ns->uop_root, name);
+ return (st == NULL) ? NULL : st->n.uop;
+}
+
+
+/* Remove a gfc_symbol structure and everything it points to. */
+
+void
+gfc_free_symbol (gfc_symbol * sym)
+{
+
+ if (sym == NULL)
+ return;
+
+ gfc_free_array_spec (sym->as);
+
+ free_components (sym->components);
+
+ gfc_free_expr (sym->value);
+
+ gfc_free_namelist (sym->namelist);
+
+ gfc_free_namespace (sym->formal_ns);
+
+ gfc_free_interface (sym->generic);
+
+ gfc_free_formal_arglist (sym->formal);
+
+ gfc_free (sym);
+}
+
+
+/* Allocate and initialize a new symbol node. */
+
+gfc_symbol *
+gfc_new_symbol (const char *name, gfc_namespace * ns)
+{
+ gfc_symbol *p;
+
+ p = gfc_getmem (sizeof (gfc_symbol));
+
+ gfc_clear_ts (&p->ts);
+ gfc_clear_attr (&p->attr);
+ p->ns = ns;
+
+ p->declared_at = *gfc_current_locus ();
+
+ if (strlen (name) > GFC_MAX_SYMBOL_LEN)
+ gfc_internal_error ("new_symbol(): Symbol name too long");
+
+ strcpy (p->name, name);
+ return p;
+}
+
+
+/* Generate an error if a symbol is ambiguous. */
+
+static void
+ambiguous_symbol (const char *name, gfc_symtree * st)
+{
+
+ if (st->n.sym->module[0])
+ gfc_error ("Name '%s' at %C is an ambiguous reference to '%s' "
+ "from module '%s'", name, st->n.sym->name, st->n.sym->module);
+ else
+ gfc_error ("Name '%s' at %C is an ambiguous reference to '%s' "
+ "from current program unit", name, st->n.sym->name);
+}
+
+
+/* Search for a symbol starting in the current namespace, resorting to
+ any parent namespaces if requested by a nonzero parent_flag.
+ Returns nonzero if the symbol is ambiguous. */
+
+int
+gfc_find_sym_tree (const char *name, gfc_namespace * ns, int parent_flag,
+ gfc_symtree ** result)
+{
+ gfc_symtree *st;
+
+ if (ns == NULL)
+ ns = gfc_current_ns;
+
+ do
+ {
+ st = gfc_find_symtree (ns->sym_root, name);
+ if (st != NULL)
+ {
+ *result = st;
+ if (st->ambiguous)
+ {
+ ambiguous_symbol (name, st);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (!parent_flag)
+ break;
+
+ ns = ns->parent;
+ }
+ while (ns != NULL);
+
+ *result = NULL;
+ return 0;
+}
+
+
+int
+gfc_find_symbol (const char *name, gfc_namespace * ns, int parent_flag,
+ gfc_symbol ** result)
+{
+ gfc_symtree *st;
+ int i;
+
+ i = gfc_find_sym_tree (name, ns, parent_flag, &st);
+
+ if (st == NULL)
+ *result = NULL;
+ else
+ *result = st->n.sym;
+
+ return i;
+}
+
+
+/* Save symbol with the information necessary to back it out. */
+
+static void
+save_symbol_data (gfc_symbol * sym)
+{
+
+ if (sym->new || sym->old_symbol != NULL)
+ return;
+
+ sym->old_symbol = gfc_getmem (sizeof (gfc_symbol));
+ *(sym->old_symbol) = *sym;
+
+ sym->tlink = changed_syms;
+ changed_syms = sym;
+}
+
+
+/* Given a name, find a symbol, or create it if it does not exist yet
+ in the current namespace. If the symbol is found we make sure that
+ it's OK.
+
+ The integer return code indicates
+ 0 All OK
+ 1 The symbol name was ambiguous
+ 2 The name meant to be established was already host associated.
+
+ So if the return value is nonzero, then an error was issued. */
+
+int
+gfc_get_sym_tree (const char *name, gfc_namespace * ns, gfc_symtree ** result)
+{
+ gfc_symtree *st;
+ gfc_symbol *p;
+
+ /* This doesn't usually happen during resolution. */
+ if (ns == NULL)
+ ns = gfc_current_ns;
+
+ /* Try to find the symbol in ns. */
+ st = gfc_find_symtree (ns->sym_root, name);
+
+ if (st == NULL)
+ {
+ /* If not there, create a new symbol. */
+ p = gfc_new_symbol (name, ns);
+
+ /* Add to the list of tentative symbols. */
+ p->old_symbol = NULL;
+ p->tlink = changed_syms;
+ p->mark = 1;
+ p->new = 1;
+ changed_syms = p;
+
+ st = gfc_new_symtree (&ns->sym_root, name);
+ st->n.sym = p;
+ p->refs++;
+
+ }
+ else
+ {
+ /* Make sure the existing symbol is OK. */
+ if (st->ambiguous)
+ {
+ ambiguous_symbol (name, st);
+ return 1;
+ }
+
+ p = st->n.sym;
+
+ if (p->ns != ns && (!p->attr.function || ns->proc_name != p))
+ {
+ /* Symbol is from another namespace. */
+ gfc_error ("Symbol '%s' at %C has already been host associated",
+ name);
+ return 2;
+ }
+
+ p->mark = 1;
+
+ /* Copy in case this symbol is changed. */
+ save_symbol_data (p);
+ }
+
+ *result = st;
+ return 0;
+}
+
+
+int
+gfc_get_symbol (const char *name, gfc_namespace * ns, gfc_symbol ** result)
+{
+ gfc_symtree *st;
+ int i;
+
+
+ i = gfc_get_sym_tree (name, ns, &st);
+ if (i != 0)
+ return i;
+
+ if (st)
+ *result = st->n.sym;
+ else
+ *result = NULL;
+ return i;
+}
+
+
+/* Subroutine that searches for a symbol, creating it if it doesn't
+ exist, but tries to host-associate the symbol if possible. */
+
+int
+gfc_get_ha_sym_tree (const char *name, gfc_symtree ** result)
+{
+ gfc_symtree *st;
+ int i;
+
+ i = gfc_find_sym_tree (name, gfc_current_ns, 0, &st);
+ if (st != NULL)
+ {
+ save_symbol_data (st->n.sym);
+
+ *result = st;
+ return i;
+ }
+
+ if (gfc_current_ns->parent != NULL)
+ {
+ i = gfc_find_sym_tree (name, gfc_current_ns->parent, 1, &st);
+ if (i)
+ return i;
+
+ if (st != NULL)
+ {
+ *result = st;
+ return 0;
+ }
+ }
+
+ return gfc_get_sym_tree (name, gfc_current_ns, result);
+}
+
+
+int
+gfc_get_ha_symbol (const char *name, gfc_symbol ** result)
+{
+ int i;
+ gfc_symtree *st;
+
+ i = gfc_get_ha_sym_tree (name, &st);
+
+ if (st)
+ *result = st->n.sym;
+ else
+ *result = NULL;
+
+ return i;
+}
+
+/* Return true if both symbols could refer to the same data object. Does
+ not take account of aliasing due to equivalence statements. */
+
+int
+gfc_symbols_could_alias (gfc_symbol * lsym, gfc_symbol * rsym)
+{
+ /* Aliasing isn't possible if the symbols have different base types. */
+ if (gfc_compare_types (&lsym->ts, &rsym->ts) == 0)
+ return 0;
+
+ /* Pointers can point to other pointers, target objects and allocatable
+ objects. Two allocatable objects cannot share the same storage. */
+ if (lsym->attr.pointer
+ && (rsym->attr.pointer || rsym->attr.allocatable || rsym->attr.target))
+ return 1;
+ if (lsym->attr.target && rsym->attr.pointer)
+ return 1;
+ if (lsym->attr.allocatable && rsym->attr.pointer)
+ return 1;
+
+ return 0;
+}
+
+
+/* Undoes all the changes made to symbols in the current statement.
+ This subroutine is made simpler due to the fact that attributes are
+ never removed once added. */
+
+void
+gfc_undo_symbols (void)
+{
+ gfc_symbol *p, *q, *old;
+
+ for (p = changed_syms; p; p = q)
+ {
+ q = p->tlink;
+
+ if (p->new)
+ {
+ /* Symbol was new. */
+ delete_symtree (&p->ns->sym_root, p->name);
+
+ p->refs--;
+ if (p->refs < 0)
+ gfc_internal_error ("gfc_undo_symbols(): Negative refs");
+ if (p->refs == 0)
+ gfc_free_symbol (p);
+ continue;
+ }
+
+ /* Restore previous state of symbol. Just copy simple stuff. */
+ p->mark = 0;
+ old = p->old_symbol;
+
+ p->ts.type = old->ts.type;
+ p->ts.kind = old->ts.kind;
+
+ p->attr = old->attr;
+
+ if (p->value != old->value)
+ {
+ gfc_free_expr (old->value);
+ p->value = NULL;
+ }
+
+ if (p->as != old->as)
+ {
+ if (p->as)
+ gfc_free_array_spec (p->as);
+ p->as = old->as;
+ }
+
+ p->generic = old->generic;
+ p->component_access = old->component_access;
+
+ if (p->namelist != NULL && old->namelist == NULL)
+ {
+ gfc_free_namelist (p->namelist);
+ p->namelist = NULL;
+ }
+ else
+ {
+
+ if (p->namelist_tail != old->namelist_tail)
+ {
+ gfc_free_namelist (old->namelist_tail);
+ old->namelist_tail->next = NULL;
+ }
+ }
+
+ p->namelist_tail = old->namelist_tail;
+
+ if (p->formal != old->formal)
+ {
+ gfc_free_formal_arglist (p->formal);
+ p->formal = old->formal;
+ }
+
+ gfc_free (p->old_symbol);
+ p->old_symbol = NULL;
+ p->tlink = NULL;
+ }
+
+ changed_syms = NULL;
+}
+
+
+/* Makes the changes made in the current statement permanent-- gets
+ rid of undo information. */
+
+void
+gfc_commit_symbols (void)
+{
+ gfc_symbol *p, *q;
+
+ for (p = changed_syms; p; p = q)
+ {
+ q = p->tlink;
+ p->tlink = NULL;
+ p->mark = 0;
+ p->new = 0;
+
+ if (p->old_symbol != NULL)
+ {
+ gfc_free (p->old_symbol);
+ p->old_symbol = NULL;
+ }
+ }
+
+ changed_syms = NULL;
+}
+
+
+/* Recursive function that deletes an entire tree and all the user
+ operator nodes that it contains. */
+
+static void
+free_uop_tree (gfc_symtree * uop_tree)
+{
+
+ if (uop_tree == NULL)
+ return;
+
+ free_uop_tree (uop_tree->left);
+ free_uop_tree (uop_tree->right);
+
+ gfc_free_interface (uop_tree->n.uop->operator);
+
+ gfc_free (uop_tree->n.uop);
+ gfc_free (uop_tree);
+}
+
+
+/* Recursive function that deletes an entire tree and all the symbols
+ that it contains. */
+
+static void
+free_sym_tree (gfc_symtree * sym_tree)
+{
+ gfc_namespace *ns;
+ gfc_symbol *sym;
+
+ if (sym_tree == NULL)
+ return;
+
+ free_sym_tree (sym_tree->left);
+ free_sym_tree (sym_tree->right);
+
+ sym = sym_tree->n.sym;
+
+ sym->refs--;
+ if (sym->refs < 0)
+ gfc_internal_error ("free_sym_tree(): Negative refs");
+
+ if (sym->formal_ns != NULL && sym->refs == 1)
+ {
+ /* As formal_ns contains a reference to sym, delete formal_ns just
+ before the deletion of sym. */
+ ns = sym->formal_ns;
+ sym->formal_ns = NULL;
+ gfc_free_namespace (ns);
+ }
+ else if (sym->refs == 0)
+ {
+ /* Go ahead and delete the symbol. */
+ gfc_free_symbol (sym);
+ }
+
+ gfc_free (sym_tree);
+}
+
+
+/* Free a namespace structure and everything below it. Interface
+ lists associated with intrinsic operators are not freed. These are
+ taken care of when a specific name is freed. */
+
+void
+gfc_free_namespace (gfc_namespace * ns)
+{
+ gfc_charlen *cl, *cl2;
+ gfc_namespace *p, *q;
+ gfc_intrinsic_op i;
+
+ if (ns == NULL)
+ return;
+
+ gfc_free_statements (ns->code);
+
+ free_sym_tree (ns->sym_root);
+ free_uop_tree (ns->uop_root);
+
+ for (cl = ns->cl_list; cl; cl = cl2)
+ {
+ cl2 = cl->next;
+ gfc_free_expr (cl->length);
+ gfc_free (cl);
+ }
+
+ free_st_labels (ns->st_labels);
+
+ gfc_free_equiv (ns->equiv);
+
+ for (i = GFC_INTRINSIC_BEGIN; i != GFC_INTRINSIC_END; i++)
+ gfc_free_interface (ns->operator[i]);
+
+ gfc_free_data (ns->data);
+ p = ns->contained;
+ gfc_free (ns);
+
+ /* Recursively free any contained namespaces. */
+ while (p != NULL)
+ {
+ q = p;
+ p = p->sibling;
+
+ gfc_free_namespace (q);
+ }
+}
+
+
+void
+gfc_symbol_init_2 (void)
+{
+
+ gfc_current_ns = gfc_get_namespace (NULL);
+}
+
+
+void
+gfc_symbol_done_2 (void)
+{
+
+ gfc_free_namespace (gfc_current_ns);
+ gfc_current_ns = NULL;
+}
+
+
+/* Clear mark bits from symbol nodes associated with a symtree node. */
+
+static void
+clear_sym_mark (gfc_symtree * st)
+{
+
+ st->n.sym->mark = 0;
+}
+
+
+/* Recursively traverse the symtree nodes. */
+
+static void
+traverse_symtree (gfc_symtree * st, void (*func) (gfc_symtree *))
+{
+
+ if (st != NULL)
+ {
+ (*func) (st);
+
+ traverse_symtree (st->left, func);
+ traverse_symtree (st->right, func);
+ }
+}
+
+
+void
+gfc_traverse_symtree (gfc_namespace * ns, void (*func) (gfc_symtree *))
+{
+
+ traverse_symtree (ns->sym_root, func);
+}
+
+
+/* Recursive namespace traversal function. */
+
+static void
+traverse_ns (gfc_symtree * st, void (*func) (gfc_symbol *))
+{
+
+ if (st == NULL)
+ return;
+
+ if (st->n.sym->mark == 0)
+ (*func) (st->n.sym);
+ st->n.sym->mark = 1;
+
+ traverse_ns (st->left, func);
+ traverse_ns (st->right, func);
+}
+
+
+/* Call a given function for all symbols in the namespace. We take
+ care that each gfc_symbol node is called exactly once. */
+
+void
+gfc_traverse_ns (gfc_namespace * ns, void (*func) (gfc_symbol *))
+{
+
+ gfc_traverse_symtree (ns, clear_sym_mark);
+
+ traverse_ns (ns->sym_root, func);
+}
+
+
+/* Given a symbol, mark it as SAVEd if it is allowed. */
+
+static void
+save_symbol (gfc_symbol * sym)
+{
+
+ if (sym->attr.use_assoc)
+ return;
+
+ if (sym->attr.common)
+ {
+ gfc_add_saved_common (&sym->attr, &sym->declared_at);
+ return;
+ }
+
+ if (sym->attr.in_common
+ || sym->attr.dummy
+ || sym->attr.flavor != FL_VARIABLE)
+ return;
+
+ gfc_add_save (&sym->attr, &sym->declared_at);
+}
+
+
+/* Mark those symbols which can be SAVEd as such. */
+
+void
+gfc_save_all (gfc_namespace * ns)
+{
+
+ gfc_traverse_ns (ns, save_symbol);
+}
+
+
+#ifdef GFC_DEBUG
+/* Make sure that no changes to symbols are pending. */
+
+void
+gfc_symbol_state(void) {
+
+ if (changed_syms != NULL)
+ gfc_internal_error("Symbol changes still pending!");
+}
+#endif
+