aboutsummaryrefslogtreecommitdiff
path: root/gcc/fortran/symbol.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/fortran/symbol.cc')
-rw-r--r--gcc/fortran/symbol.cc5251
1 files changed, 5251 insertions, 0 deletions
diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc
new file mode 100644
index 0000000..1a4b022
--- /dev/null
+++ b/gcc/fortran/symbol.cc
@@ -0,0 +1,5251 @@
+/* Maintain binary trees of symbols.
+ Copyright (C) 2000-2022 Free Software Foundation, Inc.
+ Contributed by Andy Vaught
+
+This file is part of GCC.
+
+GCC 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 3, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "options.h"
+#include "gfortran.h"
+#include "parse.h"
+#include "match.h"
+#include "constructor.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 ("UNION", FL_UNION), minit ("STRUCTURE", FL_STRUCT),
+ 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)
+};
+
+const mstring save_status[] =
+{
+ minit ("UNKNOWN", SAVE_NONE),
+ minit ("EXPLICIT-SAVE", SAVE_EXPLICIT),
+ minit ("IMPLICIT-SAVE", SAVE_IMPLICIT),
+};
+
+/* Set the mstrings for DTIO procedure names. */
+const mstring dtio_procs[] =
+{
+ minit ("_dtio_formatted_read", DTIO_RF),
+ minit ("_dtio_formatted_write", DTIO_WF),
+ minit ("_dtio_unformatted_read", DTIO_RUF),
+ minit ("_dtio_unformatted_write", DTIO_WUF),
+};
+
+/* 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;
+gfc_namespace *gfc_global_ns_list;
+
+gfc_gsymbol *gfc_gsym_root = NULL;
+
+gfc_symbol *gfc_derived_types;
+
+static gfc_undo_change_set default_undo_chgset_var = { vNULL, vNULL, NULL };
+static gfc_undo_change_set *latest_undo_chgset = &default_undo_chgset_var;
+
+
+/*********** IMPLICIT NONE and IMPLICIT statement handlers ***********/
+
+/* The following static variable indicates whether a particular element has
+ been explicitly set or not. */
+
+static int new_flag[GFC_LETTERS];
+
+
+/* Handle a correctly parsed IMPLICIT NONE. */
+
+void
+gfc_set_implicit_none (bool type, bool external, locus *loc)
+{
+ int i;
+
+ if (external)
+ gfc_current_ns->has_implicit_none_export = 1;
+
+ if (type)
+ {
+ gfc_current_ns->seen_implicit_none = 1;
+ for (i = 0; i < GFC_LETTERS; i++)
+ {
+ if (gfc_current_ns->set_flag[i])
+ {
+ gfc_error_now ("IMPLICIT NONE (type) statement at %L following an "
+ "IMPLICIT statement", loc);
+ return;
+ }
+ gfc_clear_ts (&gfc_current_ns->default_type[i]);
+ gfc_current_ns->set_flag[i] = 1;
+ }
+ }
+}
+
+
+/* Reset the implicit range flags. */
+
+void
+gfc_clear_new_implicit (void)
+{
+ int i;
+
+ for (i = 0; i < GFC_LETTERS; i++)
+ new_flag[i] = 0;
+}
+
+
+/* Prepare for a new implicit range. Sets flags in new_flag[]. */
+
+bool
+gfc_add_new_implicit_range (int c1, int c2)
+{
+ int i;
+
+ c1 -= 'a';
+ c2 -= 'a';
+
+ for (i = c1; i <= c2; i++)
+ {
+ if (new_flag[i])
+ {
+ gfc_error ("Letter %qc already set in IMPLICIT statement at %C",
+ i + 'A');
+ return false;
+ }
+
+ new_flag[i] = 1;
+ }
+
+ return true;
+}
+
+
+/* Add a matched implicit range for gfc_set_implicit(). Check if merging
+ the new implicit types back into the existing types will work. */
+
+bool
+gfc_merge_new_implicit (gfc_typespec *ts)
+{
+ int i;
+
+ if (gfc_current_ns->seen_implicit_none)
+ {
+ gfc_error ("Cannot specify IMPLICIT at %C after IMPLICIT NONE");
+ return false;
+ }
+
+ for (i = 0; i < GFC_LETTERS; i++)
+ {
+ if (new_flag[i])
+ {
+ if (gfc_current_ns->set_flag[i])
+ {
+ gfc_error ("Letter %qc already has an IMPLICIT type at %C",
+ i + 'A');
+ return false;
+ }
+
+ gfc_current_ns->default_type[i] = *ts;
+ gfc_current_ns->implicit_loc[i] = gfc_current_locus;
+ gfc_current_ns->set_flag[i] = 1;
+ }
+ }
+ return true;
+}
+
+
+/* Given a symbol, return a pointer to the typespec for its default type. */
+
+gfc_typespec *
+gfc_get_default_type (const char *name, gfc_namespace *ns)
+{
+ char letter;
+
+ letter = name[0];
+
+ if (flag_allow_leading_underscore && letter == '_')
+ gfc_fatal_error ("Option %<-fallow-leading-underscore%> is for use only by "
+ "gfortran developers, and should not be used for "
+ "implicitly typed variables");
+
+ if (letter < 'a' || letter > 'z')
+ gfc_internal_error ("gfc_get_default_type(): Bad symbol %qs", name);
+
+ if (ns == NULL)
+ ns = gfc_current_ns;
+
+ return &ns->default_type[letter - 'a'];
+}
+
+
+/* Recursively append candidate SYM to CANDIDATES. Store the number of
+ candidates in CANDIDATES_LEN. */
+
+static void
+lookup_symbol_fuzzy_find_candidates (gfc_symtree *sym,
+ char **&candidates,
+ size_t &candidates_len)
+{
+ gfc_symtree *p;
+
+ if (sym == NULL)
+ return;
+
+ if (sym->n.sym->ts.type != BT_UNKNOWN && sym->n.sym->ts.type != BT_PROCEDURE)
+ vec_push (candidates, candidates_len, sym->name);
+ p = sym->left;
+ if (p)
+ lookup_symbol_fuzzy_find_candidates (p, candidates, candidates_len);
+
+ p = sym->right;
+ if (p)
+ lookup_symbol_fuzzy_find_candidates (p, candidates, candidates_len);
+}
+
+
+/* Lookup symbol SYM_NAME fuzzily, taking names in SYMBOL into account. */
+
+static const char*
+lookup_symbol_fuzzy (const char *sym_name, gfc_symbol *symbol)
+{
+ char **candidates = NULL;
+ size_t candidates_len = 0;
+ lookup_symbol_fuzzy_find_candidates (symbol->ns->sym_root, candidates,
+ candidates_len);
+ return gfc_closest_fuzzy_match (sym_name, candidates);
+}
+
+
+/* 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. */
+
+bool
+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->name, ns);
+
+ if (ts->type == BT_UNKNOWN)
+ {
+ if (error_flag && !sym->attr.untyped && !gfc_query_suppress_errors ())
+ {
+ const char *guessed = lookup_symbol_fuzzy (sym->name, sym);
+ if (guessed)
+ gfc_error ("Symbol %qs at %L has no IMPLICIT type"
+ "; did you mean %qs?",
+ sym->name, &sym->declared_at, guessed);
+ else
+ gfc_error ("Symbol %qs at %L has no IMPLICIT type",
+ sym->name, &sym->declared_at);
+ sym->attr.untyped = 1; /* Ensure we only give an error once. */
+ }
+
+ return false;
+ }
+
+ sym->ts = *ts;
+ sym->attr.implicit_type = 1;
+
+ if (ts->type == BT_CHARACTER && ts->u.cl)
+ sym->ts.u.cl = gfc_new_charlen (sym->ns, ts->u.cl);
+ else if (ts->type == BT_CLASS
+ && !gfc_build_class_symbol (&sym->ts, &sym->attr, &sym->as))
+ return false;
+
+ if (sym->attr.is_bind_c == 1 && warn_c_binding_type)
+ {
+ /* BIND(C) variables should not be implicitly declared. */
+ gfc_warning_now (OPT_Wc_binding_type, "Implicitly declared BIND(C) "
+ "variable %qs at %L may not be C interoperable",
+ sym->name, &sym->declared_at);
+ sym->ts.f90_type = sym->ts.type;
+ }
+
+ if (sym->attr.dummy != 0)
+ {
+ if (sym->ns->proc_name != NULL
+ && (sym->ns->proc_name->attr.subroutine != 0
+ || sym->ns->proc_name->attr.function != 0)
+ && sym->ns->proc_name->attr.is_bind_c != 0
+ && warn_c_binding_type)
+ {
+ /* Dummy args to a BIND(C) routine may not be interoperable if
+ they are implicitly typed. */
+ gfc_warning_now (OPT_Wc_binding_type, "Implicitly declared variable "
+ "%qs at %L may not be C interoperable but it is a "
+ "dummy argument to the BIND(C) procedure %qs at %L",
+ sym->name, &(sym->declared_at),
+ sym->ns->proc_name->name,
+ &(sym->ns->proc_name->declared_at));
+ sym->ts.f90_type = sym->ts.type;
+ }
+ }
+
+ return true;
+}
+
+
+/* This function is called from parse.c(parse_progunit) to check the
+ type of the function is not implicitly typed in the host namespace
+ and to implicitly type the function result, if necessary. */
+
+void
+gfc_check_function_type (gfc_namespace *ns)
+{
+ gfc_symbol *proc = ns->proc_name;
+
+ if (!proc->attr.contained || proc->result->attr.implicit_type)
+ return;
+
+ if (proc->result->ts.type == BT_UNKNOWN && proc->result->ts.interface == NULL)
+ {
+ if (gfc_set_default_type (proc->result, 0, gfc_current_ns))
+ {
+ if (proc->result != proc)
+ {
+ proc->ts = proc->result->ts;
+ proc->as = gfc_copy_array_spec (proc->result->as);
+ proc->attr.dimension = proc->result->attr.dimension;
+ proc->attr.pointer = proc->result->attr.pointer;
+ proc->attr.allocatable = proc->result->attr.allocatable;
+ }
+ }
+ else if (!proc->result->attr.proc_pointer)
+ {
+ gfc_error ("Function result %qs at %L has no IMPLICIT type",
+ proc->result->name, &proc->result->declared_at);
+ proc->result->attr.untyped = 1;
+ }
+ }
+}
+
+
+/******************** 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; }
+#define conf_std(a, b, std) if (attr->a && attr->b)\
+ {\
+ a1 = a;\
+ a2 = b;\
+ standard = std;\
+ goto conflict_std;\
+ }
+
+bool
+gfc_check_conflict (symbol_attribute *attr, const char *name, locus *where)
+{
+ static const char *dummy = "DUMMY", *save = "SAVE", *pointer = "POINTER",
+ *target = "TARGET", *external = "EXTERNAL", *intent = "INTENT",
+ *intent_in = "INTENT(IN)", *intrinsic = "INTRINSIC",
+ *intent_out = "INTENT(OUT)", *intent_inout = "INTENT(INOUT)",
+ *allocatable = "ALLOCATABLE", *elemental = "ELEMENTAL",
+ *privat = "PRIVATE", *recursive = "RECURSIVE",
+ *in_common = "COMMON", *result = "RESULT", *in_namelist = "NAMELIST",
+ *publik = "PUBLIC", *optional = "OPTIONAL", *entry = "ENTRY",
+ *function = "FUNCTION", *subroutine = "SUBROUTINE",
+ *dimension = "DIMENSION", *in_equivalence = "EQUIVALENCE",
+ *use_assoc = "USE ASSOCIATED", *cray_pointer = "CRAY POINTER",
+ *cray_pointee = "CRAY POINTEE", *data = "DATA", *value = "VALUE",
+ *volatile_ = "VOLATILE", *is_protected = "PROTECTED",
+ *is_bind_c = "BIND(C)", *procedure = "PROCEDURE",
+ *proc_pointer = "PROCEDURE POINTER", *abstract = "ABSTRACT",
+ *asynchronous = "ASYNCHRONOUS", *codimension = "CODIMENSION",
+ *contiguous = "CONTIGUOUS", *generic = "GENERIC", *automatic = "AUTOMATIC",
+ *pdt_len = "LEN", *pdt_kind = "KIND";
+ static const char *threadprivate = "THREADPRIVATE";
+ static const char *omp_declare_target = "OMP DECLARE TARGET";
+ static const char *omp_declare_target_link = "OMP DECLARE TARGET LINK";
+ static const char *oacc_declare_copyin = "OACC DECLARE COPYIN";
+ static const char *oacc_declare_create = "OACC DECLARE CREATE";
+ static const char *oacc_declare_deviceptr = "OACC DECLARE DEVICEPTR";
+ static const char *oacc_declare_device_resident =
+ "OACC DECLARE DEVICE_RESIDENT";
+
+ const char *a1, *a2;
+ int standard;
+
+ if (attr->artificial)
+ return true;
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (attr->pointer && attr->intent != INTENT_UNKNOWN)
+ {
+ a1 = pointer;
+ a2 = intent;
+ standard = GFC_STD_F2003;
+ goto conflict_std;
+ }
+
+ if (attr->in_namelist && (attr->allocatable || attr->pointer))
+ {
+ a1 = in_namelist;
+ a2 = attr->allocatable ? allocatable : pointer;
+ standard = GFC_STD_F2003;
+ goto conflict_std;
+ }
+
+ /* Check for attributes not allowed in a BLOCK DATA. */
+ if (gfc_current_state () == COMP_BLOCK_DATA)
+ {
+ a1 = NULL;
+
+ if (attr->in_namelist)
+ a1 = in_namelist;
+ if (attr->allocatable)
+ a1 = allocatable;
+ if (attr->external)
+ a1 = external;
+ if (attr->optional)
+ a1 = optional;
+ if (attr->access == ACCESS_PRIVATE)
+ a1 = privat;
+ if (attr->access == ACCESS_PUBLIC)
+ a1 = publik;
+ 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 false;
+ }
+ }
+
+ if (attr->save == SAVE_EXPLICIT)
+ {
+ conf (dummy, save);
+ conf (in_common, save);
+ conf (result, save);
+ conf (automatic, save);
+
+ switch (attr->flavor)
+ {
+ case FL_PROGRAM:
+ case FL_BLOCK_DATA:
+ case FL_MODULE:
+ case FL_LABEL:
+ case_fl_struct:
+ case FL_PARAMETER:
+ a1 = gfc_code2string (flavors, attr->flavor);
+ a2 = save;
+ goto conflict;
+ case FL_NAMELIST:
+ gfc_error ("Namelist group name at %L cannot have the "
+ "SAVE attribute", where);
+ return false;
+ case FL_PROCEDURE:
+ /* Conflicts between SAVE and PROCEDURE will be checked at
+ resolution stage, see "resolve_fl_procedure". */
+ case FL_VARIABLE:
+ default:
+ break;
+ }
+ }
+
+ /* The copying of procedure dummy arguments for module procedures in
+ a submodule occur whilst the current state is COMP_CONTAINS. It
+ is necessary, therefore, to let this through. */
+ if (name && attr->dummy
+ && (attr->function || attr->subroutine)
+ && gfc_current_state () == COMP_CONTAINS
+ && !(gfc_new_block && gfc_new_block->abr_modproc_decl))
+ gfc_error_now ("internal procedure %qs at %L conflicts with "
+ "DUMMY argument", name, where);
+
+ conf (dummy, entry);
+ conf (dummy, intrinsic);
+ conf (dummy, threadprivate);
+ conf (dummy, omp_declare_target);
+ conf (dummy, omp_declare_target_link);
+ conf (pointer, target);
+ conf (pointer, intrinsic);
+ conf (pointer, elemental);
+ conf (pointer, codimension);
+ conf (allocatable, elemental);
+
+ conf (in_common, automatic);
+ conf (result, automatic);
+ conf (use_assoc, automatic);
+ conf (dummy, automatic);
+
+ conf (target, external);
+ conf (target, intrinsic);
+
+ if (!attr->if_source)
+ conf (external, dimension); /* See Fortran 95's R504. */
+
+ conf (external, intrinsic);
+ conf (entry, intrinsic);
+ conf (abstract, intrinsic);
+
+ if ((attr->if_source == IFSRC_DECL && !attr->procedure) || attr->contained)
+ conf (external, subroutine);
+
+ if (attr->proc_pointer && !gfc_notify_std (GFC_STD_F2003,
+ "Procedure pointer at %C"))
+ return false;
+
+ conf (allocatable, pointer);
+ conf_std (allocatable, dummy, GFC_STD_F2003);
+ conf_std (allocatable, function, GFC_STD_F2003);
+ conf_std (allocatable, result, GFC_STD_F2003);
+ conf_std (elemental, recursive, GFC_STD_F2018);
+
+ conf (in_common, dummy);
+ conf (in_common, allocatable);
+ conf (in_common, codimension);
+ conf (in_common, result);
+
+ conf (in_equivalence, use_assoc);
+ conf (in_equivalence, codimension);
+ conf (in_equivalence, dummy);
+ conf (in_equivalence, target);
+ conf (in_equivalence, pointer);
+ conf (in_equivalence, function);
+ conf (in_equivalence, result);
+ conf (in_equivalence, entry);
+ conf (in_equivalence, allocatable);
+ conf (in_equivalence, threadprivate);
+ conf (in_equivalence, omp_declare_target);
+ conf (in_equivalence, omp_declare_target_link);
+ conf (in_equivalence, oacc_declare_create);
+ conf (in_equivalence, oacc_declare_copyin);
+ conf (in_equivalence, oacc_declare_deviceptr);
+ conf (in_equivalence, oacc_declare_device_resident);
+ conf (in_equivalence, is_bind_c);
+
+ conf (dummy, result);
+ conf (entry, result);
+ conf (generic, result);
+ conf (generic, omp_declare_target);
+ conf (generic, omp_declare_target_link);
+
+ conf (function, subroutine);
+
+ if (!function && !subroutine)
+ conf (is_bind_c, dummy);
+
+ conf (is_bind_c, cray_pointer);
+ conf (is_bind_c, cray_pointee);
+ conf (is_bind_c, codimension);
+ conf (is_bind_c, allocatable);
+ conf (is_bind_c, elemental);
+
+ /* Need to also get volatile attr, according to 5.1 of F2003 draft.
+ Parameter conflict caught below. Also, value cannot be specified
+ for a dummy procedure. */
+
+ /* Cray pointer/pointee conflicts. */
+ conf (cray_pointer, cray_pointee);
+ conf (cray_pointer, dimension);
+ conf (cray_pointer, codimension);
+ conf (cray_pointer, contiguous);
+ conf (cray_pointer, pointer);
+ conf (cray_pointer, target);
+ conf (cray_pointer, allocatable);
+ conf (cray_pointer, external);
+ conf (cray_pointer, intrinsic);
+ conf (cray_pointer, in_namelist);
+ conf (cray_pointer, function);
+ conf (cray_pointer, subroutine);
+ conf (cray_pointer, entry);
+
+ conf (cray_pointee, allocatable);
+ conf (cray_pointee, contiguous);
+ conf (cray_pointee, codimension);
+ conf (cray_pointee, intent);
+ conf (cray_pointee, optional);
+ conf (cray_pointee, dummy);
+ conf (cray_pointee, target);
+ conf (cray_pointee, intrinsic);
+ conf (cray_pointee, pointer);
+ conf (cray_pointee, entry);
+ conf (cray_pointee, in_common);
+ conf (cray_pointee, in_equivalence);
+ conf (cray_pointee, threadprivate);
+ conf (cray_pointee, omp_declare_target);
+ conf (cray_pointee, omp_declare_target_link);
+ conf (cray_pointee, oacc_declare_create);
+ conf (cray_pointee, oacc_declare_copyin);
+ conf (cray_pointee, oacc_declare_deviceptr);
+ conf (cray_pointee, oacc_declare_device_resident);
+
+ conf (data, dummy);
+ conf (data, function);
+ conf (data, result);
+ conf (data, allocatable);
+
+ conf (value, pointer)
+ conf (value, allocatable)
+ conf (value, subroutine)
+ conf (value, function)
+ conf (value, volatile_)
+ conf (value, dimension)
+ conf (value, codimension)
+ conf (value, external)
+
+ conf (codimension, result)
+
+ if (attr->value
+ && (attr->intent == INTENT_OUT || attr->intent == INTENT_INOUT))
+ {
+ a1 = value;
+ a2 = attr->intent == INTENT_OUT ? intent_out : intent_inout;
+ goto conflict;
+ }
+
+ conf (is_protected, intrinsic)
+ conf (is_protected, in_common)
+
+ conf (asynchronous, intrinsic)
+ conf (asynchronous, external)
+
+ conf (volatile_, intrinsic)
+ conf (volatile_, external)
+
+ if (attr->volatile_ && attr->intent == INTENT_IN)
+ {
+ a1 = volatile_;
+ a2 = intent_in;
+ goto conflict;
+ }
+
+ conf (procedure, allocatable)
+ conf (procedure, dimension)
+ conf (procedure, codimension)
+ conf (procedure, intrinsic)
+ conf (procedure, target)
+ conf (procedure, value)
+ conf (procedure, volatile_)
+ conf (procedure, asynchronous)
+ conf (procedure, entry)
+
+ conf (proc_pointer, abstract)
+ conf (proc_pointer, omp_declare_target)
+ conf (proc_pointer, omp_declare_target_link)
+
+ conf (entry, omp_declare_target)
+ conf (entry, omp_declare_target_link)
+ conf (entry, oacc_declare_create)
+ conf (entry, oacc_declare_copyin)
+ conf (entry, oacc_declare_deviceptr)
+ conf (entry, oacc_declare_device_resident)
+
+ conf (pdt_kind, allocatable)
+ conf (pdt_kind, pointer)
+ conf (pdt_kind, dimension)
+ conf (pdt_kind, codimension)
+
+ conf (pdt_len, allocatable)
+ conf (pdt_len, pointer)
+ conf (pdt_len, dimension)
+ conf (pdt_len, codimension)
+ conf (pdt_len, pdt_kind)
+
+ if (attr->access == ACCESS_PRIVATE)
+ {
+ a1 = privat;
+ conf2 (pdt_kind);
+ conf2 (pdt_len);
+ }
+
+ a1 = gfc_code2string (flavors, attr->flavor);
+
+ if (attr->in_namelist
+ && attr->flavor != FL_VARIABLE
+ && attr->flavor != FL_PROCEDURE
+ && 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 (codimension);
+ conf2 (dimension);
+ conf2 (dummy);
+ conf2 (volatile_);
+ conf2 (asynchronous);
+ conf2 (contiguous);
+ conf2 (pointer);
+ conf2 (is_protected);
+ conf2 (target);
+ conf2 (external);
+ conf2 (intrinsic);
+ conf2 (allocatable);
+ conf2 (result);
+ conf2 (in_namelist);
+ conf2 (optional);
+ conf2 (function);
+ conf2 (subroutine);
+ conf2 (threadprivate);
+ conf2 (omp_declare_target);
+ conf2 (omp_declare_target_link);
+ conf2 (oacc_declare_create);
+ conf2 (oacc_declare_copyin);
+ conf2 (oacc_declare_deviceptr);
+ conf2 (oacc_declare_device_resident);
+
+ if (attr->access == ACCESS_PUBLIC || attr->access == ACCESS_PRIVATE)
+ {
+ a2 = attr->access == ACCESS_PUBLIC ? publik : privat;
+ gfc_error ("%s attribute applied to %s %s at %L", a2, a1,
+ name, where);
+ return false;
+ }
+
+ if (attr->is_bind_c)
+ {
+ gfc_error_now ("BIND(C) applied to %s %s at %L", a1, name, where);
+ return false;
+ }
+
+ break;
+
+ case FL_VARIABLE:
+ break;
+
+ case FL_NAMELIST:
+ conf2 (result);
+ break;
+
+ case FL_PROCEDURE:
+ /* Conflicts with INTENT, SAVE and RESULT will be checked
+ at resolution stage, see "resolve_fl_procedure". */
+
+ if (attr->subroutine)
+ {
+ a1 = subroutine;
+ conf2 (target);
+ conf2 (allocatable);
+ conf2 (volatile_);
+ conf2 (asynchronous);
+ conf2 (in_namelist);
+ conf2 (codimension);
+ conf2 (dimension);
+ conf2 (function);
+ if (!attr->proc_pointer)
+ conf2 (threadprivate);
+ }
+
+ /* Procedure pointers in COMMON blocks are allowed in F03,
+ * but forbidden per F08:C5100. */
+ if (!attr->proc_pointer || (gfc_option.allow_std & GFC_STD_F2008))
+ conf2 (in_common);
+
+ conf2 (omp_declare_target_link);
+
+ switch (attr->proc)
+ {
+ case PROC_ST_FUNCTION:
+ conf2 (dummy);
+ conf2 (target);
+ break;
+
+ case PROC_MODULE:
+ conf2 (dummy);
+ break;
+
+ case PROC_DUMMY:
+ conf2 (result);
+ conf2 (threadprivate);
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case_fl_struct:
+ conf2 (dummy);
+ conf2 (pointer);
+ conf2 (target);
+ conf2 (external);
+ conf2 (intrinsic);
+ conf2 (allocatable);
+ conf2 (optional);
+ conf2 (entry);
+ conf2 (function);
+ conf2 (subroutine);
+ conf2 (threadprivate);
+ conf2 (result);
+ conf2 (omp_declare_target);
+ conf2 (omp_declare_target_link);
+ conf2 (oacc_declare_create);
+ conf2 (oacc_declare_copyin);
+ conf2 (oacc_declare_deviceptr);
+ conf2 (oacc_declare_device_resident);
+
+ 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 (contiguous);
+ conf2 (pointer);
+ conf2 (is_protected);
+ conf2 (target);
+ conf2 (dummy);
+ conf2 (in_common);
+ conf2 (value);
+ conf2 (volatile_);
+ conf2 (asynchronous);
+ conf2 (threadprivate);
+ conf2 (value);
+ conf2 (codimension);
+ conf2 (result);
+ if (!attr->is_iso_c)
+ conf2 (is_bind_c);
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+
+conflict:
+ if (name == NULL)
+ gfc_error ("%s attribute conflicts with %s attribute at %L",
+ a1, a2, where);
+ else
+ gfc_error ("%s attribute conflicts with %s attribute in %qs at %L",
+ a1, a2, name, where);
+
+ return false;
+
+conflict_std:
+ if (name == NULL)
+ {
+ return gfc_notify_std (standard, "%s attribute conflicts "
+ "with %s attribute at %L", a1, a2,
+ where);
+ }
+ else
+ {
+ return gfc_notify_std (standard, "%s attribute conflicts "
+ "with %s attribute in %qs at %L",
+ a1, a2, name, where);
+ }
+}
+
+#undef conf
+#undef conf2
+#undef conf_std
+
+
+/* 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, const char *name, locus *where)
+{
+
+ if (attr->use_assoc == 0)
+ return 0;
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (name == NULL)
+ gfc_error ("Cannot change attributes of USE-associated symbol at %L",
+ where);
+ else
+ gfc_error ("Cannot change attributes of USE-associated symbol %s at %L",
+ name, 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);
+}
+
+
+bool
+gfc_add_ext_attribute (symbol_attribute *attr, ext_attr_id_t ext_attr,
+ locus *where ATTRIBUTE_UNUSED)
+{
+ attr->ext_attr |= 1 << ext_attr;
+ return true;
+}
+
+
+/* Called from decl.c (attr_decl1) to check attributes, when declared
+ separately. */
+
+bool
+gfc_add_attribute (symbol_attribute *attr, locus *where)
+{
+ if (check_used (attr, NULL, where))
+ return false;
+
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_allocatable (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->allocatable && ! gfc_submodule_procedure(attr))
+ {
+ duplicate_attr ("ALLOCATABLE", where);
+ return false;
+ }
+
+ if (attr->flavor == FL_PROCEDURE && attr->if_source == IFSRC_IFBODY
+ && !gfc_find_state (COMP_INTERFACE))
+ {
+ gfc_error ("ALLOCATABLE specified outside of INTERFACE body at %L",
+ where);
+ return false;
+ }
+
+ attr->allocatable = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_automatic (symbol_attribute *attr, const char *name, locus *where)
+{
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->automatic && !gfc_notify_std (GFC_STD_LEGACY,
+ "Duplicate AUTOMATIC attribute specified at %L", where))
+ return false;
+
+ attr->automatic = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_codimension (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->codimension)
+ {
+ duplicate_attr ("CODIMENSION", where);
+ return false;
+ }
+
+ if (attr->flavor == FL_PROCEDURE && attr->if_source == IFSRC_IFBODY
+ && !gfc_find_state (COMP_INTERFACE))
+ {
+ gfc_error ("CODIMENSION specified for %qs outside its INTERFACE body "
+ "at %L", name, where);
+ return false;
+ }
+
+ attr->codimension = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_dimension (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->dimension && ! gfc_submodule_procedure(attr))
+ {
+ duplicate_attr ("DIMENSION", where);
+ return false;
+ }
+
+ if (attr->flavor == FL_PROCEDURE && attr->if_source == IFSRC_IFBODY
+ && !gfc_find_state (COMP_INTERFACE))
+ {
+ gfc_error ("DIMENSION specified for %qs outside its INTERFACE body "
+ "at %L", name, where);
+ return false;
+ }
+
+ attr->dimension = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_contiguous (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ attr->contiguous = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_external (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->external)
+ {
+ duplicate_attr ("EXTERNAL", where);
+ return false;
+ }
+
+ if (attr->pointer && attr->if_source != IFSRC_IFBODY)
+ {
+ attr->pointer = 0;
+ attr->proc_pointer = 1;
+ }
+
+ attr->external = 1;
+
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_intrinsic (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->intrinsic)
+ {
+ duplicate_attr ("INTRINSIC", where);
+ return false;
+ }
+
+ attr->intrinsic = 1;
+
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_optional (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->optional)
+ {
+ duplicate_attr ("OPTIONAL", where);
+ return false;
+ }
+
+ attr->optional = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+bool
+gfc_add_kind (symbol_attribute *attr, locus *where)
+{
+ if (attr->pdt_kind)
+ {
+ duplicate_attr ("KIND", where);
+ return false;
+ }
+
+ attr->pdt_kind = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+bool
+gfc_add_len (symbol_attribute *attr, locus *where)
+{
+ if (attr->pdt_len)
+ {
+ duplicate_attr ("LEN", where);
+ return false;
+ }
+
+ attr->pdt_len = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_pointer (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->pointer && !(attr->if_source == IFSRC_IFBODY
+ && !gfc_find_state (COMP_INTERFACE))
+ && ! gfc_submodule_procedure(attr))
+ {
+ duplicate_attr ("POINTER", where);
+ return false;
+ }
+
+ if (attr->procedure || (attr->external && attr->if_source != IFSRC_IFBODY)
+ || (attr->if_source == IFSRC_IFBODY
+ && !gfc_find_state (COMP_INTERFACE)))
+ attr->proc_pointer = 1;
+ else
+ attr->pointer = 1;
+
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_cray_pointer (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ attr->cray_pointer = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_cray_pointee (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->cray_pointee)
+ {
+ gfc_error ("Cray Pointee at %L appears in multiple pointer()"
+ " statements", where);
+ return false;
+ }
+
+ attr->cray_pointee = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_protected (symbol_attribute *attr, const char *name, locus *where)
+{
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->is_protected)
+ {
+ if (!gfc_notify_std (GFC_STD_LEGACY,
+ "Duplicate PROTECTED attribute specified at %L",
+ where))
+ return false;
+ }
+
+ attr->is_protected = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_result (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ attr->result = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_save (symbol_attribute *attr, save_state s, const char *name,
+ locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (s == SAVE_EXPLICIT && gfc_pure (NULL))
+ {
+ gfc_error
+ ("SAVE attribute at %L cannot be specified in a PURE procedure",
+ where);
+ return false;
+ }
+
+ if (s == SAVE_EXPLICIT)
+ gfc_unset_implicit_pure (NULL);
+
+ if (s == SAVE_EXPLICIT && attr->save == SAVE_EXPLICIT
+ && (flag_automatic || pedantic))
+ {
+ if (!gfc_notify_std (GFC_STD_LEGACY,
+ "Duplicate SAVE attribute specified at %L",
+ where))
+ return false;
+ }
+
+ attr->save = s;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_value (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->value)
+ {
+ if (!gfc_notify_std (GFC_STD_LEGACY,
+ "Duplicate VALUE attribute specified at %L",
+ where))
+ return false;
+ }
+
+ attr->value = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_volatile (symbol_attribute *attr, const char *name, locus *where)
+{
+ /* No check_used needed as 11.2.1 of the F2003 standard allows
+ that the local identifier made accessible by a use statement can be
+ given a VOLATILE attribute - unless it is a coarray (F2008, C560). */
+
+ if (attr->volatile_ && attr->volatile_ns == gfc_current_ns)
+ if (!gfc_notify_std (GFC_STD_LEGACY,
+ "Duplicate VOLATILE attribute specified at %L",
+ where))
+ return false;
+
+ /* F2008: C1282 A designator of a variable with the VOLATILE attribute
+ shall not appear in a pure subprogram.
+
+ F2018: C1588 A local variable of a pure subprogram, or of a BLOCK
+ construct within a pure subprogram, shall not have the SAVE or
+ VOLATILE attribute. */
+ if (gfc_pure (NULL))
+ {
+ gfc_error ("VOLATILE attribute at %L cannot be specified in a "
+ "PURE procedure", where);
+ return false;
+ }
+
+
+ attr->volatile_ = 1;
+ attr->volatile_ns = gfc_current_ns;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_asynchronous (symbol_attribute *attr, const char *name, locus *where)
+{
+ /* No check_used needed as 11.2.1 of the F2003 standard allows
+ that the local identifier made accessible by a use statement can be
+ given a ASYNCHRONOUS attribute. */
+
+ if (attr->asynchronous && attr->asynchronous_ns == gfc_current_ns)
+ if (!gfc_notify_std (GFC_STD_LEGACY,
+ "Duplicate ASYNCHRONOUS attribute specified at %L",
+ where))
+ return false;
+
+ attr->asynchronous = 1;
+ attr->asynchronous_ns = gfc_current_ns;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_threadprivate (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->threadprivate)
+ {
+ duplicate_attr ("THREADPRIVATE", where);
+ return false;
+ }
+
+ attr->threadprivate = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_omp_declare_target (symbol_attribute *attr, const char *name,
+ locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->omp_declare_target)
+ return true;
+
+ attr->omp_declare_target = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_omp_declare_target_link (symbol_attribute *attr, const char *name,
+ locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->omp_declare_target_link)
+ return true;
+
+ attr->omp_declare_target_link = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_create (symbol_attribute *attr, const char *name,
+ locus *where)
+{
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->oacc_declare_create)
+ return true;
+
+ attr->oacc_declare_create = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_copyin (symbol_attribute *attr, const char *name,
+ locus *where)
+{
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->oacc_declare_copyin)
+ return true;
+
+ attr->oacc_declare_copyin = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_deviceptr (symbol_attribute *attr, const char *name,
+ locus *where)
+{
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->oacc_declare_deviceptr)
+ return true;
+
+ attr->oacc_declare_deviceptr = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_oacc_declare_device_resident (symbol_attribute *attr, const char *name,
+ locus *where)
+{
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->oacc_declare_device_resident)
+ return true;
+
+ attr->oacc_declare_device_resident = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_target (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->target)
+ {
+ duplicate_attr ("TARGET", where);
+ return false;
+ }
+
+ attr->target = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_dummy (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ /* Duplicate dummy arguments are allowed due to ENTRY statements. */
+ attr->dummy = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_in_common (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ /* Duplicate attribute already checked for. */
+ attr->in_common = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_in_equivalence (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ /* Duplicate attribute already checked for. */
+ attr->in_equivalence = 1;
+ if (!gfc_check_conflict (attr, name, where))
+ return false;
+
+ if (attr->flavor == FL_VARIABLE)
+ return true;
+
+ return gfc_add_flavor (attr, FL_VARIABLE, name, where);
+}
+
+
+bool
+gfc_add_data (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ attr->data = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_in_namelist (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ attr->in_namelist = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_sequence (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ attr->sequence = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_elemental (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->elemental)
+ {
+ duplicate_attr ("ELEMENTAL", where);
+ return false;
+ }
+
+ attr->elemental = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_pure (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->pure)
+ {
+ duplicate_attr ("PURE", where);
+ return false;
+ }
+
+ attr->pure = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_recursive (symbol_attribute *attr, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->recursive)
+ {
+ duplicate_attr ("RECURSIVE", where);
+ return false;
+ }
+
+ attr->recursive = 1;
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_entry (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->entry)
+ {
+ duplicate_attr ("ENTRY", where);
+ return false;
+ }
+
+ attr->entry = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_function (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (attr->flavor != FL_PROCEDURE
+ && !gfc_add_flavor (attr, FL_PROCEDURE, name, where))
+ return false;
+
+ attr->function = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_subroutine (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (attr->flavor != FL_PROCEDURE
+ && !gfc_add_flavor (attr, FL_PROCEDURE, name, where))
+ return false;
+
+ attr->subroutine = 1;
+
+ /* If we are looking at a BLOCK DATA statement and we encounter a
+ name with a leading underscore (which must be
+ compiler-generated), do not check. See PR 84394. */
+
+ if (name && *name != '_' && gfc_current_state () != COMP_BLOCK_DATA)
+ return gfc_check_conflict (attr, name, where);
+ else
+ return true;
+}
+
+
+bool
+gfc_add_generic (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (attr->flavor != FL_PROCEDURE
+ && !gfc_add_flavor (attr, FL_PROCEDURE, name, where))
+ return false;
+
+ attr->generic = 1;
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_proc (symbol_attribute *attr, const char *name, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->flavor != FL_PROCEDURE
+ && !gfc_add_flavor (attr, FL_PROCEDURE, name, where))
+ return false;
+
+ if (attr->procedure)
+ {
+ duplicate_attr ("PROCEDURE", where);
+ return false;
+ }
+
+ attr->procedure = 1;
+
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+bool
+gfc_add_abstract (symbol_attribute* attr, locus* where)
+{
+ if (attr->abstract)
+ {
+ duplicate_attr ("ABSTRACT", where);
+ return false;
+ }
+
+ attr->abstract = 1;
+
+ return gfc_check_conflict (attr, NULL, where);
+}
+
+
+/* Flavors are special because some flavors are not what Fortran
+ considers attributes and can be reaffirmed multiple times. */
+
+bool
+gfc_add_flavor (symbol_attribute *attr, sym_flavor f, const char *name,
+ locus *where)
+{
+
+ if ((f == FL_PROGRAM || f == FL_BLOCK_DATA || f == FL_MODULE
+ || f == FL_PARAMETER || f == FL_LABEL || gfc_fl_struct(f)
+ || f == FL_NAMELIST) && check_used (attr, name, where))
+ return false;
+
+ if (attr->flavor == f && f == FL_VARIABLE)
+ return true;
+
+ /* Copying a procedure dummy argument for a module procedure in a
+ submodule results in the flavor being copied and would result in
+ an error without this. */
+ if (attr->flavor == f && f == FL_PROCEDURE
+ && gfc_new_block && gfc_new_block->abr_modproc_decl)
+ return true;
+
+ if (attr->flavor != FL_UNKNOWN)
+ {
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (name)
+ gfc_error ("%s attribute of %qs conflicts with %s attribute at %L",
+ gfc_code2string (flavors, attr->flavor), name,
+ gfc_code2string (flavors, f), where);
+ else
+ gfc_error ("%s attribute conflicts with %s attribute at %L",
+ gfc_code2string (flavors, attr->flavor),
+ gfc_code2string (flavors, f), where);
+
+ return false;
+ }
+
+ attr->flavor = f;
+
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_procedure (symbol_attribute *attr, procedure_type t,
+ const char *name, locus *where)
+{
+
+ if (check_used (attr, name, where))
+ return false;
+
+ if (attr->flavor != FL_PROCEDURE
+ && !gfc_add_flavor (attr, FL_PROCEDURE, name, where))
+ return false;
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (attr->proc != PROC_UNKNOWN && !attr->module_procedure
+ && attr->access == ACCESS_UNKNOWN)
+ {
+ if (attr->proc == PROC_ST_FUNCTION && t == PROC_INTERNAL
+ && !gfc_notification_std (GFC_STD_F2008))
+ gfc_error ("%s procedure at %L is already declared as %s "
+ "procedure. \nF2008: A pointer function assignment "
+ "is ambiguous if it is the first executable statement "
+ "after the specification block. Please add any other "
+ "kind of executable statement before it. FIXME",
+ gfc_code2string (procedures, t), where,
+ gfc_code2string (procedures, attr->proc));
+ else
+ gfc_error ("%s procedure at %L is already declared as %s "
+ "procedure", gfc_code2string (procedures, t), where,
+ gfc_code2string (procedures, attr->proc));
+
+ return false;
+ }
+
+ attr->proc = t;
+
+ /* Statement functions are always scalar and functions. */
+ if (t == PROC_ST_FUNCTION
+ && ((!attr->function && !gfc_add_function (attr, name, where))
+ || attr->dimension))
+ return false;
+
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+bool
+gfc_add_intent (symbol_attribute *attr, sym_intent intent, locus *where)
+{
+
+ if (check_used (attr, NULL, where))
+ return false;
+
+ if (attr->intent == INTENT_UNKNOWN)
+ {
+ attr->intent = intent;
+ return gfc_check_conflict (attr, NULL, 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 false;
+}
+
+
+/* No checks for use-association in public and private statements. */
+
+bool
+gfc_add_access (symbol_attribute *attr, gfc_access access,
+ const char *name, locus *where)
+{
+
+ if (attr->access == ACCESS_UNKNOWN
+ || (attr->use_assoc && attr->access != ACCESS_PRIVATE))
+ {
+ attr->access = access;
+ return gfc_check_conflict (attr, name, where);
+ }
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+ gfc_error ("ACCESS specification at %L was already specified", where);
+
+ return false;
+}
+
+
+/* Set the is_bind_c field for the given symbol_attribute. */
+
+bool
+gfc_add_is_bind_c (symbol_attribute *attr, const char *name, locus *where,
+ int is_proc_lang_bind_spec)
+{
+
+ if (is_proc_lang_bind_spec == 0 && attr->flavor == FL_PROCEDURE)
+ gfc_error_now ("BIND(C) attribute at %L can only be used for "
+ "variables or common blocks", where);
+ else if (attr->is_bind_c)
+ gfc_error_now ("Duplicate BIND attribute specified at %L", where);
+ else
+ attr->is_bind_c = 1;
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (!gfc_notify_std (GFC_STD_F2003, "BIND(C) at %L", where))
+ return false;
+
+ return gfc_check_conflict (attr, name, where);
+}
+
+
+/* Set the extension field for the given symbol_attribute. */
+
+bool
+gfc_add_extension (symbol_attribute *attr, locus *where)
+{
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (attr->extension)
+ gfc_error_now ("Duplicate EXTENDS attribute specified at %L", where);
+ else
+ attr->extension = 1;
+
+ if (!gfc_notify_std (GFC_STD_F2003, "EXTENDS at %L", where))
+ return false;
+
+ return true;
+}
+
+
+bool
+gfc_add_explicit_interface (gfc_symbol *sym, ifsrc source,
+ gfc_formal_arglist * formal, locus *where)
+{
+ if (check_used (&sym->attr, sym->name, where))
+ return false;
+
+ /* Skip the following checks in the case of a module_procedures in a
+ submodule since they will manifestly fail. */
+ if (sym->attr.module_procedure == 1
+ && source == IFSRC_DECL)
+ goto finish;
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (sym->attr.if_source != IFSRC_UNKNOWN
+ && sym->attr.if_source != IFSRC_DECL)
+ {
+ gfc_error ("Symbol %qs at %L already has an explicit interface",
+ sym->name, where);
+ return false;
+ }
+
+ if (source == IFSRC_IFBODY && (sym->attr.dimension || sym->attr.allocatable))
+ {
+ gfc_error ("%qs at %L has attributes specified outside its INTERFACE "
+ "body", sym->name, where);
+ return false;
+ }
+
+finish:
+ sym->formal = formal;
+ sym->attr.if_source = source;
+
+ return true;
+}
+
+
+/* Add a type to a symbol. */
+
+bool
+gfc_add_type (gfc_symbol *sym, gfc_typespec *ts, locus *where)
+{
+ sym_flavor flavor;
+ bt type;
+
+ if (where == NULL)
+ where = &gfc_current_locus;
+
+ if (sym->result)
+ type = sym->result->ts.type;
+ else
+ type = sym->ts.type;
+
+ if (sym->attr.result && type == BT_UNKNOWN && sym->ns->proc_name)
+ type = sym->ns->proc_name->ts.type;
+
+ if (type != BT_UNKNOWN && !(sym->attr.function && sym->attr.implicit_type)
+ && !(gfc_state_stack->previous && gfc_state_stack->previous->previous
+ && gfc_state_stack->previous->previous->state == COMP_SUBMODULE)
+ && !sym->attr.module_procedure)
+ {
+ if (sym->attr.use_assoc)
+ gfc_error ("Symbol %qs at %L conflicts with symbol from module %qs, "
+ "use-associated at %L", sym->name, where, sym->module,
+ &sym->declared_at);
+ else if (sym->attr.function && sym->attr.result)
+ gfc_error ("Symbol %qs at %L already has basic type of %s",
+ sym->ns->proc_name->name, where, gfc_basic_typename (type));
+ else
+ gfc_error ("Symbol %qs at %L already has basic type of %s", sym->name,
+ where, gfc_basic_typename (type));
+ return false;
+ }
+
+ if (sym->attr.procedure && sym->ts.interface)
+ {
+ gfc_error ("Procedure %qs at %L may not have basic type of %s",
+ sym->name, where, gfc_basic_typename (ts->type));
+ return false;
+ }
+
+ 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 %qs at %L cannot have a type",
+ sym->ns->proc_name ? sym->ns->proc_name->name : sym->name,
+ where);
+ return false;
+ }
+
+ sym->ts = *ts;
+ return true;
+}
+
+
+/* Clears all attributes. */
+
+void
+gfc_clear_attr (symbol_attribute *attr)
+{
+ memset (attr, 0, sizeof (symbol_attribute));
+}
+
+
+/* Check for missing attributes in the new symbol. Currently does
+ nothing, but it's not clear that it is unnecessary yet. */
+
+bool
+gfc_missing_attr (symbol_attribute *attr ATTRIBUTE_UNUSED,
+ locus *where ATTRIBUTE_UNUSED)
+{
+
+ return true;
+}
+
+
+/* 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. */
+
+bool
+gfc_copy_attr (symbol_attribute *dest, symbol_attribute *src, locus *where)
+{
+ int is_proc_lang_bind_spec;
+
+ /* In line with the other attributes, we only add bits but do not remove
+ them; cf. also PR 41034. */
+ dest->ext_attr |= src->ext_attr;
+
+ if (src->allocatable && !gfc_add_allocatable (dest, where))
+ goto fail;
+
+ if (src->automatic && !gfc_add_automatic (dest, NULL, where))
+ goto fail;
+ if (src->dimension && !gfc_add_dimension (dest, NULL, where))
+ goto fail;
+ if (src->codimension && !gfc_add_codimension (dest, NULL, where))
+ goto fail;
+ if (src->contiguous && !gfc_add_contiguous (dest, NULL, where))
+ goto fail;
+ if (src->optional && !gfc_add_optional (dest, where))
+ goto fail;
+ if (src->pointer && !gfc_add_pointer (dest, where))
+ goto fail;
+ if (src->is_protected && !gfc_add_protected (dest, NULL, where))
+ goto fail;
+ if (src->save && !gfc_add_save (dest, src->save, NULL, where))
+ goto fail;
+ if (src->value && !gfc_add_value (dest, NULL, where))
+ goto fail;
+ if (src->volatile_ && !gfc_add_volatile (dest, NULL, where))
+ goto fail;
+ if (src->asynchronous && !gfc_add_asynchronous (dest, NULL, where))
+ goto fail;
+ if (src->threadprivate
+ && !gfc_add_threadprivate (dest, NULL, where))
+ goto fail;
+ if (src->omp_declare_target
+ && !gfc_add_omp_declare_target (dest, NULL, where))
+ goto fail;
+ if (src->omp_declare_target_link
+ && !gfc_add_omp_declare_target_link (dest, NULL, where))
+ goto fail;
+ if (src->oacc_declare_create
+ && !gfc_add_oacc_declare_create (dest, NULL, where))
+ goto fail;
+ if (src->oacc_declare_copyin
+ && !gfc_add_oacc_declare_copyin (dest, NULL, where))
+ goto fail;
+ if (src->oacc_declare_deviceptr
+ && !gfc_add_oacc_declare_deviceptr (dest, NULL, where))
+ goto fail;
+ if (src->oacc_declare_device_resident
+ && !gfc_add_oacc_declare_device_resident (dest, NULL, where))
+ goto fail;
+ if (src->target && !gfc_add_target (dest, where))
+ goto fail;
+ if (src->dummy && !gfc_add_dummy (dest, NULL, where))
+ goto fail;
+ if (src->result && !gfc_add_result (dest, NULL, where))
+ goto fail;
+ if (src->entry)
+ dest->entry = 1;
+
+ if (src->in_namelist && !gfc_add_in_namelist (dest, NULL, where))
+ goto fail;
+
+ if (src->in_common && !gfc_add_in_common (dest, NULL, where))
+ goto fail;
+
+ if (src->generic && !gfc_add_generic (dest, NULL, where))
+ goto fail;
+ if (src->function && !gfc_add_function (dest, NULL, where))
+ goto fail;
+ if (src->subroutine && !gfc_add_subroutine (dest, NULL, where))
+ goto fail;
+
+ if (src->sequence && !gfc_add_sequence (dest, NULL, where))
+ goto fail;
+ if (src->elemental && !gfc_add_elemental (dest, where))
+ goto fail;
+ if (src->pure && !gfc_add_pure (dest, where))
+ goto fail;
+ if (src->recursive && !gfc_add_recursive (dest, where))
+ goto fail;
+
+ if (src->flavor != FL_UNKNOWN
+ && !gfc_add_flavor (dest, src->flavor, NULL, where))
+ goto fail;
+
+ if (src->intent != INTENT_UNKNOWN
+ && !gfc_add_intent (dest, src->intent, where))
+ goto fail;
+
+ if (src->access != ACCESS_UNKNOWN
+ && !gfc_add_access (dest, src->access, NULL, where))
+ goto fail;
+
+ if (!gfc_missing_attr (dest, where))
+ goto fail;
+
+ if (src->cray_pointer && !gfc_add_cray_pointer (dest, where))
+ goto fail;
+ if (src->cray_pointee && !gfc_add_cray_pointee (dest, where))
+ goto fail;
+
+ is_proc_lang_bind_spec = (src->flavor == FL_PROCEDURE ? 1 : 0);
+ if (src->is_bind_c
+ && !gfc_add_is_bind_c (dest, NULL, where, is_proc_lang_bind_spec))
+ return false;
+
+ if (src->is_c_interop)
+ dest->is_c_interop = 1;
+ if (src->is_iso_c)
+ dest->is_iso_c = 1;
+
+ if (src->external && !gfc_add_external (dest, where))
+ goto fail;
+ if (src->intrinsic && !gfc_add_intrinsic (dest, where))
+ goto fail;
+ if (src->proc_pointer)
+ dest->proc_pointer = 1;
+
+ return true;
+
+fail:
+ return false;
+}
+
+
+/* A function to generate a dummy argument symbol using that from the
+ interface declaration. Can be used for the result symbol as well if
+ the flag is set. */
+
+int
+gfc_copy_dummy_sym (gfc_symbol **dsym, gfc_symbol *sym, int result)
+{
+ int rc;
+
+ rc = gfc_get_symbol (sym->name, NULL, dsym);
+ if (rc)
+ return rc;
+
+ if (!gfc_add_type (*dsym, &(sym->ts), &gfc_current_locus))
+ return 1;
+
+ if (!gfc_copy_attr (&(*dsym)->attr, &(sym->attr),
+ &gfc_current_locus))
+ return 1;
+
+ if ((*dsym)->attr.dimension)
+ (*dsym)->as = gfc_copy_array_spec (sym->as);
+
+ (*dsym)->attr.class_ok = sym->attr.class_ok;
+
+ if ((*dsym) != NULL && !result
+ && (!gfc_add_dummy(&(*dsym)->attr, (*dsym)->name, NULL)
+ || !gfc_missing_attr (&(*dsym)->attr, NULL)))
+ return 1;
+ else if ((*dsym) != NULL && result
+ && (!gfc_add_result(&(*dsym)->attr, (*dsym)->name, NULL)
+ || !gfc_missing_attr (&(*dsym)->attr, NULL)))
+ return 1;
+
+ return 0;
+}
+
+
+/************** 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. */
+
+bool
+gfc_add_component (gfc_symbol *sym, const char *name,
+ gfc_component **component)
+{
+ gfc_component *p, *tail;
+
+ /* Check for existing components with the same name, but not for union
+ components or containers. Unions and maps are anonymous so they have
+ unique internal names which will never conflict.
+ Don't use gfc_find_component here because it calls gfc_use_derived,
+ but the derived type may not be fully defined yet. */
+ tail = NULL;
+
+ for (p = sym->components; p; p = p->next)
+ {
+ if (strcmp (p->name, name) == 0)
+ {
+ gfc_error ("Component %qs at %C already declared at %L",
+ name, &p->loc);
+ return false;
+ }
+
+ tail = p;
+ }
+
+ if (sym->attr.extension
+ && gfc_find_component (sym->components->ts.u.derived,
+ name, true, true, NULL))
+ {
+ gfc_error ("Component %qs at %C already in the parent type "
+ "at %L", name, &sym->components->ts.u.derived->declared_at);
+ return false;
+ }
+
+ /* Allocate a new component. */
+ p = gfc_get_component ();
+
+ if (tail == NULL)
+ sym->components = p;
+ else
+ tail->next = p;
+
+ p->name = gfc_get_string ("%s", name);
+ p->loc = gfc_current_locus;
+ p->ts.type = BT_UNKNOWN;
+
+ *component = p;
+ return true;
+}
+
+
+/* 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.u.derived == from)
+ sym->ts.u.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 we 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. */
+
+gfc_symbol *
+gfc_use_derived (gfc_symbol *sym)
+{
+ gfc_symbol *s;
+ gfc_typespec *t;
+ gfc_symtree *st;
+ int i;
+
+ if (!sym)
+ return NULL;
+
+ if (sym->attr.unlimited_polymorphic)
+ return sym;
+
+ if (sym->attr.generic)
+ sym = gfc_find_dt_in_generic (sym);
+
+ if (sym->components != NULL || sym->attr.zero_comp)
+ return sym; /* Already defined. */
+
+ if (sym->ns->parent == NULL)
+ goto bad;
+
+ if (gfc_find_symbol (sym->name, sym->ns->parent, 1, &s))
+ {
+ gfc_error ("Symbol %qs at %C is ambiguous", sym->name);
+ return NULL;
+ }
+
+ if (s == NULL || !gfc_fl_struct (s->attr.flavor))
+ 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->u.derived == sym)
+ t->u.derived = s;
+ }
+
+ st = gfc_find_symtree (sym->ns->sym_root, sym->name);
+ st->n.sym = s;
+
+ s->refs++;
+
+ /* Unlink from list of modified symbols. */
+ gfc_commit_symbol (sym);
+
+ 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 s;
+
+bad:
+ gfc_error ("Derived type %qs at %C is being used before it is defined",
+ sym->name);
+ return NULL;
+}
+
+
+/* Find the component with the given name in the union type symbol.
+ If ref is not NULL it will be set to the chain of components through which
+ the component can actually be accessed. This is necessary for unions because
+ intermediate structures may be maps, nested structures, or other unions,
+ all of which may (or must) be 'anonymous' to user code. */
+
+static gfc_component *
+find_union_component (gfc_symbol *un, const char *name,
+ bool noaccess, gfc_ref **ref)
+{
+ gfc_component *m, *check;
+ gfc_ref *sref, *tmp;
+
+ for (m = un->components; m; m = m->next)
+ {
+ check = gfc_find_component (m->ts.u.derived, name, noaccess, true, &tmp);
+ if (check == NULL)
+ continue;
+
+ /* Found component somewhere in m; chain the refs together. */
+ if (ref)
+ {
+ /* Map ref. */
+ sref = gfc_get_ref ();
+ sref->type = REF_COMPONENT;
+ sref->u.c.component = m;
+ sref->u.c.sym = m->ts.u.derived;
+ sref->next = tmp;
+
+ *ref = sref;
+ }
+ /* Other checks (such as access) were done in the recursive calls. */
+ return check;
+ }
+ return NULL;
+}
+
+
+/* Recursively append candidate COMPONENT structures to CANDIDATES. Store
+ the number of total candidates in CANDIDATES_LEN. */
+
+static void
+lookup_component_fuzzy_find_candidates (gfc_component *component,
+ char **&candidates,
+ size_t &candidates_len)
+{
+ for (gfc_component *p = component; p; p = p->next)
+ vec_push (candidates, candidates_len, p->name);
+}
+
+
+/* Lookup component MEMBER fuzzily, taking names in COMPONENT into account. */
+
+static const char*
+lookup_component_fuzzy (const char *member, gfc_component *component)
+{
+ char **candidates = NULL;
+ size_t candidates_len = 0;
+ lookup_component_fuzzy_find_candidates (component, candidates,
+ candidates_len);
+ return gfc_closest_fuzzy_match (member, candidates);
+}
+
+
+/* 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. If noaccess is set, no access
+ checks are done. If silent is set, an error will not be generated if
+ the component cannot be found or accessed.
+
+ If ref is not NULL, *ref is set to represent the chain of components
+ required to get to the ultimate component.
+
+ If the component is simply a direct subcomponent, or is inherited from a
+ parent derived type in the given derived type, this is a single ref with its
+ component set to the returned component.
+
+ Otherwise, *ref is constructed as a chain of subcomponents. This occurs
+ when the component is found through an implicit chain of nested union and
+ map components. Unions and maps are "anonymous" substructures in FORTRAN
+ which cannot be explicitly referenced, but the reference chain must be
+ considered as in C for backend translation to correctly compute layouts.
+ (For example, x.a may refer to x->(UNION)->(MAP)->(UNION)->(MAP)->a). */
+
+gfc_component *
+gfc_find_component (gfc_symbol *sym, const char *name,
+ bool noaccess, bool silent, gfc_ref **ref)
+{
+ gfc_component *p, *check;
+ gfc_ref *sref = NULL, *tmp = NULL;
+
+ if (name == NULL || sym == NULL)
+ return NULL;
+
+ if (sym->attr.flavor == FL_DERIVED)
+ sym = gfc_use_derived (sym);
+ else
+ gcc_assert (gfc_fl_struct (sym->attr.flavor));
+
+ if (sym == NULL)
+ return NULL;
+
+ /* Handle UNIONs specially - mutually recursive with gfc_find_component. */
+ if (sym->attr.flavor == FL_UNION)
+ return find_union_component (sym, name, noaccess, ref);
+
+ if (ref) *ref = NULL;
+ for (p = sym->components; p; p = p->next)
+ {
+ /* Nest search into union's maps. */
+ if (p->ts.type == BT_UNION)
+ {
+ check = find_union_component (p->ts.u.derived, name, noaccess, &tmp);
+ if (check != NULL)
+ {
+ /* Union ref. */
+ if (ref)
+ {
+ sref = gfc_get_ref ();
+ sref->type = REF_COMPONENT;
+ sref->u.c.component = p;
+ sref->u.c.sym = p->ts.u.derived;
+ sref->next = tmp;
+ *ref = sref;
+ }
+ return check;
+ }
+ }
+ else if (strcmp (p->name, name) == 0)
+ break;
+
+ continue;
+ }
+
+ if (p && sym->attr.use_assoc && !noaccess)
+ {
+ bool is_parent_comp = sym->attr.extension && (p == sym->components);
+ if (p->attr.access == ACCESS_PRIVATE ||
+ (p->attr.access != ACCESS_PUBLIC
+ && sym->component_access == ACCESS_PRIVATE
+ && !is_parent_comp))
+ {
+ if (!silent)
+ gfc_error ("Component %qs at %C is a PRIVATE component of %qs",
+ name, sym->name);
+ return NULL;
+ }
+ }
+
+ if (p == NULL
+ && sym->attr.extension
+ && sym->components->ts.type == BT_DERIVED)
+ {
+ p = gfc_find_component (sym->components->ts.u.derived, name,
+ noaccess, silent, ref);
+ /* Do not overwrite the error. */
+ if (p == NULL)
+ return p;
+ }
+
+ if (p == NULL && !silent)
+ {
+ const char *guessed = lookup_component_fuzzy (name, sym->components);
+ if (guessed)
+ gfc_error ("%qs at %C is not a member of the %qs structure"
+ "; did you mean %qs?",
+ name, sym->name, guessed);
+ else
+ gfc_error ("%qs at %C is not a member of the %qs structure",
+ name, sym->name);
+ }
+
+ /* Component was found; build the ultimate component reference. */
+ if (p != NULL && ref)
+ {
+ tmp = gfc_get_ref ();
+ tmp->type = REF_COMPONENT;
+ tmp->u.c.component = p;
+ tmp->u.c.sym = sym;
+ /* Link the final component ref to the end of the chain of subrefs. */
+ if (sref)
+ {
+ *ref = sref;
+ for (; sref->next; sref = sref->next)
+ ;
+ sref->next = tmp;
+ }
+ else
+ *ref = tmp;
+ }
+
+ 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);
+ if (p->kind_expr)
+ gfc_free_expr (p->kind_expr);
+ if (p->param_list)
+ gfc_free_actual_arglist (p->param_list);
+ free (p->tb);
+ p->tb = NULL;
+ free (p);
+ }
+}
+
+
+/******************** Statement label management ********************/
+
+/* Comparison function for statement labels, used for managing the
+ binary tree. */
+
+static int
+compare_st_labels (void *a1, void *b1)
+{
+ int a = ((gfc_st_label *) a1)->value;
+ int b = ((gfc_st_label *) b1)->value;
+
+ return (b - a);
+}
+
+
+/* Free a single gfc_st_label structure, making sure the tree is not
+ messed up. This function is called only when some parse error
+ occurs. */
+
+void
+gfc_free_st_label (gfc_st_label *label)
+{
+
+ if (label == NULL)
+ return;
+
+ gfc_delete_bbt (&label->ns->st_labels, label, compare_st_labels);
+
+ if (label->format != NULL)
+ gfc_free_expr (label->format);
+
+ free (label);
+}
+
+
+/* Free a whole tree of gfc_st_label structures. */
+
+static void
+free_st_labels (gfc_st_label *label)
+{
+
+ if (label == NULL)
+ return;
+
+ free_st_labels (label->left);
+ free_st_labels (label->right);
+
+ if (label->format != NULL)
+ gfc_free_expr (label->format);
+ free (label);
+}
+
+
+/* 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;
+ gfc_namespace *ns;
+
+ if (gfc_current_state () == COMP_DERIVED)
+ ns = gfc_current_block ()->f2k_derived;
+ else
+ {
+ /* Find the namespace of the scoping unit:
+ If we're in a BLOCK construct, jump to the parent namespace. */
+ ns = gfc_current_ns;
+ while (ns->proc_name && ns->proc_name->attr.flavor == FL_LABEL)
+ ns = ns->parent;
+ }
+
+ /* First see if the label is already in this namespace. */
+ lp = ns->st_labels;
+ while (lp)
+ {
+ if (lp->value == labelno)
+ return lp;
+
+ if (lp->value < labelno)
+ lp = lp->left;
+ else
+ lp = lp->right;
+ }
+
+ lp = XCNEW (gfc_st_label);
+
+ lp->value = labelno;
+ lp->defined = ST_LABEL_UNKNOWN;
+ lp->referenced = ST_LABEL_UNKNOWN;
+ lp->ns = ns;
+
+ gfc_insert_bbt (&ns->st_labels, lp, compare_st_labels);
+
+ 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
+ || lp->referenced == ST_LABEL_DO_TARGET)
+ gfc_error ("Label %d at %C already referenced as branch target",
+ labelno);
+ else
+ lp->defined = ST_LABEL_FORMAT;
+
+ break;
+
+ case ST_LABEL_TARGET:
+ case ST_LABEL_DO_TARGET:
+ if (lp->referenced == ST_LABEL_FORMAT)
+ gfc_error ("Label %d at %C already referenced as a format label",
+ labelno);
+ else
+ lp->defined = type;
+
+ if (lp->referenced == ST_LABEL_DO_TARGET && type != ST_LABEL_DO_TARGET
+ && !gfc_notify_std (GFC_STD_F95_OBS | GFC_STD_F2018_DEL,
+ "DO termination statement which is not END DO"
+ " or CONTINUE with label %d at %C", labelno))
+ return;
+ 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 false if something goes
+ wrong. */
+
+bool
+gfc_reference_st_label (gfc_st_label *lp, gfc_sl_type type)
+{
+ gfc_sl_type label_type;
+ int labelno;
+ bool rc;
+
+ if (lp == NULL)
+ return true;
+
+ 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 || type == ST_LABEL_DO_TARGET))
+ {
+ gfc_error ("Label %d at %C previously used as a FORMAT label", labelno);
+ rc = false;
+ goto done;
+ }
+
+ if ((label_type == ST_LABEL_TARGET || label_type == ST_LABEL_DO_TARGET
+ || label_type == ST_LABEL_BAD_TARGET)
+ && type == ST_LABEL_FORMAT)
+ {
+ gfc_error ("Label %d at %C previously used as branch target", labelno);
+ rc = false;
+ goto done;
+ }
+
+ if (lp->referenced == ST_LABEL_DO_TARGET && type == ST_LABEL_DO_TARGET
+ && !gfc_notify_std (GFC_STD_F95_OBS | GFC_STD_F2018_DEL,
+ "Shared DO termination label %d at %C", labelno))
+ return false;
+
+ if (type == ST_LABEL_DO_TARGET
+ && !gfc_notify_std (GFC_STD_F2018_OBS, "Labeled DO statement "
+ "at %L", &gfc_current_locus))
+ return false;
+
+ if (lp->referenced != ST_LABEL_DO_TARGET)
+ lp->referenced = type;
+ rc = true;
+
+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 vector so that we can commit or
+ undo the changes at a later time.
+
+ A symtree may point to a symbol node outside of its namespace. In
+ this case, that symbol has been used as a host associated variable
+ at some previous time. */
+
+/* Allocate a new namespace structure. Copies the implicit types from
+ PARENT if PARENT_TYPES is set. */
+
+gfc_namespace *
+gfc_get_namespace (gfc_namespace *parent, int parent_types)
+{
+ gfc_namespace *ns;
+ gfc_typespec *ts;
+ int in;
+ int i;
+
+ ns = XCNEW (gfc_namespace);
+ ns->sym_root = NULL;
+ ns->uop_root = NULL;
+ ns->tb_sym_root = NULL;
+ ns->finalizers = 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;
+ ns->tb_op[in] = NULL;
+ }
+
+ /* Initialize default implicit types. */
+ for (i = 'a'; i <= 'z'; i++)
+ {
+ ns->set_flag[i - 'a'] = 0;
+ ts = &ns->default_type[i - 'a'];
+
+ if (parent_types && ns->parent != NULL)
+ {
+ /* Copy parent settings. */
+ *ts = ns->parent->default_type[i - 'a'];
+ continue;
+ }
+
+ if (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;
+ }
+ }
+
+ ns->refs = 1;
+
+ 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 = XCNEW (gfc_symtree);
+ st->name = gfc_get_string ("%s", name);
+
+ gfc_insert_bbt (root, st, compare_symtree);
+ return st;
+}
+
+
+/* Delete a symbol from the tree. Does not free the symbol itself! */
+
+void
+gfc_delete_symtree (gfc_symtree **root, const char *name)
+{
+ gfc_symtree st, *st0;
+ const char *p;
+
+ /* Submodules are marked as mod.submod. When freeing a submodule
+ symbol, the symtree only has "submod", so adjust that here. */
+
+ p = strrchr(name, '.');
+ if (p)
+ p++;
+ else
+ p = name;
+
+ st0 = gfc_find_symtree (*root, p);
+
+ st.name = gfc_get_string ("%s", p);
+ gfc_delete_bbt (root, &st, compare_symtree);
+
+ 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;
+}
+
+
+/* Return a symtree node with a name that is guaranteed to be unique
+ within the namespace and corresponds to an illegal fortran name. */
+
+gfc_symtree *
+gfc_get_unique_symtree (gfc_namespace *ns)
+{
+ char name[GFC_MAX_SYMBOL_LEN + 1];
+ static int serial = 0;
+
+ sprintf (name, "@%d", serial++);
+ return gfc_new_symtree (&ns->sym_root, name);
+}
+
+
+/* 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;
+ gfc_namespace *ns = gfc_current_ns;
+
+ if (ns->omp_udr_ns)
+ ns = ns->parent;
+ st = gfc_find_symtree (ns->uop_root, name);
+ if (st != NULL)
+ return st->n.uop;
+
+ st = gfc_new_symtree (&ns->uop_root, name);
+
+ uop = st->n.uop = XCNEW (gfc_user_op);
+ uop->name = gfc_get_string ("%s", name);
+ uop->access = ACCESS_UNKNOWN;
+ uop->ns = 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;
+}
+
+
+/* Update a symbol's common_block field, and take care of the associated
+ memory management. */
+
+static void
+set_symbol_common_block (gfc_symbol *sym, gfc_common_head *common_block)
+{
+ if (sym->common_block == common_block)
+ return;
+
+ if (sym->common_block && sym->common_block->name[0] != '\0')
+ {
+ sym->common_block->refs--;
+ if (sym->common_block->refs == 0)
+ free (sym->common_block);
+ }
+ sym->common_block = common_block;
+}
+
+
+/* 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);
+
+ if (sym->ns != sym->formal_ns)
+ gfc_free_namespace (sym->formal_ns);
+
+ if (!sym->attr.generic_copy)
+ gfc_free_interface (sym->generic);
+
+ gfc_free_formal_arglist (sym->formal);
+
+ gfc_free_namespace (sym->f2k_derived);
+
+ set_symbol_common_block (sym, NULL);
+
+ if (sym->param_list)
+ gfc_free_actual_arglist (sym->param_list);
+
+ free (sym);
+ sym = NULL;
+}
+
+
+/* Decrease the reference counter and free memory when we reach zero. */
+
+void
+gfc_release_symbol (gfc_symbol *&sym)
+{
+ if (sym == NULL)
+ return;
+
+ if (sym->formal_ns != NULL && sym->refs == 2 && sym->formal_ns != sym->ns
+ && (!sym->attr.entry || !sym->module))
+ {
+ /* As formal_ns contains a reference to sym, delete formal_ns just
+ before the deletion of sym. */
+ gfc_namespace *ns = sym->formal_ns;
+ sym->formal_ns = NULL;
+ gfc_free_namespace (ns);
+ }
+
+ sym->refs--;
+ if (sym->refs > 0)
+ return;
+
+ gcc_assert (sym->refs == 0);
+ gfc_free_symbol (sym);
+}
+
+
+/* Allocate and initialize a new symbol node. */
+
+gfc_symbol *
+gfc_new_symbol (const char *name, gfc_namespace *ns)
+{
+ gfc_symbol *p;
+
+ p = XCNEW (gfc_symbol);
+
+ gfc_clear_ts (&p->ts);
+ gfc_clear_attr (&p->attr);
+ p->ns = ns;
+ p->declared_at = gfc_current_locus;
+ p->name = gfc_get_string ("%s", name);
+
+ return p;
+}
+
+
+/* Generate an error if a symbol is ambiguous, and set the error flag
+ on it. */
+
+static void
+ambiguous_symbol (const char *name, gfc_symtree *st)
+{
+
+ if (st->n.sym->error)
+ return;
+
+ if (st->n.sym->module)
+ gfc_error ("Name %qs at %C is an ambiguous reference to %qs "
+ "from module %qs", name, st->n.sym->name, st->n.sym->module);
+ else
+ gfc_error ("Name %qs at %C is an ambiguous reference to %qs "
+ "from current program unit", name, st->n.sym->name);
+
+ st->n.sym->error = 1;
+}
+
+
+/* If we're in a SELECT TYPE block, check if the variable 'st' matches any
+ selector on the stack. If yes, replace it by the corresponding temporary. */
+
+static void
+select_type_insert_tmp (gfc_symtree **st)
+{
+ gfc_select_type_stack *stack = select_type_stack;
+ for (; stack; stack = stack->prev)
+ if ((*st)->n.sym == stack->selector && stack->tmp)
+ {
+ *st = stack->tmp;
+ select_type_insert_tmp (st);
+ return;
+ }
+}
+
+
+/* Look for a symtree in the current procedure -- that is, go up to
+ parent namespaces but only if inside a BLOCK. Returns NULL if not found. */
+
+gfc_symtree*
+gfc_find_symtree_in_proc (const char* name, gfc_namespace* ns)
+{
+ while (ns)
+ {
+ gfc_symtree* st = gfc_find_symtree (ns->sym_root, name);
+ if (st)
+ return st;
+
+ if (!ns->construct_entities)
+ break;
+ ns = ns->parent;
+ }
+
+ return NULL;
+}
+
+
+/* Search for a symtree starting in the current namespace, resorting to
+ any parent namespaces if requested by a nonzero parent_flag.
+ Returns nonzero if the name 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)
+ {
+ select_type_insert_tmp (&st);
+
+ *result = st;
+ /* Ambiguous generic interfaces are permitted, as long
+ as the specific interfaces are different. */
+ if (st->ambiguous && !st->n.sym->attr.generic)
+ {
+ ambiguous_symbol (name, st);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (!parent_flag)
+ break;
+
+ /* Don't escape an interface block. */
+ if (ns && !ns->has_import_set
+ && ns->proc_name && ns->proc_name->attr.if_source == IFSRC_IFBODY)
+ break;
+
+ ns = ns->parent;
+ }
+ while (ns != NULL);
+
+ if (gfc_current_state() == COMP_DERIVED
+ && gfc_current_block ()->attr.pdt_template)
+ {
+ gfc_symbol *der = gfc_current_block ();
+ for (; der; der = gfc_get_derived_super_type (der))
+ {
+ if (der->f2k_derived && der->f2k_derived->sym_root)
+ {
+ st = gfc_find_symtree (der->f2k_derived->sym_root, name);
+ if (st)
+ break;
+ }
+ }
+ *result = st;
+ return 0;
+ }
+
+ *result = NULL;
+
+ return 0;
+}
+
+
+/* Same, but returns the symbol instead. */
+
+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;
+}
+
+
+/* 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. */
+
+void
+gfc_save_symbol_data (gfc_symbol *sym)
+{
+ gfc_symbol *s;
+ unsigned i;
+
+ 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;
+
+ s = XCNEW (gfc_symbol);
+ *s = *sym;
+ sym->old_symbol = s;
+ sym->gfc_new = 0;
+
+ latest_undo_chgset->syms.safe_push (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,
+ bool allow_subroutine)
+{
+ 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 && ns->omp_udr_ns)
+ {
+ ns = ns->parent;
+ 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->mark = 1;
+ p->gfc_new = 1;
+ latest_undo_chgset->syms.safe_push (p);
+
+ st = gfc_new_symtree (&ns->sym_root, name);
+ st->n.sym = p;
+ p->refs++;
+
+ }
+ else
+ {
+ /* Make sure the existing symbol is OK. Ambiguous
+ generic interfaces are permitted, as long as the
+ specific interfaces are different. */
+ if (st->ambiguous && !st->n.sym->attr.generic)
+ {
+ ambiguous_symbol (name, st);
+ return 1;
+ }
+
+ p = st->n.sym;
+ if (p->ns != ns && (!p->attr.function || ns->proc_name != p)
+ && !(allow_subroutine && p->attr.subroutine)
+ && !(ns->proc_name && ns->proc_name->attr.if_source == IFSRC_IFBODY
+ && (ns->has_import_set || p->attr.imported)))
+ {
+ /* Symbol is from another namespace. */
+ gfc_error ("Symbol %qs at %C has already been host associated",
+ name);
+ return 2;
+ }
+
+ p->mark = 1;
+
+ /* Copy in case this symbol is changed. */
+ gfc_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, false);
+ 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)
+ {
+ gfc_save_symbol_data (st->n.sym);
+ *result = st;
+ return i;
+ }
+
+ i = gfc_find_sym_tree (name, gfc_current_ns, 1, &st);
+ if (i)
+ return i;
+
+ if (st != NULL)
+ {
+ *result = st;
+ return 0;
+ }
+
+ return gfc_get_sym_tree (name, gfc_current_ns, result, false);
+}
+
+
+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;
+}
+
+
+/* Search for the symtree belonging to a gfc_common_head; we cannot use
+ head->name as the common_root symtree's name might be mangled. */
+
+static gfc_symtree *
+find_common_symtree (gfc_symtree *st, gfc_common_head *head)
+{
+
+ gfc_symtree *result;
+
+ if (st == NULL)
+ return NULL;
+
+ if (st->n.common == head)
+ return st;
+
+ result = find_common_symtree (st->left, head);
+ if (!result)
+ result = find_common_symtree (st->right, head);
+
+ return result;
+}
+
+
+/* Restore previous state of symbol. Just copy simple stuff. */
+
+static void
+restore_old_symbol (gfc_symbol *p)
+{
+ gfc_symbol *old;
+
+ 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)
+ {
+ gcc_checking_assert (old->value == NULL);
+ gfc_free_expr (p->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->next);
+ 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;
+ }
+
+ set_symbol_common_block (p, old->common_block);
+ p->common_head = old->common_head;
+
+ 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 since the previous checkpoint.
+ This subroutine is made simpler due to the fact that attributes are
+ never removed once added. */
+
+void
+gfc_restore_last_undo_checkpoint (void)
+{
+ gfc_symbol *p;
+ unsigned i;
+
+ FOR_EACH_VEC_ELT (latest_undo_chgset->syms, i, p)
+ {
+ /* Symbol in a common block was new. Or was old and just put in common */
+ if (p->common_block
+ && (p->gfc_new || !p->old_symbol->common_block))
+ {
+ /* If the symbol was added to any common block, it
+ needs to be removed to stop the resolver looking
+ for a (possibly) dead symbol. */
+ if (p->common_block->head == p && !p->common_next)
+ {
+ gfc_symtree st, *st0;
+ st0 = find_common_symtree (p->ns->common_root,
+ p->common_block);
+ if (st0)
+ {
+ st.name = st0->name;
+ gfc_delete_bbt (&p->ns->common_root, &st, compare_symtree);
+ free (st0);
+ }
+ }
+
+ if (p->common_block->head == p)
+ p->common_block->head = p->common_next;
+ else
+ {
+ gfc_symbol *cparent, *csym;
+
+ cparent = p->common_block->head;
+ csym = cparent->common_next;
+
+ while (csym != p)
+ {
+ cparent = csym;
+ csym = csym->common_next;
+ }
+
+ gcc_assert(cparent->common_next == p);
+ cparent->common_next = csym->common_next;
+ }
+ p->common_next = NULL;
+ }
+ if (p->gfc_new)
+ {
+ /* The derived type is saved in the symtree with the first
+ letter capitalized; the all lower-case version to the
+ derived type contains its associated generic function. */
+ if (gfc_fl_struct (p->attr.flavor))
+ gfc_delete_symtree (&p->ns->sym_root,gfc_dt_upper_string (p->name));
+ else
+ gfc_delete_symtree (&p->ns->sym_root, p->name);
+
+ gfc_release_symbol (p);
+ }
+ else
+ restore_old_symbol (p);
+ }
+
+ 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 ();
+}
+
+
+/* Free sym->old_symbol. sym->old_symbol is mostly a shallow copy of sym; the
+ components of old_symbol that might need deallocation are the "allocatables"
+ that are restored in gfc_undo_symbols(), with two exceptions: namelist and
+ namelist_tail. In case these differ between old_symbol and sym, it's just
+ because sym->namelist has gotten a few more items. */
+
+static void
+free_old_symbol (gfc_symbol *sym)
+{
+
+ if (sym->old_symbol == NULL)
+ return;
+
+ if (sym->old_symbol->as != sym->as)
+ gfc_free_array_spec (sym->old_symbol->as);
+
+ if (sym->old_symbol->value != sym->value)
+ gfc_free_expr (sym->old_symbol->value);
+
+ if (sym->old_symbol->formal != sym->formal)
+ gfc_free_formal_arglist (sym->old_symbol->formal);
+
+ free (sym->old_symbol);
+ sym->old_symbol = NULL;
+}
+
+
+/* Makes the changes made in the current statement permanent-- gets
+ rid of undo information. */
+
+void
+gfc_commit_symbols (void)
+{
+ gfc_symbol *p;
+ gfc_typebound_proc *tbp;
+ unsigned i;
+
+ enforce_single_undo_checkpoint ();
+
+ FOR_EACH_VEC_ELT (latest_undo_chgset->syms, i, p)
+ {
+ p->mark = 0;
+ p->gfc_new = 0;
+ free_old_symbol (p);
+ }
+ latest_undo_chgset->syms.truncate (0);
+
+ FOR_EACH_VEC_ELT (latest_undo_chgset->tbps, i, tbp)
+ tbp->error = 0;
+ latest_undo_chgset->tbps.truncate (0);
+}
+
+
+/* Makes the changes made in one symbol permanent -- gets rid of undo
+ information. */
+
+void
+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)
+ {
+ latest_undo_chgset->syms.unordered_remove (i);
+ break;
+ }
+
+ sym->mark = 0;
+ sym->gfc_new = 0;
+
+ free_old_symbol (sym);
+}
+
+
+/* Recursively free trees containing type-bound procedures. */
+
+static void
+free_tb_tree (gfc_symtree *t)
+{
+ if (t == NULL)
+ return;
+
+ free_tb_tree (t->left);
+ free_tb_tree (t->right);
+
+ /* TODO: Free type-bound procedure u.generic */
+ free (t->n.tb);
+ t->n.tb = NULL;
+ free (t);
+}
+
+
+/* Recursive function that deletes an entire tree and all the common
+ head structures it points to. */
+
+static void
+free_common_tree (gfc_symtree * common_tree)
+{
+ if (common_tree == NULL)
+ return;
+
+ free_common_tree (common_tree->left);
+ free_common_tree (common_tree->right);
+
+ free (common_tree);
+}
+
+
+/* Recursive function that deletes an entire tree and all the common
+ head structures it points to. */
+
+static void
+free_omp_udr_tree (gfc_symtree * omp_udr_tree)
+{
+ if (omp_udr_tree == NULL)
+ return;
+
+ free_omp_udr_tree (omp_udr_tree->left);
+ free_omp_udr_tree (omp_udr_tree->right);
+
+ gfc_free_omp_udr (omp_udr_tree->n.omp_udr);
+ free (omp_udr_tree);
+}
+
+
+/* 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->op);
+ free (uop_tree->n.uop);
+ 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)
+{
+ if (sym_tree == NULL)
+ return;
+
+ free_sym_tree (sym_tree->left);
+ free_sym_tree (sym_tree->right);
+
+ gfc_release_symbol (sym_tree->n.sym);
+ free (sym_tree);
+}
+
+
+/* Free the gfc_equiv_info's. */
+
+static void
+gfc_free_equiv_infos (gfc_equiv_info *s)
+{
+ if (s == NULL)
+ return;
+ gfc_free_equiv_infos (s->next);
+ free (s);
+}
+
+
+/* Free the gfc_equiv_lists. */
+
+static void
+gfc_free_equiv_lists (gfc_equiv_list *l)
+{
+ if (l == NULL)
+ return;
+ gfc_free_equiv_lists (l->next);
+ gfc_free_equiv_infos (l->equiv);
+ free (l);
+}
+
+
+/* Free a finalizer procedure list. */
+
+void
+gfc_free_finalizer (gfc_finalizer* el)
+{
+ if (el)
+ {
+ gfc_release_symbol (el->proc_sym);
+ free (el);
+ }
+}
+
+static void
+gfc_free_finalizer_list (gfc_finalizer* list)
+{
+ while (list)
+ {
+ gfc_finalizer* current = list;
+ list = list->next;
+ gfc_free_finalizer (current);
+ }
+}
+
+
+/* Create a new gfc_charlen structure and add it to a namespace.
+ If 'old_cl' is given, the newly created charlen will be a copy of it. */
+
+gfc_charlen*
+gfc_new_charlen (gfc_namespace *ns, gfc_charlen *old_cl)
+{
+ gfc_charlen *cl;
+
+ cl = gfc_get_charlen ();
+
+ /* Copy old_cl. */
+ if (old_cl)
+ {
+ cl->length = gfc_copy_expr (old_cl->length);
+ cl->length_from_typespec = old_cl->length_from_typespec;
+ cl->backend_decl = old_cl->backend_decl;
+ cl->passed_length = old_cl->passed_length;
+ cl->resolved = old_cl->resolved;
+ }
+
+ /* Put into namespace. */
+ cl->next = ns->cl_list;
+ ns->cl_list = cl;
+
+ return cl;
+}
+
+
+/* Free the charlen list from cl to end (end is not freed).
+ Free the whole list if end is NULL. */
+
+static void
+gfc_free_charlen (gfc_charlen *cl, gfc_charlen *end)
+{
+ gfc_charlen *cl2;
+
+ for (; cl != end; cl = cl2)
+ {
+ gcc_assert (cl);
+
+ cl2 = cl->next;
+ gfc_free_expr (cl->length);
+ free (cl);
+ }
+}
+
+
+/* Free entry list structs. */
+
+static void
+free_entry_list (gfc_entry_list *el)
+{
+ gfc_entry_list *next;
+
+ if (el == NULL)
+ return;
+
+ next = el->next;
+ free (el);
+ free_entry_list (next);
+}
+
+
+/* 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_namespace *p, *q;
+ int i;
+ gfc_was_finalized *f;
+
+ if (ns == NULL)
+ return;
+
+ ns->refs--;
+ if (ns->refs > 0)
+ return;
+
+ gcc_assert (ns->refs == 0);
+
+ gfc_free_statements (ns->code);
+
+ free_sym_tree (ns->sym_root);
+ free_uop_tree (ns->uop_root);
+ free_common_tree (ns->common_root);
+ free_omp_udr_tree (ns->omp_udr_root);
+ free_tb_tree (ns->tb_sym_root);
+ free_tb_tree (ns->tb_uop_root);
+ gfc_free_finalizer_list (ns->finalizers);
+ gfc_free_omp_declare_simd_list (ns->omp_declare_simd);
+ gfc_free_omp_declare_variant_list (ns->omp_declare_variant);
+ gfc_free_charlen (ns->cl_list, NULL);
+ free_st_labels (ns->st_labels);
+
+ free_entry_list (ns->entries);
+ gfc_free_equiv (ns->equiv);
+ gfc_free_equiv_lists (ns->equiv_lists);
+ gfc_free_use_stmts (ns->use_stmts);
+
+ for (i = GFC_INTRINSIC_BEGIN; i != GFC_INTRINSIC_END; i++)
+ gfc_free_interface (ns->op[i]);
+
+ gfc_free_data (ns->data);
+
+ /* Free all the expr + component combinations that have been
+ finalized. */
+ f = ns->was_finalized;
+ while (f)
+ {
+ gfc_was_finalized* current = f;
+ f = f->next;
+ free (current);
+ }
+
+ p = ns->contained;
+ free (ns);
+ ns = NULL;
+
+ /* 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, 0);
+}
+
+
+void
+gfc_symbol_done_2 (void)
+{
+ if (gfc_current_ns != NULL)
+ {
+ /* free everything from the root. */
+ while (gfc_current_ns->parent != NULL)
+ gfc_current_ns = gfc_current_ns->parent;
+ gfc_free_namespace (gfc_current_ns);
+ gfc_current_ns = NULL;
+ }
+ gfc_derived_types = NULL;
+
+ enforce_single_undo_checkpoint ();
+ free_undo_change_set_data (*latest_undo_chgset);
+}
+
+
+/* Count how many nodes a symtree has. */
+
+static unsigned
+count_st_nodes (const gfc_symtree *st)
+{
+ unsigned nodes;
+ if (!st)
+ return 0;
+
+ nodes = count_st_nodes (st->left);
+ nodes++;
+ nodes += count_st_nodes (st->right);
+
+ return nodes;
+}
+
+
+/* Convert symtree tree into symtree vector. */
+
+static unsigned
+fill_st_vector (gfc_symtree *st, gfc_symtree **st_vec, unsigned node_cntr)
+{
+ if (!st)
+ return node_cntr;
+
+ node_cntr = fill_st_vector (st->left, st_vec, node_cntr);
+ st_vec[node_cntr++] = st;
+ node_cntr = fill_st_vector (st->right, st_vec, node_cntr);
+
+ return node_cntr;
+}
+
+
+/* Traverse namespace. As the functions might modify the symtree, we store the
+ symtree as a vector and operate on this vector. Note: We assume that
+ sym_func or st_func never deletes nodes from the symtree - only adding is
+ allowed. Additionally, newly added nodes are not traversed. */
+
+static void
+do_traverse_symtree (gfc_symtree *st, void (*st_func) (gfc_symtree *),
+ void (*sym_func) (gfc_symbol *))
+{
+ gfc_symtree **st_vec;
+ unsigned nodes, i, node_cntr;
+
+ gcc_assert ((st_func && !sym_func) || (!st_func && sym_func));
+ nodes = count_st_nodes (st);
+ st_vec = XALLOCAVEC (gfc_symtree *, nodes);
+ node_cntr = 0;
+ fill_st_vector (st, st_vec, node_cntr);
+
+ if (sym_func)
+ {
+ /* Clear marks. */
+ for (i = 0; i < nodes; i++)
+ st_vec[i]->n.sym->mark = 0;
+ for (i = 0; i < nodes; i++)
+ if (!st_vec[i]->n.sym->mark)
+ {
+ (*sym_func) (st_vec[i]->n.sym);
+ st_vec[i]->n.sym->mark = 1;
+ }
+ }
+ else
+ for (i = 0; i < nodes; i++)
+ (*st_func) (st_vec[i]);
+}
+
+
+/* Recursively traverse the symtree nodes. */
+
+void
+gfc_traverse_symtree (gfc_symtree *st, void (*st_func) (gfc_symtree *))
+{
+ do_traverse_symtree (st, st_func, NULL);
+}
+
+
+/* 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 (*sym_func) (gfc_symbol *))
+{
+ do_traverse_symtree (ns->sym_root, NULL, sym_func);
+}
+
+
+/* Return TRUE when name is the name of an intrinsic type. */
+
+bool
+gfc_is_intrinsic_typename (const char *name)
+{
+ if (strcmp (name, "integer") == 0
+ || strcmp (name, "real") == 0
+ || strcmp (name, "character") == 0
+ || strcmp (name, "logical") == 0
+ || strcmp (name, "complex") == 0
+ || strcmp (name, "doubleprecision") == 0
+ || strcmp (name, "doublecomplex") == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/* Return TRUE if the symbol is an automatic variable. */
+
+static bool
+gfc_is_var_automatic (gfc_symbol *sym)
+{
+ /* Pointer and allocatable variables are never automatic. */
+ if (sym->attr.pointer || sym->attr.allocatable)
+ return false;
+ /* Check for arrays with non-constant size. */
+ if (sym->attr.dimension && sym->as
+ && !gfc_is_compile_time_shape (sym->as))
+ return true;
+ /* Check for non-constant length character variables. */
+ if (sym->ts.type == BT_CHARACTER
+ && sym->ts.u.cl
+ && !gfc_is_constant_expr (sym->ts.u.cl->length))
+ return true;
+ /* Variables with explicit AUTOMATIC attribute. */
+ if (sym->attr.automatic)
+ return true;
+
+ return false;
+}
+
+/* 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.in_common
+ || sym->attr.in_equivalence
+ || sym->attr.dummy
+ || sym->attr.result
+ || sym->attr.flavor != FL_VARIABLE)
+ return;
+ /* Automatic objects are not saved. */
+ if (gfc_is_var_automatic (sym))
+ return;
+ gfc_add_save (&sym->attr, SAVE_EXPLICIT, sym->name, &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);
+}
+
+
+/* Make sure that no changes to symbols are pending. */
+
+void
+gfc_enforce_clean_symbol_state(void)
+{
+ enforce_single_undo_checkpoint ();
+ gcc_assert (latest_undo_chgset->syms.is_empty ());
+}
+
+
+/************** Global symbol handling ************/
+
+
+/* Search a tree for the global symbol. */
+
+gfc_gsymbol *
+gfc_find_gsymbol (gfc_gsymbol *symbol, const char *name)
+{
+ int c;
+
+ if (symbol == NULL)
+ return NULL;
+
+ while (symbol)
+ {
+ c = strcmp (name, symbol->name);
+ if (!c)
+ return symbol;
+
+ symbol = (c < 0) ? symbol->left : symbol->right;
+ }
+
+ return NULL;
+}
+
+
+/* Case insensitive search a tree for the global symbol. */
+
+gfc_gsymbol *
+gfc_find_case_gsymbol (gfc_gsymbol *symbol, const char *name)
+{
+ int c;
+
+ if (symbol == NULL)
+ return NULL;
+
+ while (symbol)
+ {
+ c = strcasecmp (name, symbol->name);
+ if (!c)
+ return symbol;
+
+ symbol = (c < 0) ? symbol->left : symbol->right;
+ }
+
+ return NULL;
+}
+
+
+/* Compare two global symbols. Used for managing the BB tree. */
+
+static int
+gsym_compare (void *_s1, void *_s2)
+{
+ gfc_gsymbol *s1, *s2;
+
+ s1 = (gfc_gsymbol *) _s1;
+ s2 = (gfc_gsymbol *) _s2;
+ return strcmp (s1->name, s2->name);
+}
+
+
+/* Get a global symbol, creating it if it doesn't exist. */
+
+gfc_gsymbol *
+gfc_get_gsymbol (const char *name, bool bind_c)
+{
+ gfc_gsymbol *s;
+
+ s = gfc_find_gsymbol (gfc_gsym_root, name);
+ if (s != NULL)
+ return s;
+
+ s = XCNEW (gfc_gsymbol);
+ s->type = GSYM_UNKNOWN;
+ s->name = gfc_get_string ("%s", name);
+ s->bind_c = bind_c;
+
+ gfc_insert_bbt (&gfc_gsym_root, s, gsym_compare);
+
+ return s;
+}
+
+void
+gfc_traverse_gsymbol (gfc_gsymbol *gsym,
+ void (*do_something) (gfc_gsymbol *, void *),
+ void *data)
+{
+ if (gsym->left)
+ gfc_traverse_gsymbol (gsym->left, do_something, data);
+
+ (*do_something) (gsym, data);
+
+ if (gsym->right)
+ gfc_traverse_gsymbol (gsym->right, do_something, data);
+}
+
+static gfc_symbol *
+get_iso_c_binding_dt (int sym_id)
+{
+ gfc_symbol *dt_list = gfc_derived_types;
+
+ /* Loop through the derived types in the name list, searching for
+ the desired symbol from iso_c_binding. Search the parent namespaces
+ if necessary and requested to (parent_flag). */
+ if (dt_list)
+ {
+ while (dt_list->dt_next != gfc_derived_types)
+ {
+ if (dt_list->from_intmod != INTMOD_NONE
+ && dt_list->intmod_sym_id == sym_id)
+ return dt_list;
+
+ dt_list = dt_list->dt_next;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Verifies that the given derived type symbol, derived_sym, is interoperable
+ with C. This is necessary for any derived type that is BIND(C) and for
+ derived types that are parameters to functions that are BIND(C). All
+ fields of the derived type are required to be interoperable, and are tested
+ for such. If an error occurs, the errors are reported here, allowing for
+ multiple errors to be handled for a single derived type. */
+
+bool
+verify_bind_c_derived_type (gfc_symbol *derived_sym)
+{
+ gfc_component *curr_comp = NULL;
+ bool is_c_interop = false;
+ bool retval = true;
+
+ if (derived_sym == NULL)
+ gfc_internal_error ("verify_bind_c_derived_type(): Given symbol is "
+ "unexpectedly NULL");
+
+ /* If we've already looked at this derived symbol, do not look at it again
+ so we don't repeat warnings/errors. */
+ if (derived_sym->ts.is_c_interop)
+ return true;
+
+ /* The derived type must have the BIND attribute to be interoperable
+ J3/04-007, Section 15.2.3. */
+ if (derived_sym->attr.is_bind_c != 1)
+ {
+ derived_sym->ts.is_c_interop = 0;
+ gfc_error_now ("Derived type %qs declared at %L must have the BIND "
+ "attribute to be C interoperable", derived_sym->name,
+ &(derived_sym->declared_at));
+ retval = false;
+ }
+
+ curr_comp = derived_sym->components;
+
+ /* Fortran 2003 allows an empty derived type. C99 appears to disallow an
+ empty struct. Section 15.2 in Fortran 2003 states: "The following
+ subclauses define the conditions under which a Fortran entity is
+ interoperable. If a Fortran entity is interoperable, an equivalent
+ entity may be defined by means of C and the Fortran entity is said
+ to be interoperable with the C entity. There does not have to be such
+ an interoperating C entity."
+ */
+ if (curr_comp == NULL)
+ {
+ gfc_warning (0, "Derived type %qs with BIND(C) attribute at %L is empty, "
+ "and may be inaccessible by the C companion processor",
+ derived_sym->name, &(derived_sym->declared_at));
+ derived_sym->ts.is_c_interop = 1;
+ derived_sym->attr.is_bind_c = 1;
+ return true;
+ }
+
+
+ /* Initialize the derived type as being C interoperable.
+ If we find an error in the components, this will be set false. */
+ derived_sym->ts.is_c_interop = 1;
+
+ /* Loop through the list of components to verify that the kind of
+ each is a C interoperable type. */
+ do
+ {
+ /* The components cannot be pointers (fortran sense).
+ J3/04-007, Section 15.2.3, C1505. */
+ if (curr_comp->attr.pointer != 0)
+ {
+ gfc_error ("Component %qs at %L cannot have the "
+ "POINTER attribute because it is a member "
+ "of the BIND(C) derived type %qs at %L",
+ curr_comp->name, &(curr_comp->loc),
+ derived_sym->name, &(derived_sym->declared_at));
+ retval = false;
+ }
+
+ if (curr_comp->attr.proc_pointer != 0)
+ {
+ gfc_error ("Procedure pointer component %qs at %L cannot be a member"
+ " of the BIND(C) derived type %qs at %L", curr_comp->name,
+ &curr_comp->loc, derived_sym->name,
+ &derived_sym->declared_at);
+ retval = false;
+ }
+
+ /* The components cannot be allocatable.
+ J3/04-007, Section 15.2.3, C1505. */
+ if (curr_comp->attr.allocatable != 0)
+ {
+ gfc_error ("Component %qs at %L cannot have the "
+ "ALLOCATABLE attribute because it is a member "
+ "of the BIND(C) derived type %qs at %L",
+ curr_comp->name, &(curr_comp->loc),
+ derived_sym->name, &(derived_sym->declared_at));
+ retval = false;
+ }
+
+ /* BIND(C) derived types must have interoperable components. */
+ if (curr_comp->ts.type == BT_DERIVED
+ && curr_comp->ts.u.derived->ts.is_iso_c != 1
+ && curr_comp->ts.u.derived != derived_sym)
+ {
+ /* This should be allowed; the draft says a derived-type cannot
+ have type parameters if it is has the BIND attribute. Type
+ parameters seem to be for making parameterized derived types.
+ There's no need to verify the type if it is c_ptr/c_funptr. */
+ retval = verify_bind_c_derived_type (curr_comp->ts.u.derived);
+ }
+ else
+ {
+ /* Grab the typespec for the given component and test the kind. */
+ is_c_interop = gfc_verify_c_interop (&(curr_comp->ts));
+
+ if (!is_c_interop)
+ {
+ /* Report warning and continue since not fatal. The
+ draft does specify a constraint that requires all fields
+ to interoperate, but if the user says real(4), etc., it
+ may interoperate with *something* in C, but the compiler
+ most likely won't know exactly what. Further, it may not
+ interoperate with the same data type(s) in C if the user
+ recompiles with different flags (e.g., -m32 and -m64 on
+ x86_64 and using integer(4) to claim interop with a
+ C_LONG). */
+ if (derived_sym->attr.is_bind_c == 1 && warn_c_binding_type)
+ /* If the derived type is bind(c), all fields must be
+ interop. */
+ gfc_warning (OPT_Wc_binding_type,
+ "Component %qs in derived type %qs at %L "
+ "may not be C interoperable, even though "
+ "derived type %qs is BIND(C)",
+ curr_comp->name, derived_sym->name,
+ &(curr_comp->loc), derived_sym->name);
+ else if (warn_c_binding_type)
+ /* If derived type is param to bind(c) routine, or to one
+ of the iso_c_binding procs, it must be interoperable, so
+ all fields must interop too. */
+ gfc_warning (OPT_Wc_binding_type,
+ "Component %qs in derived type %qs at %L "
+ "may not be C interoperable",
+ curr_comp->name, derived_sym->name,
+ &(curr_comp->loc));
+ }
+ }
+
+ curr_comp = curr_comp->next;
+ } while (curr_comp != NULL);
+
+ if (derived_sym->attr.sequence != 0)
+ {
+ gfc_error ("Derived type %qs at %L cannot have the SEQUENCE "
+ "attribute because it is BIND(C)", derived_sym->name,
+ &(derived_sym->declared_at));
+ retval = false;
+ }
+
+ /* Mark the derived type as not being C interoperable if we found an
+ error. If there were only warnings, proceed with the assumption
+ it's interoperable. */
+ if (!retval)
+ derived_sym->ts.is_c_interop = 0;
+
+ return retval;
+}
+
+
+/* Generate symbols for the named constants c_null_ptr and c_null_funptr. */
+
+static bool
+gen_special_c_interop_ptr (gfc_symbol *tmp_sym, gfc_symtree *dt_symtree)
+{
+ gfc_constructor *c;
+
+ gcc_assert (tmp_sym && dt_symtree && dt_symtree->n.sym);
+ dt_symtree->n.sym->attr.referenced = 1;
+
+ tmp_sym->attr.is_c_interop = 1;
+ tmp_sym->attr.is_bind_c = 1;
+ tmp_sym->ts.is_c_interop = 1;
+ tmp_sym->ts.is_iso_c = 1;
+ tmp_sym->ts.type = BT_DERIVED;
+ tmp_sym->ts.f90_type = BT_VOID;
+ tmp_sym->attr.flavor = FL_PARAMETER;
+ tmp_sym->ts.u.derived = dt_symtree->n.sym;
+
+ /* Set the c_address field of c_null_ptr and c_null_funptr to
+ the value of NULL. */
+ tmp_sym->value = gfc_get_expr ();
+ tmp_sym->value->expr_type = EXPR_STRUCTURE;
+ tmp_sym->value->ts.type = BT_DERIVED;
+ tmp_sym->value->ts.f90_type = BT_VOID;
+ tmp_sym->value->ts.u.derived = tmp_sym->ts.u.derived;
+ gfc_constructor_append_expr (&tmp_sym->value->value.constructor, NULL, NULL);
+ c = gfc_constructor_first (tmp_sym->value->value.constructor);
+ c->expr = gfc_get_int_expr (gfc_index_integer_kind, NULL, 0);
+ c->expr->ts.is_iso_c = 1;
+
+ return true;
+}
+
+
+/* Add a formal argument, gfc_formal_arglist, to the
+ end of the given list of arguments. Set the reference to the
+ provided symbol, param_sym, in the argument. */
+
+static void
+add_formal_arg (gfc_formal_arglist **head,
+ gfc_formal_arglist **tail,
+ gfc_formal_arglist *formal_arg,
+ gfc_symbol *param_sym)
+{
+ /* Put in list, either as first arg or at the tail (curr arg). */
+ if (*head == NULL)
+ *head = *tail = formal_arg;
+ else
+ {
+ (*tail)->next = formal_arg;
+ (*tail) = formal_arg;
+ }
+
+ (*tail)->sym = param_sym;
+ (*tail)->next = NULL;
+
+ return;
+}
+
+
+/* Add a procedure interface to the given symbol (i.e., store a
+ reference to the list of formal arguments). */
+
+static void
+add_proc_interface (gfc_symbol *sym, ifsrc source, gfc_formal_arglist *formal)
+{
+
+ sym->formal = formal;
+ sym->attr.if_source = source;
+}
+
+
+/* Copy the formal args from an existing symbol, src, into a new
+ symbol, dest. New formal args are created, and the description of
+ each arg is set according to the existing ones. This function is
+ used when creating procedure declaration variables from a procedure
+ declaration statement (see match_proc_decl()) to create the formal
+ args based on the args of a given named interface.
+
+ When an actual argument list is provided, skip the absent arguments
+ unless copy_type is true.
+ To be used together with gfc_se->ignore_optional. */
+
+void
+gfc_copy_formal_args_intr (gfc_symbol *dest, gfc_intrinsic_sym *src,
+ gfc_actual_arglist *actual, bool copy_type)
+{
+ gfc_formal_arglist *head = NULL;
+ gfc_formal_arglist *tail = NULL;
+ gfc_formal_arglist *formal_arg = NULL;
+ gfc_intrinsic_arg *curr_arg = NULL;
+ gfc_formal_arglist *formal_prev = NULL;
+ gfc_actual_arglist *act_arg = actual;
+ /* Save current namespace so we can change it for formal args. */
+ gfc_namespace *parent_ns = gfc_current_ns;
+
+ /* Create a new namespace, which will be the formal ns (namespace
+ of the formal args). */
+ gfc_current_ns = gfc_get_namespace (parent_ns, 0);
+ gfc_current_ns->proc_name = dest;
+
+ for (curr_arg = src->formal; curr_arg; curr_arg = curr_arg->next)
+ {
+ /* Skip absent arguments. */
+ if (actual)
+ {
+ gcc_assert (act_arg != NULL);
+ if (act_arg->expr == NULL)
+ {
+ act_arg = act_arg->next;
+ continue;
+ }
+ }
+ formal_arg = gfc_get_formal_arglist ();
+ gfc_get_symbol (curr_arg->name, gfc_current_ns, &(formal_arg->sym));
+
+ /* May need to copy more info for the symbol. */
+ if (copy_type && act_arg->expr != NULL)
+ {
+ formal_arg->sym->ts = act_arg->expr->ts;
+ if (act_arg->expr->rank > 0)
+ {
+ formal_arg->sym->attr.dimension = 1;
+ formal_arg->sym->as = gfc_get_array_spec();
+ formal_arg->sym->as->rank = -1;
+ formal_arg->sym->as->type = AS_ASSUMED_RANK;
+ }
+ if (act_arg->name && strcmp (act_arg->name, "%VAL") == 0)
+ formal_arg->sym->pass_as_value = 1;
+ }
+ else
+ formal_arg->sym->ts = curr_arg->ts;
+
+ formal_arg->sym->attr.optional = curr_arg->optional;
+ formal_arg->sym->attr.value = curr_arg->value;
+ formal_arg->sym->attr.intent = curr_arg->intent;
+ formal_arg->sym->attr.flavor = FL_VARIABLE;
+ formal_arg->sym->attr.dummy = 1;
+
+ if (formal_arg->sym->ts.type == BT_CHARACTER)
+ formal_arg->sym->ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL);
+
+ /* If this isn't the first arg, set up the next ptr. For the
+ last arg built, the formal_arg->next will never get set to
+ anything other than NULL. */
+ if (formal_prev != NULL)
+ formal_prev->next = formal_arg;
+ else
+ formal_arg->next = NULL;
+
+ formal_prev = formal_arg;
+
+ /* Add arg to list of formal args. */
+ add_formal_arg (&head, &tail, formal_arg, formal_arg->sym);
+
+ /* Validate changes. */
+ gfc_commit_symbol (formal_arg->sym);
+ if (actual)
+ act_arg = act_arg->next;
+ }
+
+ /* Add the interface to the symbol. */
+ add_proc_interface (dest, IFSRC_DECL, head);
+
+ /* Store the formal namespace information. */
+ if (dest->formal != NULL)
+ /* The current ns should be that for the dest proc. */
+ dest->formal_ns = gfc_current_ns;
+ /* Restore the current namespace to what it was on entry. */
+ gfc_current_ns = parent_ns;
+}
+
+
+static int
+std_for_isocbinding_symbol (int id)
+{
+ switch (id)
+ {
+#define NAMED_INTCST(a,b,c,d) \
+ case a:\
+ return d;
+#include "iso-c-binding.def"
+#undef NAMED_INTCST
+
+#define NAMED_FUNCTION(a,b,c,d) \
+ case a:\
+ return d;
+#define NAMED_SUBROUTINE(a,b,c,d) \
+ case a:\
+ return d;
+#include "iso-c-binding.def"
+#undef NAMED_FUNCTION
+#undef NAMED_SUBROUTINE
+
+ default:
+ return GFC_STD_F2003;
+ }
+}
+
+/* Generate the given set of C interoperable kind objects, or all
+ interoperable kinds. This function will only be given kind objects
+ for valid iso_c_binding defined types because this is verified when
+ the 'use' statement is parsed. If the user gives an 'only' clause,
+ the specific kinds are looked up; if they don't exist, an error is
+ reported. If the user does not give an 'only' clause, all
+ iso_c_binding symbols are generated. If a list of specific kinds
+ is given, it must have a NULL in the first empty spot to mark the
+ end of the list. For C_null_(fun)ptr, dt_symtree has to be set and
+ point to the symtree for c_(fun)ptr. */
+
+gfc_symtree *
+generate_isocbinding_symbol (const char *mod_name, iso_c_binding_symbol s,
+ const char *local_name, gfc_symtree *dt_symtree,
+ bool hidden)
+{
+ const char *const name = (local_name && local_name[0])
+ ? local_name : c_interop_kinds_table[s].name;
+ gfc_symtree *tmp_symtree;
+ gfc_symbol *tmp_sym = NULL;
+ int index;
+
+ if (gfc_notification_std (std_for_isocbinding_symbol (s)) == ERROR)
+ return NULL;
+
+ tmp_symtree = gfc_find_symtree (gfc_current_ns->sym_root, name);
+ if (hidden
+ && (!tmp_symtree || !tmp_symtree->n.sym
+ || tmp_symtree->n.sym->from_intmod != INTMOD_ISO_C_BINDING
+ || tmp_symtree->n.sym->intmod_sym_id != s))
+ tmp_symtree = NULL;
+
+ /* Already exists in this scope so don't re-add it. */
+ if (tmp_symtree != NULL && (tmp_sym = tmp_symtree->n.sym) != NULL
+ && (!tmp_sym->attr.generic
+ || (tmp_sym = gfc_find_dt_in_generic (tmp_sym)) != NULL)
+ && tmp_sym->from_intmod == INTMOD_ISO_C_BINDING)
+ {
+ if (tmp_sym->attr.flavor == FL_DERIVED
+ && !get_iso_c_binding_dt (tmp_sym->intmod_sym_id))
+ {
+ if (gfc_derived_types)
+ {
+ tmp_sym->dt_next = gfc_derived_types->dt_next;
+ gfc_derived_types->dt_next = tmp_sym;
+ }
+ else
+ {
+ tmp_sym->dt_next = tmp_sym;
+ }
+ gfc_derived_types = tmp_sym;
+ }
+
+ return tmp_symtree;
+ }
+
+ /* Create the sym tree in the current ns. */
+ if (hidden)
+ {
+ tmp_symtree = gfc_get_unique_symtree (gfc_current_ns);
+ tmp_sym = gfc_new_symbol (name, gfc_current_ns);
+
+ /* Add to the list of tentative symbols. */
+ latest_undo_chgset->syms.safe_push (tmp_sym);
+ tmp_sym->old_symbol = NULL;
+ tmp_sym->mark = 1;
+ tmp_sym->gfc_new = 1;
+
+ tmp_symtree->n.sym = tmp_sym;
+ tmp_sym->refs++;
+ }
+ else
+ {
+ gfc_get_sym_tree (name, gfc_current_ns, &tmp_symtree, false);
+ gcc_assert (tmp_symtree);
+ tmp_sym = tmp_symtree->n.sym;
+ }
+
+ /* Say what module this symbol belongs to. */
+ tmp_sym->module = gfc_get_string ("%s", mod_name);
+ tmp_sym->from_intmod = INTMOD_ISO_C_BINDING;
+ tmp_sym->intmod_sym_id = s;
+ tmp_sym->attr.is_iso_c = 1;
+ tmp_sym->attr.use_assoc = 1;
+
+ gcc_assert (dt_symtree == NULL || s == ISOCBINDING_NULL_FUNPTR
+ || s == ISOCBINDING_NULL_PTR);
+
+ switch (s)
+ {
+
+#define NAMED_INTCST(a,b,c,d) case a :
+#define NAMED_REALCST(a,b,c,d) case a :
+#define NAMED_CMPXCST(a,b,c,d) case a :
+#define NAMED_LOGCST(a,b,c) case a :
+#define NAMED_CHARKNDCST(a,b,c) case a :
+#include "iso-c-binding.def"
+
+ tmp_sym->value = gfc_get_int_expr (gfc_default_integer_kind, NULL,
+ c_interop_kinds_table[s].value);
+
+ /* Initialize an integer constant expression node. */
+ tmp_sym->attr.flavor = FL_PARAMETER;
+ tmp_sym->ts.type = BT_INTEGER;
+ tmp_sym->ts.kind = gfc_default_integer_kind;
+
+ /* Mark this type as a C interoperable one. */
+ tmp_sym->ts.is_c_interop = 1;
+ tmp_sym->ts.is_iso_c = 1;
+ tmp_sym->value->ts.is_c_interop = 1;
+ tmp_sym->value->ts.is_iso_c = 1;
+ tmp_sym->attr.is_c_interop = 1;
+
+ /* Tell what f90 type this c interop kind is valid. */
+ tmp_sym->ts.f90_type = c_interop_kinds_table[s].f90_type;
+
+ break;
+
+
+#define NAMED_CHARCST(a,b,c) case a :
+#include "iso-c-binding.def"
+
+ /* Initialize an integer constant expression node for the
+ length of the character. */
+ tmp_sym->value = gfc_get_character_expr (gfc_default_character_kind,
+ &gfc_current_locus, NULL, 1);
+ tmp_sym->value->ts.is_c_interop = 1;
+ tmp_sym->value->ts.is_iso_c = 1;
+ tmp_sym->value->value.character.length = 1;
+ tmp_sym->value->value.character.string[0]
+ = (gfc_char_t) c_interop_kinds_table[s].value;
+ tmp_sym->ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL);
+ tmp_sym->ts.u.cl->length = gfc_get_int_expr (gfc_charlen_int_kind,
+ NULL, 1);
+
+ /* May not need this in both attr and ts, but do need in
+ attr for writing module file. */
+ tmp_sym->attr.is_c_interop = 1;
+
+ tmp_sym->attr.flavor = FL_PARAMETER;
+ tmp_sym->ts.type = BT_CHARACTER;
+
+ /* Need to set it to the C_CHAR kind. */
+ tmp_sym->ts.kind = gfc_default_character_kind;
+
+ /* Mark this type as a C interoperable one. */
+ tmp_sym->ts.is_c_interop = 1;
+ tmp_sym->ts.is_iso_c = 1;
+
+ /* Tell what f90 type this c interop kind is valid. */
+ tmp_sym->ts.f90_type = BT_CHARACTER;
+
+ break;
+
+ case ISOCBINDING_PTR:
+ case ISOCBINDING_FUNPTR:
+ {
+ gfc_symbol *dt_sym;
+ gfc_component *tmp_comp = NULL;
+
+ /* Generate real derived type. */
+ if (hidden)
+ dt_sym = tmp_sym;
+ else
+ {
+ const char *hidden_name;
+ gfc_interface *intr, *head;
+
+ hidden_name = gfc_dt_upper_string (tmp_sym->name);
+ tmp_symtree = gfc_find_symtree (gfc_current_ns->sym_root,
+ hidden_name);
+ gcc_assert (tmp_symtree == NULL);
+ gfc_get_sym_tree (hidden_name, gfc_current_ns, &tmp_symtree, false);
+ dt_sym = tmp_symtree->n.sym;
+ dt_sym->name = gfc_get_string (s == ISOCBINDING_PTR
+ ? "c_ptr" : "c_funptr");
+
+ /* Generate an artificial generic function. */
+ head = tmp_sym->generic;
+ intr = gfc_get_interface ();
+ intr->sym = dt_sym;
+ intr->where = gfc_current_locus;
+ intr->next = head;
+ tmp_sym->generic = intr;
+
+ if (!tmp_sym->attr.generic
+ && !gfc_add_generic (&tmp_sym->attr, tmp_sym->name, NULL))
+ return NULL;
+
+ if (!tmp_sym->attr.function
+ && !gfc_add_function (&tmp_sym->attr, tmp_sym->name, NULL))
+ return NULL;
+ }
+
+ /* Say what module this symbol belongs to. */
+ dt_sym->module = gfc_get_string ("%s", mod_name);
+ dt_sym->from_intmod = INTMOD_ISO_C_BINDING;
+ dt_sym->intmod_sym_id = s;
+ dt_sym->attr.use_assoc = 1;
+
+ /* Initialize an integer constant expression node. */
+ dt_sym->attr.flavor = FL_DERIVED;
+ dt_sym->ts.is_c_interop = 1;
+ dt_sym->attr.is_c_interop = 1;
+ dt_sym->attr.private_comp = 1;
+ dt_sym->component_access = ACCESS_PRIVATE;
+ dt_sym->ts.is_iso_c = 1;
+ dt_sym->ts.type = BT_DERIVED;
+ dt_sym->ts.f90_type = BT_VOID;
+
+ /* A derived type must have the bind attribute to be
+ interoperable (J3/04-007, Section 15.2.3), even though
+ the binding label is not used. */
+ dt_sym->attr.is_bind_c = 1;
+
+ dt_sym->attr.referenced = 1;
+ dt_sym->ts.u.derived = dt_sym;
+
+ /* Add the symbol created for the derived type to the current ns. */
+ if (gfc_derived_types)
+ {
+ dt_sym->dt_next = gfc_derived_types->dt_next;
+ gfc_derived_types->dt_next = dt_sym;
+ }
+ else
+ {
+ dt_sym->dt_next = dt_sym;
+ }
+ gfc_derived_types = dt_sym;
+
+ gfc_add_component (dt_sym, "c_address", &tmp_comp);
+ if (tmp_comp == NULL)
+ gcc_unreachable ();
+
+ tmp_comp->ts.type = BT_INTEGER;
+
+ /* Set this because the module will need to read/write this field. */
+ tmp_comp->ts.f90_type = BT_INTEGER;
+
+ /* The kinds for c_ptr and c_funptr are the same. */
+ index = get_c_kind ("c_ptr", c_interop_kinds_table);
+ tmp_comp->ts.kind = c_interop_kinds_table[index].value;
+ tmp_comp->attr.access = ACCESS_PRIVATE;
+
+ /* Mark the component as C interoperable. */
+ tmp_comp->ts.is_c_interop = 1;
+ }
+
+ break;
+
+ case ISOCBINDING_NULL_PTR:
+ case ISOCBINDING_NULL_FUNPTR:
+ gen_special_c_interop_ptr (tmp_sym, dt_symtree);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ gfc_commit_symbol (tmp_sym);
+ return tmp_symtree;
+}
+
+
+/* Check that a symbol is already typed. If strict is not set, an untyped
+ symbol is acceptable for non-standard-conforming mode. */
+
+bool
+gfc_check_symbol_typed (gfc_symbol* sym, gfc_namespace* ns,
+ bool strict, locus where)
+{
+ gcc_assert (sym);
+
+ if (gfc_matching_prefix)
+ return true;
+
+ /* Check for the type and try to give it an implicit one. */
+ if (sym->ts.type == BT_UNKNOWN
+ && !gfc_set_default_type (sym, 0, ns))
+ {
+ if (strict)
+ {
+ gfc_error ("Symbol %qs is used before it is typed at %L",
+ sym->name, &where);
+ return false;
+ }
+
+ if (!gfc_notify_std (GFC_STD_GNU, "Symbol %qs is used before"
+ " it is typed at %L", sym->name, &where))
+ return false;
+ }
+
+ /* Everything is ok. */
+ return true;
+}
+
+
+/* Construct a typebound-procedure structure. Those are stored in a tentative
+ list and marked `error' until symbols are committed. */
+
+gfc_typebound_proc*
+gfc_get_typebound_proc (gfc_typebound_proc *tb0)
+{
+ gfc_typebound_proc *result;
+
+ result = XCNEW (gfc_typebound_proc);
+ if (tb0)
+ *result = *tb0;
+ result->error = 1;
+
+ latest_undo_chgset->tbps.safe_push (result);
+
+ return result;
+}
+
+
+/* Get the super-type of a given derived type. */
+
+gfc_symbol*
+gfc_get_derived_super_type (gfc_symbol* derived)
+{
+ gcc_assert (derived);
+
+ if (derived->attr.generic)
+ derived = gfc_find_dt_in_generic (derived);
+
+ if (!derived->attr.extension)
+ return NULL;
+
+ gcc_assert (derived->components);
+ gcc_assert (derived->components->ts.type == BT_DERIVED);
+ gcc_assert (derived->components->ts.u.derived);
+
+ if (derived->components->ts.u.derived->attr.generic)
+ return gfc_find_dt_in_generic (derived->components->ts.u.derived);
+
+ return derived->components->ts.u.derived;
+}
+
+
+/* Check if a derived type t2 is an extension of (or equal to) a type t1. */
+
+bool
+gfc_type_is_extension_of (gfc_symbol *t1, gfc_symbol *t2)
+{
+ while (!gfc_compare_derived_types (t1, t2) && t2->attr.extension)
+ t2 = gfc_get_derived_super_type (t2);
+ return gfc_compare_derived_types (t1, t2);
+}
+
+
+/* Check if two typespecs are type compatible (F03:5.1.1.2):
+ If ts1 is nonpolymorphic, ts2 must be the same type.
+ If ts1 is polymorphic (CLASS), ts2 must be an extension of ts1. */
+
+bool
+gfc_type_compatible (gfc_typespec *ts1, gfc_typespec *ts2)
+{
+ bool is_class1 = (ts1->type == BT_CLASS);
+ bool is_class2 = (ts2->type == BT_CLASS);
+ bool is_derived1 = (ts1->type == BT_DERIVED);
+ bool is_derived2 = (ts2->type == BT_DERIVED);
+ bool is_union1 = (ts1->type == BT_UNION);
+ bool is_union2 = (ts2->type == BT_UNION);
+
+ if (is_class1
+ && ts1->u.derived->components
+ && ((ts1->u.derived->attr.is_class
+ && ts1->u.derived->components->ts.u.derived->attr
+ .unlimited_polymorphic)
+ || ts1->u.derived->attr.unlimited_polymorphic))
+ return 1;
+
+ if (!is_derived1 && !is_derived2 && !is_class1 && !is_class2
+ && !is_union1 && !is_union2)
+ return (ts1->type == ts2->type);
+
+ if ((is_derived1 && is_derived2) || (is_union1 && is_union2))
+ return gfc_compare_derived_types (ts1->u.derived, ts2->u.derived);
+
+ if (is_derived1 && is_class2)
+ return gfc_compare_derived_types (ts1->u.derived,
+ ts2->u.derived->attr.is_class ?
+ ts2->u.derived->components->ts.u.derived
+ : ts2->u.derived);
+ if (is_class1 && is_derived2)
+ return gfc_type_is_extension_of (ts1->u.derived->attr.is_class ?
+ ts1->u.derived->components->ts.u.derived
+ : ts1->u.derived,
+ ts2->u.derived);
+ else if (is_class1 && is_class2)
+ return gfc_type_is_extension_of (ts1->u.derived->attr.is_class ?
+ ts1->u.derived->components->ts.u.derived
+ : ts1->u.derived,
+ ts2->u.derived->attr.is_class ?
+ ts2->u.derived->components->ts.u.derived
+ : ts2->u.derived);
+ else
+ return 0;
+}
+
+
+/* Find the parent-namespace of the current function. If we're inside
+ BLOCK constructs, it may not be the current one. */
+
+gfc_namespace*
+gfc_find_proc_namespace (gfc_namespace* ns)
+{
+ while (ns->construct_entities)
+ {
+ ns = ns->parent;
+ gcc_assert (ns);
+ }
+
+ return ns;
+}
+
+
+/* Check if an associate-variable should be translated as an `implicit' pointer
+ internally (if it is associated to a variable and not an array with
+ descriptor). */
+
+bool
+gfc_is_associate_pointer (gfc_symbol* sym)
+{
+ if (!sym->assoc)
+ return false;
+
+ if (sym->ts.type == BT_CLASS)
+ return true;
+
+ if (sym->ts.type == BT_CHARACTER
+ && sym->ts.deferred
+ && sym->assoc->target
+ && sym->assoc->target->expr_type == EXPR_FUNCTION)
+ return true;
+
+ if (!sym->assoc->variable)
+ return false;
+
+ if (sym->attr.dimension && sym->as->type != AS_EXPLICIT)
+ return false;
+
+ return true;
+}
+
+
+gfc_symbol *
+gfc_find_dt_in_generic (gfc_symbol *sym)
+{
+ gfc_interface *intr = NULL;
+
+ if (!sym || gfc_fl_struct (sym->attr.flavor))
+ return sym;
+
+ if (sym->attr.generic)
+ for (intr = sym->generic; intr; intr = intr->next)
+ if (gfc_fl_struct (intr->sym->attr.flavor))
+ break;
+ return intr ? intr->sym : NULL;
+}
+
+
+/* Get the dummy arguments from a procedure symbol. If it has been declared
+ via a PROCEDURE statement with a named interface, ts.interface will be set
+ and the arguments need to be taken from there. */
+
+gfc_formal_arglist *
+gfc_sym_get_dummy_args (gfc_symbol *sym)
+{
+ gfc_formal_arglist *dummies;
+
+ if (sym == NULL)
+ return NULL;
+
+ dummies = sym->formal;
+ if (dummies == NULL && sym->ts.interface != NULL)
+ dummies = sym->ts.interface->formal;
+
+ return dummies;
+}