/* Implementation of Fortran 2003 Polymorphism. Copyright (C) 2009, 2010 Free Software Foundation, Inc. Contributed by Paul Richard Thomas & Janus Weil 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/>. */ /* class.c -- This file contains the front end functions needed to service the implementation of Fortran 2003 polymorphism and other object-oriented features. */ /* Outline of the internal representation: Each CLASS variable is encapsulated by a class container, which is a structure with two fields: * _data: A pointer to the actual data of the variable. This field has the declared type of the class variable and its attributes (pointer/allocatable/dimension/...). * _vptr: A pointer to the vtable entry (see below) of the dynamic type. For each derived type we set up a "vtable" entry, i.e. a structure with the following fields: * _hash: A hash value serving as a unique identifier for this type. * _size: The size in bytes of the derived type. * _extends: A pointer to the vtable entry of the parent derived type. * _def_init: A pointer to a default initialized variable of this type. * _copy: A procedure pointer to a copying procedure. After these follow procedure pointer components for the specific type-bound procedures. */ #include "config.h" #include "system.h" #include "gfortran.h" #include "constructor.h" /* Insert a reference to the component of the given name. Only to be used with CLASS containers and vtables. */ void gfc_add_component_ref (gfc_expr *e, const char *name) { gfc_ref **tail = &(e->ref); gfc_ref *next = NULL; gfc_symbol *derived = e->symtree->n.sym->ts.u.derived; while (*tail != NULL) { if ((*tail)->type == REF_COMPONENT) derived = (*tail)->u.c.component->ts.u.derived; if ((*tail)->type == REF_ARRAY && (*tail)->next == NULL) break; tail = &((*tail)->next); } if (*tail != NULL && strcmp (name, "_data") == 0) next = *tail; (*tail) = gfc_get_ref(); (*tail)->next = next; (*tail)->type = REF_COMPONENT; (*tail)->u.c.sym = derived; (*tail)->u.c.component = gfc_find_component (derived, name, true, true); gcc_assert((*tail)->u.c.component); if (!next) e->ts = (*tail)->u.c.component->ts; } /* Build a NULL initializer for CLASS pointers, initializing the _data and _vptr components to zero. */ gfc_expr * gfc_class_null_initializer (gfc_typespec *ts) { gfc_expr *init; gfc_component *comp; init = gfc_get_structure_constructor_expr (ts->type, ts->kind, &ts->u.derived->declared_at); init->ts = *ts; for (comp = ts->u.derived->components; comp; comp = comp->next) { gfc_constructor *ctor = gfc_constructor_get(); ctor->expr = gfc_get_expr (); ctor->expr->expr_type = EXPR_NULL; ctor->expr->ts = comp->ts; gfc_constructor_append (&init->value.constructor, ctor); } return init; } /* Create a unique string identifier for a derived type, composed of its name and module name. This is used to construct unique names for the class containers and vtab symbols. */ static void get_unique_type_string (char *string, gfc_symbol *derived) { if (derived->module) sprintf (string, "%s_%s", derived->module, derived->name); else sprintf (string, "%s_%s", derived->ns->proc_name->name, derived->name); } /* Build a polymorphic CLASS entity, using the symbol that comes from build_sym. A CLASS entity is represented by an encapsulating type, which contains the declared type as '_data' component, plus a pointer component '_vptr' which determines the dynamic type. */ gfc_try gfc_build_class_symbol (gfc_typespec *ts, symbol_attribute *attr, gfc_array_spec **as, bool delayed_vtab) { char name[GFC_MAX_SYMBOL_LEN], tname[GFC_MAX_SYMBOL_LEN]; gfc_symbol *fclass; gfc_symbol *vtab; gfc_component *c; /* Determine the name of the encapsulating type. */ get_unique_type_string (tname, ts->u.derived); if ((*as) && (*as)->rank && attr->allocatable) sprintf (name, "__class_%s_%d_a", tname, (*as)->rank); else if ((*as) && (*as)->rank) sprintf (name, "__class_%s_%d", tname, (*as)->rank); else if (attr->pointer) sprintf (name, "__class_%s_p", tname); else if (attr->allocatable) sprintf (name, "__class_%s_a", tname); else sprintf (name, "__class_%s", tname); gfc_find_symbol (name, ts->u.derived->ns, 0, &fclass); if (fclass == NULL) { gfc_symtree *st; /* If not there, create a new symbol. */ fclass = gfc_new_symbol (name, ts->u.derived->ns); st = gfc_new_symtree (&ts->u.derived->ns->sym_root, name); st->n.sym = fclass; gfc_set_sym_referenced (fclass); fclass->refs++; fclass->ts.type = BT_UNKNOWN; fclass->attr.abstract = ts->u.derived->attr.abstract; if (ts->u.derived->f2k_derived) fclass->f2k_derived = gfc_get_namespace (NULL, 0); if (gfc_add_flavor (&fclass->attr, FL_DERIVED, NULL, &gfc_current_locus) == FAILURE) return FAILURE; /* Add component '_data'. */ if (gfc_add_component (fclass, "_data", &c) == FAILURE) return FAILURE; c->ts = *ts; c->ts.type = BT_DERIVED; c->attr.access = ACCESS_PRIVATE; c->ts.u.derived = ts->u.derived; c->attr.class_pointer = attr->pointer; c->attr.pointer = attr->pointer || attr->dummy; c->attr.allocatable = attr->allocatable; c->attr.dimension = attr->dimension; c->attr.codimension = attr->codimension; c->attr.abstract = ts->u.derived->attr.abstract; c->as = (*as); c->initializer = NULL; /* Add component '_vptr'. */ if (gfc_add_component (fclass, "_vptr", &c) == FAILURE) return FAILURE; c->ts.type = BT_DERIVED; if (delayed_vtab) c->ts.u.derived = NULL; else { vtab = gfc_find_derived_vtab (ts->u.derived); gcc_assert (vtab); c->ts.u.derived = vtab->ts.u.derived; } c->attr.access = ACCESS_PRIVATE; c->attr.pointer = 1; } /* Since the extension field is 8 bit wide, we can only have up to 255 extension levels. */ if (ts->u.derived->attr.extension == 255) { gfc_error ("Maximum extension level reached with type '%s' at %L", ts->u.derived->name, &ts->u.derived->declared_at); return FAILURE; } fclass->attr.extension = ts->u.derived->attr.extension + 1; fclass->attr.is_class = 1; ts->u.derived = fclass; attr->allocatable = attr->pointer = attr->dimension = 0; (*as) = NULL; /* XXX */ return SUCCESS; } /* Add a procedure pointer component to the vtype to represent a specific type-bound procedure. */ static void add_proc_comp (gfc_symbol *vtype, const char *name, gfc_typebound_proc *tb) { gfc_component *c; c = gfc_find_component (vtype, name, true, true); if (c == NULL) { /* Add procedure component. */ if (gfc_add_component (vtype, name, &c) == FAILURE) return; if (!c->tb) c->tb = XCNEW (gfc_typebound_proc); *c->tb = *tb; c->tb->ppc = 1; c->attr.procedure = 1; c->attr.proc_pointer = 1; c->attr.flavor = FL_PROCEDURE; c->attr.access = ACCESS_PRIVATE; c->attr.external = 1; c->attr.untyped = 1; c->attr.if_source = IFSRC_IFBODY; } else if (c->attr.proc_pointer && c->tb) { *c->tb = *tb; c->tb->ppc = 1; } if (tb->u.specific) { c->ts.interface = tb->u.specific->n.sym; if (!tb->deferred) c->initializer = gfc_get_variable_expr (tb->u.specific); } } /* Add all specific type-bound procedures in the symtree 'st' to a vtype. */ static void add_procs_to_declared_vtab1 (gfc_symtree *st, gfc_symbol *vtype) { if (!st) return; if (st->left) add_procs_to_declared_vtab1 (st->left, vtype); if (st->right) add_procs_to_declared_vtab1 (st->right, vtype); if (st->n.tb && !st->n.tb->error && !st->n.tb->is_generic && st->n.tb->u.specific) add_proc_comp (vtype, st->name, st->n.tb); } /* Copy procedure pointers components from the parent type. */ static void copy_vtab_proc_comps (gfc_symbol *declared, gfc_symbol *vtype) { gfc_component *cmp; gfc_symbol *vtab; vtab = gfc_find_derived_vtab (declared); for (cmp = vtab->ts.u.derived->components; cmp; cmp = cmp->next) { if (gfc_find_component (vtype, cmp->name, true, true)) continue; add_proc_comp (vtype, cmp->name, cmp->tb); } } /* Add procedure pointers for all type-bound procedures to a vtab. */ static void add_procs_to_declared_vtab (gfc_symbol *derived, gfc_symbol *vtype) { gfc_symbol* super_type; super_type = gfc_get_derived_super_type (derived); if (super_type && (super_type != derived)) { /* Make sure that the PPCs appear in the same order as in the parent. */ copy_vtab_proc_comps (super_type, vtype); /* Only needed to get the PPC initializers right. */ add_procs_to_declared_vtab (super_type, vtype); } if (derived->f2k_derived && derived->f2k_derived->tb_sym_root) add_procs_to_declared_vtab1 (derived->f2k_derived->tb_sym_root, vtype); if (derived->f2k_derived && derived->f2k_derived->tb_uop_root) add_procs_to_declared_vtab1 (derived->f2k_derived->tb_uop_root, vtype); } /* Find (or generate) the symbol for a derived type's vtab. */ gfc_symbol * gfc_find_derived_vtab (gfc_symbol *derived) { gfc_namespace *ns; gfc_symbol *vtab = NULL, *vtype = NULL, *found_sym = NULL, *def_init = NULL; gfc_symbol *copy = NULL, *src = NULL, *dst = NULL; /* Find the top-level namespace (MODULE or PROGRAM). */ for (ns = gfc_current_ns; ns; ns = ns->parent) if (!ns->parent) break; /* If the type is a class container, use the underlying derived type. */ if (derived->attr.is_class) derived = gfc_get_derived_super_type (derived); if (ns) { char name[GFC_MAX_SYMBOL_LEN], tname[GFC_MAX_SYMBOL_LEN]; get_unique_type_string (tname, derived); sprintf (name, "__vtab_%s", tname); /* Look for the vtab symbol in various namespaces. */ gfc_find_symbol (name, gfc_current_ns, 0, &vtab); if (vtab == NULL) gfc_find_symbol (name, ns, 0, &vtab); if (vtab == NULL) gfc_find_symbol (name, derived->ns, 0, &vtab); if (vtab == NULL) { gfc_get_symbol (name, ns, &vtab); vtab->ts.type = BT_DERIVED; if (gfc_add_flavor (&vtab->attr, FL_VARIABLE, NULL, &gfc_current_locus) == FAILURE) goto cleanup; vtab->attr.target = 1; vtab->attr.save = SAVE_EXPLICIT; vtab->attr.vtab = 1; vtab->attr.access = ACCESS_PUBLIC; gfc_set_sym_referenced (vtab); sprintf (name, "__vtype_%s", tname); gfc_find_symbol (name, ns, 0, &vtype); if (vtype == NULL) { gfc_component *c; gfc_symbol *parent = NULL, *parent_vtab = NULL; gfc_get_symbol (name, ns, &vtype); if (gfc_add_flavor (&vtype->attr, FL_DERIVED, NULL, &gfc_current_locus) == FAILURE) goto cleanup; vtype->attr.access = ACCESS_PUBLIC; vtype->attr.vtype = 1; gfc_set_sym_referenced (vtype); /* Add component '_hash'. */ if (gfc_add_component (vtype, "_hash", &c) == FAILURE) goto cleanup; c->ts.type = BT_INTEGER; c->ts.kind = 4; c->attr.access = ACCESS_PRIVATE; c->initializer = gfc_get_int_expr (gfc_default_integer_kind, NULL, derived->hash_value); /* Add component '_size'. */ if (gfc_add_component (vtype, "_size", &c) == FAILURE) goto cleanup; c->ts.type = BT_INTEGER; c->ts.kind = 4; c->attr.access = ACCESS_PRIVATE; /* Remember the derived type in ts.u.derived, so that the correct initializer can be set later on (in gfc_conv_structure). */ c->ts.u.derived = derived; c->initializer = gfc_get_int_expr (gfc_default_integer_kind, NULL, 0); /* Add component _extends. */ if (gfc_add_component (vtype, "_extends", &c) == FAILURE) goto cleanup; c->attr.pointer = 1; c->attr.access = ACCESS_PRIVATE; parent = gfc_get_derived_super_type (derived); if (parent) { parent_vtab = gfc_find_derived_vtab (parent); c->ts.type = BT_DERIVED; c->ts.u.derived = parent_vtab->ts.u.derived; c->initializer = gfc_get_expr (); c->initializer->expr_type = EXPR_VARIABLE; gfc_find_sym_tree (parent_vtab->name, parent_vtab->ns, 0, &c->initializer->symtree); } else { c->ts.type = BT_DERIVED; c->ts.u.derived = vtype; c->initializer = gfc_get_null_expr (NULL); } if (derived->components == NULL && !derived->attr.zero_comp) { /* At this point an error must have occurred. Prevent further errors on the vtype components. */ found_sym = vtab; goto have_vtype; } /* Add component _def_init. */ if (gfc_add_component (vtype, "_def_init", &c) == FAILURE) goto cleanup; c->attr.pointer = 1; c->attr.access = ACCESS_PRIVATE; c->ts.type = BT_DERIVED; c->ts.u.derived = derived; if (derived->attr.abstract) c->initializer = gfc_get_null_expr (NULL); else { /* Construct default initialization variable. */ sprintf (name, "__def_init_%s", tname); gfc_get_symbol (name, ns, &def_init); def_init->attr.target = 1; def_init->attr.save = SAVE_EXPLICIT; def_init->attr.access = ACCESS_PUBLIC; def_init->attr.flavor = FL_VARIABLE; gfc_set_sym_referenced (def_init); def_init->ts.type = BT_DERIVED; def_init->ts.u.derived = derived; def_init->value = gfc_default_initializer (&def_init->ts); c->initializer = gfc_lval_expr_from_sym (def_init); } /* Add component _copy. */ if (gfc_add_component (vtype, "_copy", &c) == FAILURE) goto cleanup; c->attr.proc_pointer = 1; c->attr.access = ACCESS_PRIVATE; c->tb = XCNEW (gfc_typebound_proc); c->tb->ppc = 1; if (derived->attr.abstract) c->initializer = gfc_get_null_expr (NULL); else { /* Set up namespace. */ gfc_namespace *sub_ns = gfc_get_namespace (ns, 0); sub_ns->sibling = ns->contained; ns->contained = sub_ns; sub_ns->resolved = 1; /* Set up procedure symbol. */ sprintf (name, "__copy_%s", tname); gfc_get_symbol (name, sub_ns, ©); sub_ns->proc_name = copy; copy->attr.flavor = FL_PROCEDURE; copy->attr.if_source = IFSRC_DECL; gfc_set_sym_referenced (copy); /* Set up formal arguments. */ gfc_get_symbol ("src", sub_ns, &src); src->ts.type = BT_DERIVED; src->ts.u.derived = derived; src->attr.flavor = FL_VARIABLE; src->attr.dummy = 1; gfc_set_sym_referenced (src); copy->formal = gfc_get_formal_arglist (); copy->formal->sym = src; gfc_get_symbol ("dst", sub_ns, &dst); dst->ts.type = BT_DERIVED; dst->ts.u.derived = derived; dst->attr.flavor = FL_VARIABLE; dst->attr.dummy = 1; gfc_set_sym_referenced (dst); copy->formal->next = gfc_get_formal_arglist (); copy->formal->next->sym = dst; /* Set up code. */ sub_ns->code = gfc_get_code (); sub_ns->code->op = EXEC_ASSIGN; sub_ns->code->expr1 = gfc_lval_expr_from_sym (dst); sub_ns->code->expr2 = gfc_lval_expr_from_sym (src); /* Set initializer. */ c->initializer = gfc_lval_expr_from_sym (copy); c->ts.interface = copy; } /* Add procedure pointers for type-bound procedures. */ add_procs_to_declared_vtab (derived, vtype); } have_vtype: vtab->ts.u.derived = vtype; vtab->value = gfc_default_initializer (&vtab->ts); } } found_sym = vtab; cleanup: /* It is unexpected to have some symbols added at resolution or code generation time. We commit the changes in order to keep a clean state. */ if (found_sym) { gfc_commit_symbol (vtab); if (vtype) gfc_commit_symbol (vtype); if (def_init) gfc_commit_symbol (def_init); if (copy) gfc_commit_symbol (copy); if (src) gfc_commit_symbol (src); if (dst) gfc_commit_symbol (dst); } else gfc_undo_symbols (); return found_sym; } /* General worker function to find either a type-bound procedure or a type-bound user operator. */ static gfc_symtree* find_typebound_proc_uop (gfc_symbol* derived, gfc_try* t, const char* name, bool noaccess, bool uop, locus* where) { gfc_symtree* res; gfc_symtree* root; /* Set correct symbol-root. */ gcc_assert (derived->f2k_derived); root = (uop ? derived->f2k_derived->tb_uop_root : derived->f2k_derived->tb_sym_root); /* Set default to failure. */ if (t) *t = FAILURE; /* Try to find it in the current type's namespace. */ res = gfc_find_symtree (root, name); if (res && res->n.tb && !res->n.tb->error) { /* We found one. */ if (t) *t = SUCCESS; if (!noaccess && derived->attr.use_assoc && res->n.tb->access == ACCESS_PRIVATE) { if (where) gfc_error ("'%s' of '%s' is PRIVATE at %L", name, derived->name, where); if (t) *t = FAILURE; } return res; } /* Otherwise, recurse on parent type if derived is an extension. */ if (derived->attr.extension) { gfc_symbol* super_type; super_type = gfc_get_derived_super_type (derived); gcc_assert (super_type); return find_typebound_proc_uop (super_type, t, name, noaccess, uop, where); } /* Nothing found. */ return NULL; } /* Find a type-bound procedure or user operator by name for a derived-type (looking recursively through the super-types). */ gfc_symtree* gfc_find_typebound_proc (gfc_symbol* derived, gfc_try* t, const char* name, bool noaccess, locus* where) { return find_typebound_proc_uop (derived, t, name, noaccess, false, where); } gfc_symtree* gfc_find_typebound_user_op (gfc_symbol* derived, gfc_try* t, const char* name, bool noaccess, locus* where) { return find_typebound_proc_uop (derived, t, name, noaccess, true, where); } /* Find a type-bound intrinsic operator looking recursively through the super-type hierarchy. */ gfc_typebound_proc* gfc_find_typebound_intrinsic_op (gfc_symbol* derived, gfc_try* t, gfc_intrinsic_op op, bool noaccess, locus* where) { gfc_typebound_proc* res; /* Set default to failure. */ if (t) *t = FAILURE; /* Try to find it in the current type's namespace. */ if (derived->f2k_derived) res = derived->f2k_derived->tb_op[op]; else res = NULL; /* Check access. */ if (res && !res->error) { /* We found one. */ if (t) *t = SUCCESS; if (!noaccess && derived->attr.use_assoc && res->access == ACCESS_PRIVATE) { if (where) gfc_error ("'%s' of '%s' is PRIVATE at %L", gfc_op2string (op), derived->name, where); if (t) *t = FAILURE; } return res; } /* Otherwise, recurse on parent type if derived is an extension. */ if (derived->attr.extension) { gfc_symbol* super_type; super_type = gfc_get_derived_super_type (derived); gcc_assert (super_type); return gfc_find_typebound_intrinsic_op (super_type, t, op, noaccess, where); } /* Nothing found. */ return NULL; } /* Get a typebound-procedure symtree or create and insert it if not yet present. This is like a very simplified version of gfc_get_sym_tree for tbp-symtrees rather than regular ones. */ gfc_symtree* gfc_get_tbp_symtree (gfc_symtree **root, const char *name) { gfc_symtree *result; result = gfc_find_symtree (*root, name); if (!result) { result = gfc_new_symtree (root, name); gcc_assert (result); result->n.tb = NULL; } return result; }