OSDN Git Service

2013-01-17 Janus Weil <janus@gcc.gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / fortran / class.c
index df3a314..d4ed6b0 100644 (file)
@@ -1,7 +1,8 @@
 /* Implementation of Fortran 2003 Polymorphism.
    Copyright (C) 2009, 2010
    Free Software Foundation, Inc.
-   Contributed by Paul Richard Thomas & Janus Weil
+   Contributed by Paul Richard Thomas <pault@gcc.gnu.org>
+   and Janus Weil <janus@gcc.gnu.org>
 
 This file is part of GCC.
 
@@ -29,19 +30,20 @@ along with GCC; see the file COPYING3.  If not see
 
    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
+    * _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.
+    * _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.
-   In addition to these fields, each vtable entry contains additional procedure
-   pointer components, which contain pointers to the procedures which are bound
-   to the type's "methods" (type-bound procedures).  */
+    * _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"
@@ -50,8 +52,147 @@ along with GCC; see the file COPYING3.  If not see
 #include "constructor.h"
 
 
+/* Inserts a derived type component reference in a data reference chain.
+    TS: base type of the ref chain so far, in which we will pick the component
+    REF: the address of the GFC_REF pointer to update
+    NAME: name of the component to insert
+   Note that component insertion makes sense only if we are at the end of
+   the chain (*REF == NULL) or if we are adding a missing "_data" component
+   to access the actual contents of a class object.  */
+
+static void
+insert_component_ref (gfc_typespec *ts, gfc_ref **ref, const char * const name)
+{
+  gfc_symbol *type_sym;
+  gfc_ref *new_ref;
+
+  gcc_assert (ts->type == BT_DERIVED || ts->type == BT_CLASS);
+  type_sym = ts->u.derived;
+
+  new_ref = gfc_get_ref ();
+  new_ref->type = REF_COMPONENT;
+  new_ref->next = *ref;
+  new_ref->u.c.sym = type_sym;
+  new_ref->u.c.component = gfc_find_component (type_sym, name, true, true);
+  gcc_assert (new_ref->u.c.component);
+
+  if (new_ref->next)
+    {
+      gfc_ref *next = NULL;
+
+      /* We need to update the base type in the trailing reference chain to
+        that of the new component.  */
+
+      gcc_assert (strcmp (name, "_data") == 0);
+
+      if (new_ref->next->type == REF_COMPONENT)
+       next = new_ref->next;
+      else if (new_ref->next->type == REF_ARRAY
+              && new_ref->next->next
+              && new_ref->next->next->type == REF_COMPONENT)
+       next = new_ref->next->next;
+
+      if (next != NULL)
+       {
+         gcc_assert (new_ref->u.c.component->ts.type == BT_CLASS
+                     || new_ref->u.c.component->ts.type == BT_DERIVED);
+         next->u.c.sym = new_ref->u.c.component->ts.u.derived;
+       }
+    }
+
+  *ref = new_ref;
+}
+
+
+/* Tells whether we need to add a "_data" reference to access REF subobject
+   from an object of type TS.  If FIRST_REF_IN_CHAIN is set, then the base
+   object accessed by REF is a variable; in other words it is a full object,
+   not a subobject.  */
+
+static bool
+class_data_ref_missing (gfc_typespec *ts, gfc_ref *ref, bool first_ref_in_chain)
+{
+  /* Only class containers may need the "_data" reference.  */
+  if (ts->type != BT_CLASS)
+    return false;
+
+  /* Accessing a class container with an array reference is certainly wrong.  */
+  if (ref->type != REF_COMPONENT)
+    return true;
+
+  /* Accessing the class container's fields is fine.  */
+  if (ref->u.c.component->name[0] == '_')
+    return false;
+
+  /* At this point we have a class container with a non class container's field
+     component reference.  We don't want to add the "_data" component if we are
+     at the first reference and the symbol's type is an extended derived type.
+     In that case, conv_parent_component_references will do the right thing so
+     it is not absolutely necessary.  Omitting it prevents a regression (see
+     class_41.f03) in the interface mapping mechanism.  When evaluating string
+     lengths depending on dummy arguments, we create a fake symbol with a type
+     equal to that of the dummy type.  However, because of type extension,
+     the backend type (corresponding to the actual argument) can have a
+     different (extended) type.  Adding the "_data" component explicitly, using
+     the base type, confuses the gfc_conv_component_ref code which deals with
+     the extended type.  */
+  if (first_ref_in_chain && ts->u.derived->attr.extension)
+    return false;
+
+  /* We have a class container with a non class container's field component
+     reference that doesn't fall into the above.  */
+  return true;
+}
+
+
+/* Browse through a data reference chain and add the missing "_data" references
+   when a subobject of a class object is accessed without it.
+   Note that it doesn't add the "_data" reference when the class container
+   is the last element in the reference chain.  */
+
+void
+gfc_fix_class_refs (gfc_expr *e)
+{
+  gfc_typespec *ts;
+  gfc_ref **ref;
+
+  if ((e->expr_type != EXPR_VARIABLE
+       && e->expr_type != EXPR_FUNCTION)
+      || (e->expr_type == EXPR_FUNCTION
+         && e->value.function.isym != NULL))
+    return;
+
+  if (e->expr_type == EXPR_VARIABLE)
+    ts = &e->symtree->n.sym->ts;
+  else
+    {
+      gfc_symbol *func;
+
+      gcc_assert (e->expr_type == EXPR_FUNCTION);
+      if (e->value.function.esym != NULL)
+       func = e->value.function.esym;
+      else
+       func = e->symtree->n.sym;
+
+      if (func->result != NULL)
+       ts = &func->result->ts;
+      else
+       ts = &func->ts;
+    }
+
+  for (ref = &e->ref; *ref != NULL; ref = &(*ref)->next)
+    {
+      if (class_data_ref_missing (ts, *ref, ref == &e->ref))
+       insert_component_ref (ts, ref, "_data");
+
+      if ((*ref)->type == REF_COMPONENT)
+       ts = &(*ref)->u.c.component->ts;
+    }
+}
+
+
 /* Insert a reference to the component of the given name.
-   Only to be used with CLASS containers.  */
+   Only to be used with CLASS containers and vtables.  */
 
 void
 gfc_add_component_ref (gfc_expr *e, const char *name)
