diff options
Diffstat (limited to 'gcc/fortran/symbol.cc')
-rw-r--r-- | gcc/fortran/symbol.cc | 5251 |
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; +} |