@@ -62,12 +203,19 @@ gfc_add_component_ref (gfc_expr *e, const char *name)
   while (*tail != NULL)
     {
       if ((*tail)->type == REF_COMPONENT)
-       derived = (*tail)->u.c.component->ts.u.derived;
+       {
+         if (strcmp ((*tail)->u.c.component->name, "_data") == 0
+               && (*tail)->next
+               && (*tail)->next->type == REF_ARRAY
+               && (*tail)->next->next == NULL)
+           return;
+         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)
+  if (*tail != NULL && strcmp (name, "_data") == 0)
     next = *tail;
   (*tail) = gfc_get_ref();
   (*tail)->next = next;
@@ -80,8 +228,158 @@ gfc_add_component_ref (gfc_expr *e, const char *name)
 }
 
 
+/* This is used to add both the _data component reference and an array
+   reference to class expressions.  Used in translation of intrinsic
+   array inquiry functions.  */
+
+void
+gfc_add_class_array_ref (gfc_expr *e)
+{
+  int rank =  CLASS_DATA (e)->as->rank;
+  gfc_array_spec *as = CLASS_DATA (e)->as;
+  gfc_ref *ref = NULL;
+  gfc_add_component_ref (e, "_data");
+  e->rank = rank;
+  for (ref = e->ref; ref; ref = ref->next)
+    if (!ref->next)
+      break;
+  if (ref->type != REF_ARRAY)
+    {
+      ref->next = gfc_get_ref ();
+      ref = ref->next;
+      ref->type = REF_ARRAY;
+      ref->u.ar.type = AR_FULL;
+      ref->u.ar.as = as;         
+    }
+}
+
+
+/* Unfortunately, class array expressions can appear in various conditions;
+   with and without both _data component and an arrayspec.  This function
+   deals with that variability.  The previous reference to 'ref' is to a
+   class array.  */
+
+static bool
+class_array_ref_detected (gfc_ref *ref, bool *full_array)
+{
+  bool no_data = false;
+  bool with_data = false;
+
+  /* An array reference with no _data component.  */
+  if (ref && ref->type == REF_ARRAY
+       && !ref->next
+       && ref->u.ar.type != AR_ELEMENT)
+    {
+      if (full_array)
+        *full_array = ref->u.ar.type == AR_FULL;
+      no_data = true;
+    }
+
+  /* Cover cases where _data appears, with or without an array ref.  */
+  if (ref && ref->type == REF_COMPONENT
+       && strcmp (ref->u.c.component->name, "_data") == 0)
+    {
+      if (!ref->next)
+       {
+         with_data = true;
+         if (full_array)
+           *full_array = true;
+       }
+      else if (ref->next && ref->next->type == REF_ARRAY
+           && !ref->next->next
+           && ref->type == REF_COMPONENT
+           && ref->next->type == REF_ARRAY
+           && ref->next->u.ar.type != AR_ELEMENT)
+       {
+         with_data = true;
+         if (full_array)
+           *full_array = ref->next->u.ar.type == AR_FULL;
+       }
+    }
+
+  return no_data || with_data;
+}
+
+
+/* Returns true if the expression contains a reference to a class
+   array.  Notice that class array elements return false.  */
+
+bool
+gfc_is_class_array_ref (gfc_expr *e, bool *full_array)
+{
+  gfc_ref *ref;
+
+  if (!e->rank)
+    return false;
+
+  if (full_array)
+    *full_array= false;
+
+  /* Is this a class array object? ie. Is the symbol of type class?  */
+  if (e->symtree
+       && e->symtree->n.sym->ts.type == BT_CLASS
+       && CLASS_DATA (e->symtree->n.sym)
+       && CLASS_DATA (e->symtree->n.sym)->attr.dimension
+       && class_array_ref_detected (e->ref, full_array))
+    return true;
+
+  /* Or is this a class array component reference?  */
+  for (ref = e->ref; ref; ref = ref->next)
+    {
+      if (ref->type == REF_COMPONENT
+           && ref->u.c.component->ts.type == BT_CLASS
+           && CLASS_DATA (ref->u.c.component)->attr.dimension
+           && class_array_ref_detected (ref->next, full_array))
+       return true;
+    }
+
+  return false;
+}
+
+
+/* Returns true if the expression is a reference to a class
+   scalar.  This function is necessary because such expressions
+   can be dressed with a reference to the _data component and so
+   have a type other than BT_CLASS.  */
+
+bool
+gfc_is_class_scalar_expr (gfc_expr *e)
+{
+  gfc_ref *ref;
+
+  if (e->rank)
+    return false;
+
+  /* Is this a class object?  */
+  if (e->symtree
+       && e->symtree->n.sym->ts.type == BT_CLASS
+       && CLASS_DATA (e->symtree->n.sym)
+       && !CLASS_DATA (e->symtree->n.sym)->attr.dimension
+       && (e->ref == NULL
+           || (strcmp (e->ref->u.c.component->name, "_data") == 0
+               && e->ref->next == NULL)))
+    return true;
+
+  /* Or is the final reference BT_CLASS or _data?  */
+  for (ref = e->ref; ref; ref = ref->next)
+    {
+      if (ref->type == REF_COMPONENT
+           && ref->u.c.component->ts.type == BT_CLASS
+           && CLASS_DATA (ref->u.c.component)
+           && !CLASS_DATA (ref->u.c.component)->attr.dimension
+           && (ref->next == NULL
+               || (strcmp (ref->next->u.c.component->name, "_data") == 0
+                   && ref->next->next == NULL)))
+       return true;
+    }
+
+  return false;
+}
+
+
 /* Build a NULL initializer for CLASS pointers,
-   initializing the $data and $vptr components to zero.  */
+   initializing the _data component to NULL and
+   the _vptr component to the declared type.  */
 
 gfc_expr *
 gfc_class_null_initializer (gfc_typespec *ts)
@@ -96,9 +394,10 @@ gfc_class_null_initializer (gfc_typespec *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;
+      if (strcmp (comp->name, "_vptr") == 0)
+       ctor->expr = gfc_lval_expr_from_sym (gfc_find_derived_vtab (ts->u.derived));
+      else
+       ctor->expr = gfc_get_null_expr (NULL);
       gfc_constructor_append (&init->value.constructor, ctor);
     }
 
@@ -106,31 +405,112 @@ gfc_class_null_initializer (gfc_typespec *ts)
 }
 
 
+/* 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)
+{
+  char dt_name[GFC_MAX_SYMBOL_LEN+1];
+  sprintf (dt_name, "%s", derived->name);
+  dt_name[0] = TOUPPER (dt_name[0]);
+  if (derived->module)
+    sprintf (string, "%s_%s", derived->module, dt_name);
+  else if (derived->ns->proc_name)
+    sprintf (string, "%s_%s", derived->ns->proc_name->name, dt_name);
+  else
+    sprintf (string, "_%s", dt_name);
+}
+
+
+/* A relative of 'get_unique_type_string' which makes sure the generated
+   string will not be too long (replacing it by a hash string if needed).  */
+
+static void
+get_unique_hashed_string (char *string, gfc_symbol *derived)
+{
+  char tmp[2*GFC_MAX_SYMBOL_LEN+2];
+  get_unique_type_string (&tmp[0], derived);
+  /* If string is too long, use hash value in hex representation (allow for
+     extra decoration, cf. gfc_build_class_symbol & gfc_find_derived_vtab).
+     We need space to for 15 characters "__class_" + symbol name + "_%d_%da",
+     where %d is the (co)rank which can be up to n = 15.  */
+  if (strlen (tmp) > GFC_MAX_SYMBOL_LEN - 15)
+    {
+      int h = gfc_hash_value (derived);
+      sprintf (string, "%X", h);
+    }
+  else
+    strcpy (string, tmp);
+}
+
+
+/* Assign a hash value for a derived type. The algorithm is that of SDBM.  */
+
+unsigned int
+gfc_hash_value (gfc_symbol *sym)
+{
+  unsigned int hash = 0;
+  char c[2*(GFC_MAX_SYMBOL_LEN+1)];
+  int i, len;
+  
+  get_unique_type_string (&c[0], sym);
+  len = strlen (c);
+  
+  for (i = 0; i < len; i++)
+    hash = (hash << 6) + (hash << 16) - hash + c[i];
+
+  /* Return the hash but take the modulus for the sake of module read,
+     even though this slightly increases the chance of collision.  */
+  return (hash % 100000000);
+}
+
+
 /* 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.  */
+   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 + 5];
+  char name[GFC_MAX_SYMBOL_LEN+1], tname[GFC_MAX_SYMBOL_LEN+1];
   gfc_symbol *fclass;
   gfc_symbol *vtab;
   gfc_component *c;
 
+  if (as && *as && (*as)->type == AS_ASSUMED_SIZE)
+    {
+      gfc_error ("Assumed size polymorphic objects or components, such "
+                "as that at %C, have not yet been implemented");
+      return FAILURE;
+    }
+
+  if (attr->class_ok)
+    /* Class container has already been built.  */
+    return SUCCESS;
+
+  attr->class_ok = attr->dummy || attr->pointer || attr->allocatable
+                  || attr->select_type_temporary;
+  
+  if (!attr->class_ok)
+    /* We can not build the class container yet.  */
+    return SUCCESS;
+
   /* Determine the name of the encapsulating type.  */
-  if ((*as) && (*as)->rank && attr->allocatable)
-    sprintf (name, "class$%s_%d_a", ts->u.derived->name, (*as)->rank);
-  else if ((*as) && (*as)->rank)
-    sprintf (name, "class$%s_%d", ts->u.derived->name, (*as)->rank);
+  get_unique_hashed_string (tname, ts->u.derived);
+  if ((*as) && attr->allocatable)
+    sprintf (name, "__class_%s_%d_%da", tname, (*as)->rank, (*as)->corank);
+  else if ((*as))
+    sprintf (name, "__class_%s_%d_%d", tname, (*as)->rank, (*as)->corank);
   else if (attr->pointer)
-    sprintf (name, "class$%s_p", ts->u.derived->name);
+    sprintf (name, "__class_%s_p", tname);
   else if (attr->allocatable)
-    sprintf (name, "class$%s_a", ts->u.derived->name);
+    sprintf (name, "__class_%s_a", tname);
   else
-    sprintf (name, "class$%s", ts->u.derived->name);
+    sprintf (name, "__class_%s", tname);
 
   gfc_find_symbol (name, ts->u.derived->ns, 0, &fclass);
   if (fclass == NULL)
@@ -150,15 +530,16 @@ gfc_build_class_symbol (gfc_typespec *ts, symbol_attribute *attr,
          NULL, &gfc_current_locus) == FAILURE)
        return FAILURE;
 
-      /* Add component '$data'.  */
-      if (gfc_add_component (fclass, "$data", &c) == 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.pointer = attr->pointer || (attr->dummy && !attr->allocatable)
+                       || attr->select_type_temporary;
       c->attr.allocatable = attr->allocatable;
       c->attr.dimension = attr->dimension;
       c->attr.codimension = attr->codimension;
@@ -166,8 +547,8 @@ gfc_build_class_symbol (gfc_typespec *ts, symbol_attribute *attr,
       c->as = (*as);
       c->initializer = NULL;
 
-      /* Add component '$vptr'.  */
-      if (gfc_add_component (fclass, "$vptr", &c) == FAILURE)
+      /* Add component '_vptr'.  */
+      if (gfc_add_component (fclass, "_vptr", &c) == FAILURE)
        return FAILURE;
       c->ts.type = BT_DERIVED;
       if (delayed_vtab)
@@ -181,6 +562,8 @@ gfc_build_class_symbol (gfc_typespec *ts, symbol_attribute *attr,
       c->attr.access = ACCESS_PRIVATE;
       c->attr.pointer = 1;
     }
+  else if (!fclass->f2k_derived)
+    fclass->f2k_derived = gfc_get_namespace (NULL, 0);
 
   /* Since the extension field is 8 bit wide, we can only have
      up to 255 extension levels.  */
@@ -192,10 +575,11 @@ gfc_build_class_symbol (gfc_typespec *ts, symbol_attribute *attr,
     }
     
   fclass->attr.extension = ts->u.derived->attr.extension + 1;
+  fclass->attr.alloc_comp = ts->u.derived->attr.alloc_comp;
   fclass->attr.is_class = 1;
   ts->u.derived = fclass;
-  attr->allocatable = attr->pointer = attr->dimension = 0;
-  (*as) = NULL;  /* XXX */
+  attr->allocatable = attr->pointer = attr->dimension = attr->codimension = 0;
+  (*as) = NULL;
   return SUCCESS;
 }
 
@@ -207,6 +591,10 @@ static void
 add_proc_comp (gfc_symbol *vtype, const char *name, gfc_typebound_proc *tb)
 {
   gfc_component *c;
+
+  if (tb->non_overridable)
+    return;
+  
   c = gfc_find_component (vtype, name, true, true);
 
   if (c == NULL)
@@ -307,21 +695,15 @@ add_procs_to_declared_vtab (gfc_symbol *derived, gfc_symbol *vtype)
 }
 
 
-/* Find the symbol for a derived type's vtab.
-   A vtab has the following fields:
-    * $hash    a hash value used to identify the derived type
-    * $size    the size in bytes of the derived type
-    * $extends a pointer to the vtable of the parent derived type
-   After these follow procedure pointer components for the
-   specific type-bound procedures.  */
+/* 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;
-  char name[2 * GFC_MAX_SYMBOL_LEN + 8];
-  
+  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)
@@ -333,8 +715,17 @@ gfc_find_derived_vtab (gfc_symbol *derived)
     
   if (ns)
     {
-      sprintf (name, "vtab$%s", derived->name);
-      gfc_find_symbol (name, ns, 0, &vtab);
+      char name[GFC_MAX_SYMBOL_LEN+1], tname[GFC_MAX_SYMBOL_LEN+1];
+      
+      get_unique_hashed_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)
        {
@@ -344,11 +735,11 @@ gfc_find_derived_vtab (gfc_symbol *derived)
                              &gfc_current_locus) == FAILURE)
            goto cleanup;
          vtab->attr.target = 1;
-         vtab->attr.save = SAVE_EXPLICIT;
+         vtab->attr.save = SAVE_IMPLICIT;
          vtab->attr.vtab = 1;
          vtab->attr.access = ACCESS_PUBLIC;
          gfc_set_sym_referenced (vtab);
-         sprintf (name, "vtype$%s", derived->name);
+         sprintf (name, "__vtype_%s", tname);
          
          gfc_find_symbol (name, ns, 0, &vtype);
          if (vtype == NULL)
@@ -361,10 +752,11 @@ gfc_find_derived_vtab (gfc_symbol *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)
+             /* Add component '_hash'.  */
+             if (gfc_add_component (vtype, "_hash", &c) == FAILURE)
                goto cleanup;
              c->ts.type = BT_INTEGER;
              c->ts.kind = 4;
@@ -372,8 +764,8 @@ gfc_find_derived_vtab (gfc_symbol *derived)
              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)
+             /* Add component '_size'.  */
+             if (gfc_add_component (vtype, "_size", &c) == FAILURE)
                goto cleanup;
              c->ts.type = BT_INTEGER;
              c->ts.kind = 4;
@@ -385,8 +777,8 @@ gfc_find_derived_vtab (gfc_symbol *derived)
              c->initializer = gfc_get_int_expr (gfc_default_integer_kind,
                                                 NULL, 0);
 
-             /* Add component $extends.  */
-             if (gfc_add_component (vtype, "$extends", &c) == FAILURE)
+             /* Add component _extends.  */
+             if (gfc_add_component (vtype, "_extends", &c) == FAILURE)
                goto cleanup;
              c->attr.pointer = 1;
              c->attr.access = ACCESS_PRIVATE;
@@ -408,10 +800,104 @@ gfc_find_derived_vtab (gfc_symbol *derived)
                  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_IMPLICIT;
+                 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, &copy);
+                 sub_ns->proc_name = copy;
+                 copy->attr.flavor = FL_PROCEDURE;
+                 copy->attr.subroutine = 1;
+                 copy->attr.pure = 1;
+                 copy->attr.if_source = IFSRC_DECL;
+                 /* This is elemental so that arrays are automatically
+                    treated correctly by the scalarizer.  */
+                 copy->attr.elemental = 1;
+                 if (ns->proc_name->attr.flavor == FL_MODULE)
+                   copy->module = ns->proc_name->name;
+                 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;
+                 src->attr.intent = INTENT_IN;
+                 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;
+                 dst->attr.intent = INTENT_OUT;
+                 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_INIT_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);
-             vtype->attr.vtype = 1;
            }
 
+have_vtype:
          vtab->ts.u.derived = vtype;
          vtab->value = gfc_default_initializer (&vtab->ts);
        }
@@ -427,6 +913,14 @@ cleanup:
       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 ();
@@ -446,15 +940,17 @@ find_typebound_proc_uop (gfc_symbol* derived, gfc_try* t,
   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;
 
+  if (derived->f2k_derived)
+    /* Set correct symbol-root.  */
+    root = (uop ? derived->f2k_derived->tb_uop_root
+               : derived->f2k_derived->tb_sym_root);
+  else
+    return NULL;
+
   /* 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)