386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Mon, 13 Aug 1990 17:24:26 +0000 (09:24 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Mon, 13 Aug 1990 17:24:26 +0000 (09:24 -0800)
Work on file usr/src/usr.bin/g++/cc1plus/cplus-tree.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/g++/cc1plus/cplus-tree.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/g++/cc1plus/cplus-tree.c b/usr/src/usr.bin/g++/cc1plus/cplus-tree.c
new file mode 100644 (file)
index 0000000..9387d98
--- /dev/null
@@ -0,0 +1,1320 @@
+/* Language-depednent node constructors for parse phase of GNU compiler.
+   Copyright (C) 1987, 1988 Free Software Foundation, Inc.
+   Hacked by Michael Tiemann (tiemann@mcc.com)
+
+This file is part of GNU CC.
+
+GNU CC 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 1, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "config.h"
+#include <stdio.h>
+#include "obstack.h"
+#include "tree.h"
+#include "cplus-tree.h"
+#include "flags.h"
+#include "assert.h"
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define CEIL(x,y) (((x) + (y) - 1) / (y))
+
+/* Return nonzero if REF is an lvalue valid for this language.
+   Lvalues can be assigned, unless they have TREE_READONLY.
+   Lvalues can have their address taken, unless they have TREE_REGDECL.  */
+
+int
+lvalue_p (ref)
+     tree ref;
+{
+  register enum tree_code code = TREE_CODE (ref);
+
+  if (language_lvalue_valid (ref))
+    switch (code)
+      {
+      case COMPONENT_REF:
+       return lvalue_p (TREE_OPERAND (ref, 0));
+
+      case STRING_CST:
+       return 1;
+
+      case VAR_DECL:
+       if (TREE_READONLY (ref) && ! TREE_STATIC (ref)
+           && DECL_LANG_SPECIFIC (ref)
+           && DECL_IN_AGGR_P (ref))
+         return 0;
+      case INDIRECT_REF:
+      case ARRAY_REF:
+      case PARM_DECL:
+      case RESULT_DECL:
+      case ERROR_MARK:
+       if (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
+           && TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE)
+         return 1;
+       break;
+
+      case NEW_EXPR:
+       return 1;
+
+      case CALL_EXPR:
+       if (TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE
+           /* unary_complex_lvalue knows how to deal with this case.  */
+           || TREE_ADDRESSABLE (TREE_TYPE (ref)))
+         return 1;
+       break;
+
+       /* A currently unresolved scope ref.  */
+      case SCOPE_REF:
+       abort ();
+      case OFFSET_REF:
+       if (TREE_CODE (TREE_OPERAND (ref, 1)) == FUNCTION_DECL)
+         return 1;
+       if (TREE_CODE (TREE_OPERAND (ref, 1)) == VAR_DECL)
+         if (TREE_READONLY (ref) && ! TREE_STATIC (ref)
+             && DECL_LANG_SPECIFIC (ref)
+             && DECL_IN_AGGR_P (ref))
+           return 0;
+         else
+           return 1;
+       break;
+      }
+  return 0;
+}
+
+/* Return nonzero if REF is an lvalue valid for this language;
+   otherwise, print an error message and return zero.  */
+
+int
+lvalue_or_else (ref, string)
+     tree ref;
+     char *string;
+{
+  int win = lvalue_p (ref);
+  if (! win)
+    error ("invalid lvalue in %s", string);
+  return win;
+}
+
+/* INIT is a CALL_EXPR which needs info about its target.
+   TYPE is the type that this initialization should appear to have.
+
+   Build an encapsultation of the initialization to perfom
+   and return it so that it can be processed by language-independent
+   and language-specific expression expanders.  */
+tree
+build_cplus_new (type, init)
+     tree type;
+     tree init;
+{
+  tree slot = build (VAR_DECL, type);
+  tree rval = build (CPLUS_NEW_EXPR, type,
+                    TREE_OPERAND (init, 0), TREE_OPERAND (init, 1), slot);
+  TREE_ADDRESSABLE (rval) = 1;
+  rval = build (NEW_EXPR, type, slot, rval, 0);
+  TREE_ADDRESSABLE (rval) = 1;
+  return rval;
+}
+\f
+extern struct obstack *current_obstack;
+extern struct obstack permanent_obstack, class_obstack;
+extern struct obstack *saveable_obstack;
+
+/* Return a type like TYPE except that its CLASSTYPE_OFFSET
+   is OFFSET.
+
+   Such variant types already made are recorded so that duplicates
+   are not made.
+
+   A variant types should never be used as the type of an expression.
+   Use TYPE_MAIN_VARIANT to find the main variant.  */
+
+tree
+build_classtype_variant (type, offset, virtualp)
+     tree type;
+     tree offset;
+     int virtualp;
+{
+  register tree t, m = CLASSTYPE_MAIN_VARIANT (type);
+  register struct obstack *ambient_obstack = current_obstack;
+  register struct obstack *ambient_saveable_obstack = saveable_obstack;
+  register int lo = TREE_INT_CST_LOW (offset);
+  register int hi = TREE_INT_CST_HIGH (offset);
+  /* First search the chain variants for one that is what we want.  */
+
+  if (hi == 0 && lo == 0)
+    offset = integer_zero_node;
+
+  for (t = m; t; t = CLASSTYPE_NEXT_VARIANT (t))
+    if (virtualp == TREE_VIA_VIRTUAL (t)
+       && lo == TREE_INT_CST_LOW (CLASSTYPE_OFFSET (t))
+       && hi == TREE_INT_CST_HIGH (CLASSTYPE_OFFSET (t)))
+      return t;
+
+  /* We need a new one.  */
+  if (TREE_PERMANENT (type))
+    saveable_obstack = &permanent_obstack;
+  current_obstack = saveable_obstack;
+
+  t = copy_node (type);
+  copy_type_lang_specific (t);
+  CLASSTYPE_AS_LIST (t) = build_tree_list (NULL_TREE, t);
+
+  TYPE_POINTER_TO (t) = 0;
+  TYPE_REFERENCE_TO (t) = 0;
+  CLASSTYPE_OFFSET (t) = offset;
+  TREE_VIA_VIRTUAL (t) = virtualp;
+
+  /* Always promise to have TYPE_POINTER_TO filled in.  */
+  build_pointer_type (t);
+
+  /* Add this type to the chain of variants of TYPE.  */
+  CLASSTYPE_NEXT_VARIANT (t) = CLASSTYPE_NEXT_VARIANT (m);
+  CLASSTYPE_NEXT_VARIANT (m) = t;
+
+  current_obstack = ambient_obstack;
+  saveable_obstack = ambient_saveable_obstack;
+  return t;
+}
+
+/* Here is how primitive or already-canonicalized types' hash
+   codes are made.  MUST BE CONSISTENT WITH tree.c !!! */
+#define TYPE_HASH(TYPE) TREE_UID (TYPE)
+
+/* Construct, lay out and return the type of methods belonging to class
+   BASETYPE and whose arguments and values are described by TYPE.
+   If that type exists already, reuse it.
+   TYPE must be a FUNCTION_TYPE node.  */
+
+tree
+build_cplus_method_type (basetype, rettype, argtypes)
+     tree basetype, rettype, argtypes;
+{
+  register tree t;
+  tree ptype = build_pointer_type (basetype);
+  int hashcode;
+
+  /* Make a node of the sort we want.  */
+  t = make_node (METHOD_TYPE);
+
+  TYPE_METHOD_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);
+  TREE_TYPE (t) = rettype;
+  ptype = build_type_variant (ptype, !flag_this_is_variable, 0);
+
+  /* The actual arglist for this function includes a "hidden" argument
+     which is "this".  Put it into the list of argument types.  */
+
+  TYPE_ARG_TYPES (t) = tree_cons (NULL, ptype, argtypes);
+
+  /* If we already have such a type, use the old one and free this one.
+     Note that it also frees up the above cons cell if found.  */
+  hashcode = TYPE_HASH (basetype) + TYPE_HASH (rettype) + type_hash_list (argtypes);
+  t = type_hash_canon (hashcode, t);
+
+  if (TYPE_SIZE (t) == 0)
+    layout_type (t);
+
+  return t;
+}
+
+tree
+build_cplus_array_type (elt_type, index_type)
+     tree elt_type;
+     tree index_type;
+{
+  register struct obstack *ambient_obstack = current_obstack;
+  register struct obstack *ambient_saveable_obstack = saveable_obstack;
+  tree t;
+
+  /* We need a new one.  If ELT_TYPE is permanent, make this permanent too.  */
+  if (TREE_PERMANENT (elt_type))
+    {
+      current_obstack = &permanent_obstack;
+      saveable_obstack = &permanent_obstack;
+    }
+
+  t = build_array_type (elt_type, index_type);
+
+  /* Push these needs up so that initialization takes place
+     more easily.  */
+  TYPE_NEEDS_CONSTRUCTING (t) = TYPE_NEEDS_CONSTRUCTING (TYPE_MAIN_VARIANT (elt_type));
+  TYPE_NEEDS_DESTRUCTOR (t) = TYPE_NEEDS_DESTRUCTOR (TYPE_MAIN_VARIANT (elt_type));
+  current_obstack = ambient_obstack;
+  saveable_obstack = ambient_saveable_obstack;
+  return t;
+}
+
+/* This is temporary until later.  */
+/* Construct, lay out and return the type of objects which are of type TYPE
+   as members of type BASETYPE.  If that type exists already, reuse it.  */
+tree
+build_member_type (basetype, type)
+     tree basetype, type;
+{
+  register tree t;
+  int hashcode;
+
+  assert (TREE_CODE (type) != FUNCTION_TYPE);
+
+  /* Make a node of the sort we want.  */
+  t = make_node (OFFSET_TYPE);
+  TYPE_OFFSET_BASETYPE (t) = basetype;
+  TREE_TYPE (t) = type;
+  hashcode = TREE_UID (basetype) + TREE_UID (type);
+  t = type_hash_canon (hashcode, t);
+
+  return t;
+}
+\f
+/* Compute the actual offsets that our virtual base classes
+   will have *for this type*.  This must be performed after
+   the fields are laid out, since virtual baseclasses must
+   lay down at the end of the record.
+
+   Returns the maximum number of virtual functions any of the virtual
+   baseclasses provide.  */
+int
+layout_vbasetypes (rec, max)
+     tree rec;
+     int max;
+{
+  /* Get all the virtual base types that this type uses.
+     The TREE_VALUE slot holds the virtual baseclass type.  */
+  tree vbase_types = get_vbase_types (rec);
+
+#ifdef STRUCTURE_SIZE_BOUNDARY
+  int record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
+#else
+  int record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
+#endif
+
+  /* Record size so far is CONST_SIZE + VAR_SIZE * SIZE_UNIT bits,
+     where CONST_SIZE is an integer
+     and VAR_SIZE is a tree expression.
+     If VAR_SIZE is null, the size is just CONST_SIZE.
+     Naturally we try to avoid using VAR_SIZE.  */
+  register int const_size = 0;
+  register tree var_size = 0;
+  register int size_unit = BITS_PER_UNIT;
+  int nonvirtual_const_size;
+  tree nonvirtual_var_size;
+
+  CLASSTYPE_VBASECLASSES (rec) = vbase_types;
+
+  if (TREE_CODE (TYPE_SIZE (rec)) == INTEGER_CST)
+    const_size = TREE_INT_CST_LOW (TYPE_SIZE (rec)) * TYPE_SIZE_UNIT (rec);
+  else
+    {
+      var_size = TYPE_SIZE (rec);
+      size_unit = record_align;
+    }
+
+  nonvirtual_const_size = const_size;
+  nonvirtual_var_size = var_size;
+
+  while (vbase_types)
+    {
+      int inc;
+      tree basetype = ASSOC_TYPE (vbase_types);
+      tree offset;
+
+      if (const_size == 0)
+       offset = integer_zero_node;
+      else
+       offset = convert_units (build_int (const_size), 1, BITS_PER_UNIT);
+
+      if (CLASSTYPE_VSIZE (basetype) > max)
+       max = CLASSTYPE_VSIZE (basetype);
+      ASSOC_OFFSET (vbase_types) = offset;
+
+      inc = MAX (record_align,
+                (TREE_INT_CST_LOW (TYPE_SIZE (basetype))
+                 - TREE_INT_CST_LOW (CLASSTYPE_VBASE_SIZE (basetype)))
+                * TYPE_SIZE_UNIT (basetype));
+
+      const_size += inc;
+      vbase_types = TREE_CHAIN (vbase_types);
+    }
+
+  if (const_size - nonvirtual_const_size)
+    {
+      CLASSTYPE_VBASE_SIZE (rec) = convert_units (build_int (const_size - nonvirtual_const_size),
+                                                 1, BITS_PER_UNIT);
+      TYPE_SIZE (rec) = convert_units (build_int (const_size), 1, BITS_PER_UNIT);
+    }
+  else
+    CLASSTYPE_VBASE_SIZE (rec) = integer_zero_node;
+  return max;
+}
+
+#if 0
+/* This function should never be needed.  */
+void
+fixup_vbase_offsets (type)
+     tree type;
+{
+  tree virtuals = TREE_CHAIN (CLASS_ASSOC_VIRTUALS (type));
+
+  while (virtuals)
+    {
+      tree pfn = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals));
+      tree decl = TREE_OPERAND (pfn, 0);
+      tree vcontext = get_base_type (DECL_VCONTEXT (decl), DECL_CONTEXT (decl), 0);
+      if (vcontext != NULL_TREE && TREE_VIA_VIRTUAL (vcontext))
+       {
+         tree offset;
+         tree vbase_offset_info;
+         tree parent_type;
+
+         if (DECL_CONTEXT (decl) == TYPE_MAIN_VARIANT (type))
+           parent_type = type;
+         else
+           {
+             parent_type = get_base_type (DECL_CONTEXT (decl), type, 0);
+             if (parent_type == 0)
+               parent_type = type;
+           }
+         vbase_offset_info = value_member (vcontext,
+                                           CLASSTYPE_VBASECLASSES (parent_type));
+         offset = genop (MINUS_EXPR, CLASSTYPE_OFFSET (parent_type),
+                         TREE_PURPOSE (vbase_offset_info));
+         TREE_VALUE (virtuals) = build_vtable_entry (offset, pfn);
+       }
+      virtuals = TREE_CHAIN (virtuals);
+    }
+}
+#endif
+
+/* Lay out the base types of a record type, REC.
+   Tentatively set the size and alignment of REC
+   according to the base types alone.
+
+   Returns list of virtual base classes in a FIELD_DECL chain.  */
+tree
+layout_basetypes (rec)
+     tree rec;
+{
+  /* Chain to hold all the new FIELD_DECLs which point at virtual
+     base classes.  */
+  tree vbase_decls = NULL_TREE;
+
+#ifdef STRUCTURE_SIZE_BOUNDARY
+  int record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
+#else
+  int record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
+#endif
+
+  /* Record size so far is CONST_SIZE + VAR_SIZE * SIZE_UNIT bits,
+     where CONST_SIZE is an integer
+     and VAR_SIZE is a tree expression.
+     If VAR_SIZE is null, the size is just CONST_SIZE.
+     Naturally we try to avoid using VAR_SIZE.  */
+  register int const_size = 0;
+  register tree var_size = 0;
+  register int size_unit = BITS_PER_UNIT;
+  int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (rec);
+
+  /* Handle basetypes almost like fields, but record their
+     offsets differently.  */
+
+  for (i = 1; i <= n_baseclasses; i++)
+    {
+      int inc, desired_align;
+      register tree basetype = CLASSTYPE_BASECLASS (rec, i);
+      tree decl;
+
+      if (TYPE_SIZE (basetype) == 0)
+       {
+         error_with_aggr_type (basetype, "base class `%s' has incomplete type");
+         SET_CLASSTYPE_VIAS (rec, i, 1, 0);
+         continue;
+       }
+
+      /* All basetypes are recorded in the association list of the
+        derived type.  */
+
+      if (CLASSTYPE_VIA_VIRTUAL (rec, i))
+       {
+         int j;
+         char *name = (char *)alloca (TYPE_NAME_LENGTH (basetype)
+                                      + sizeof (VBASE_NAME) + 1);
+         sprintf (name, VBASE_NAME_FORMAT, TYPE_NAME_STRING (basetype));
+
+         /* The offset for a virtual base class is only
+            used in computing virtual function tables and
+            for initializing virtual base pointers.  The assoc
+            for this base type is built once `get_vbase_types'
+            is called.  */
+         CLASSTYPE_BASECLASS (rec, i) = basetype
+           = build_classtype_variant (basetype, integer_zero_node, 1);
+
+         /* If this basetype can come from another vbase pointer
+            without an additional indirection, we will share
+            that pointer.  If an indirection is involved, we
+            make our own pointer.  */
+         for (j = 1; j <= n_baseclasses; j++)
+           if (! CLASSTYPE_VIA_VIRTUAL (rec, j)
+               && TYPE_USES_VIRTUAL_BASECLASSES (CLASSTYPE_BASECLASS (rec, j))
+               && value_member (TYPE_MAIN_VARIANT (basetype),
+                                CLASSTYPE_VBASECLASSES (CLASSTYPE_BASECLASS (rec, j))))
+             {
+               goto got_it;
+             }
+
+         decl = build_lang_decl (FIELD_DECL, get_identifier (name),
+                                 build_pointer_type (basetype));
+         DECL_FIELD_CONTEXT (decl) = rec;
+         SET_DECL_FCONTEXT (decl, TYPE_MAIN_VARIANT (basetype));
+         DECL_VBASE_P (decl) = 1;
+         TREE_CHAIN (decl) = vbase_decls;
+         vbase_decls = decl;
+
+       got_it:
+         /* The space this decl occupies has already been accounted for.  */
+         continue;
+       }
+      else
+       {
+         tree class_offset;
+         tree assoc;
+
+         if (const_size == 0)
+           class_offset = integer_zero_node;
+         else
+           {
+             /* Give each base type the alignment it wants.  */
+             const_size = CEIL (const_size, TYPE_ALIGN (basetype))
+               * TYPE_ALIGN (basetype);
+             class_offset = convert_units (build_int (const_size), 1, BITS_PER_UNIT);
+             CLASSTYPE_BASECLASS (rec, i) = basetype
+               = build_classtype_variant (basetype, class_offset, 0);
+           }
+
+         if (CLASSTYPE_VSIZE (basetype))
+           assoc = make_assoc (class_offset, basetype,
+                               CLASS_ASSOC_VTABLE (basetype),
+                               CLASS_ASSOC_VIRTUALS (basetype),
+                               CLASSTYPE_ASSOC (rec));
+         else
+           assoc = make_assoc (class_offset, basetype, 0, 0,
+                               CLASSTYPE_ASSOC (rec));
+         CLASSTYPE_ASSOC (rec) = assoc;
+         if (const_size != 0)
+           {
+             TYPE_NAME (basetype) = copy_node (TYPE_NAME (basetype));
+             TREE_TYPE (TYPE_NAME (basetype)) = basetype;
+             DECL_OFFSET (TYPE_NAME (basetype)) = const_size;
+           }
+       }
+
+      /* Add only the amount of storage not present in
+        the virtual baseclasses.  */
+      inc = MAX (record_align,
+                (TREE_INT_CST_LOW (TYPE_SIZE (basetype))
+                 - TREE_INT_CST_LOW (CLASSTYPE_VBASE_SIZE (basetype)))
+                * TYPE_SIZE_UNIT (basetype));
+
+      /* Record must have at least as much alignment as any field.  */
+      desired_align = TYPE_ALIGN (basetype);
+      record_align = MAX (record_align, desired_align);
+
+      const_size += inc;
+    }
+
+  if (const_size)
+    CLASSTYPE_SIZE (rec) = build_int_2 (const_size, 0);
+  else
+    CLASSTYPE_SIZE (rec) = integer_zero_node;
+  CLASSTYPE_ALIGN (rec) = record_align;
+
+  return vbase_decls;
+}
+\f
+/* Hashing of lists so that we don't make duplicates.
+   The entry point is `list_hash_canon'.  */
+
+/* Each hash table slot is a bucket containing a chain
+   of these structures.  */
+
+struct list_hash
+{
+  struct list_hash *next;      /* Next structure in the bucket.  */
+  int hashcode;                        /* Hash code of this list.  */
+  tree list;                   /* The list recorded here.  */
+};
+
+/* Now here is the hash table.  When recording a list, it is added
+   to the slot whose index is the hash code mod the table size.
+   Note that the hash table is used for several kinds of lists.
+   While all these live in the same table, they are completely independent,
+   and the hash code is computed differently for each of these.  */
+
+#define TYPE_HASH_SIZE 59
+struct list_hash *list_hash_table[TYPE_HASH_SIZE];
+
+/* Here is how primitive or already-canonicalized lists' hash
+   codes are made.  */
+#define TYPE_HASH(TYPE) TREE_UID (TYPE)
+
+/* Compute a hash code for a list (chain of TREE_LIST nodes
+   with goodies in the TREE_PURPOSE, TREE_VALUE, and bits of the
+   TREE_COMMON slots), by adding the hash codes of the individual entries.  */
+
+int
+list_hash (list)
+     tree list;
+{
+  register int hashcode = 0;
+
+  if (TREE_CHAIN (list))
+    hashcode = TYPE_HASH (TREE_CHAIN (list));
+  else
+    hashcode = 0;
+  if (TREE_VALUE (list))
+    hashcode += TYPE_HASH (TREE_VALUE (list));
+  else
+    hashcode += 1007;
+  if (TREE_PURPOSE (list))
+    hashcode += TYPE_HASH (TREE_PURPOSE (list));
+  else
+    hashcode += 1009;
+  return hashcode;
+}
+
+/* Look in the type hash table for a type isomorphic to TYPE.
+   If one is found, return it.  Otherwise return 0.  */
+
+tree
+list_hash_lookup (hashcode, list)
+     int hashcode;
+     tree list;
+{
+  register struct list_hash *h;
+  for (h = list_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
+    if (h->hashcode == hashcode
+       && TREE_VIA_VIRTUAL (h->list) == TREE_VIA_VIRTUAL (list)
+       && TREE_VIA_PUBLIC (h->list) == TREE_VIA_PUBLIC (list)
+       && TREE_PURPOSE (h->list) == TREE_PURPOSE (list)
+       && TREE_VALUE (h->list) == TREE_VALUE (list))
+      {
+       assert (TREE_TYPE (h->list) == TREE_TYPE (list));
+       assert (TREE_CHAIN (h->list) == TREE_CHAIN (list));
+       return h->list;
+      }
+  return 0;
+}
+
+/* Add an entry to the list-hash-table
+   for a list TYPE whose hash code is HASHCODE.  */
+
+void
+list_hash_add (hashcode, list)
+     int hashcode;
+     tree list;
+{
+  register struct list_hash *h;
+
+  h = (struct list_hash *) obstack_alloc (&class_obstack, sizeof (struct list_hash));
+  h->hashcode = hashcode;
+  h->list = list;
+  h->next = list_hash_table[hashcode % TYPE_HASH_SIZE];
+  list_hash_table[hashcode % TYPE_HASH_SIZE] = h;
+}
+
+/* Given TYPE, and HASHCODE its hash code, return the canonical
+   object for an identical list if one already exists.
+   Otherwise, return TYPE, and record it as the canonical object
+   if it is a permanent object.
+
+   To use this function, first create a list of the sort you want.
+   Then compute its hash code from the fields of the list that
+   make it different from other similar lists.
+   Then call this function and use the value.
+   This function frees the list you pass in if it is a duplicate.  */
+
+/* Set to 1 to debug without canonicalization.  Never set by program.  */
+int debug_no_list_hash = 0;
+
+tree
+list_hash_canon (hashcode, list)
+     int hashcode;
+     tree list;
+{
+  tree t1;
+
+  if (debug_no_list_hash)
+    return list;
+
+  t1 = list_hash_lookup (hashcode, list);
+  if (t1 != 0)
+    {
+      obstack_free (&class_obstack, list);
+      return t1;
+    }
+
+  /* If this is a new list, record it for later reuse.  */
+  list_hash_add (hashcode, list);
+
+  return list;
+}
+
+tree
+hash_tree_cons (via_public, via_virtual, purpose, value, chain)
+     int via_public, via_virtual;
+     tree purpose, value, chain;
+{
+  struct obstack *ambient_obstack = current_obstack;
+  tree t;
+  int hashcode;
+
+  current_obstack = &class_obstack;
+  t = tree_cons (purpose, value, chain);
+  TREE_VIA_PUBLIC (t) = via_public;
+  TREE_VIA_VIRTUAL (t) = via_virtual;
+  hashcode = list_hash (t);
+  t = list_hash_canon (hashcode, t);
+  current_obstack = ambient_obstack;
+  return t;
+}
+
+/* Constructor for hashed lists.  */
+tree
+hash_tree_chain (value, chain)
+     tree value, chain;
+{
+  struct obstack *ambient_obstack = current_obstack;
+  tree t;
+  int hashcode;
+
+  current_obstack = &class_obstack;
+  t = tree_cons (NULL_TREE, value, chain);
+  hashcode = list_hash (t);
+  t = list_hash_canon (hashcode, t);
+  current_obstack = ambient_obstack;
+  return t;
+}
+
+/* Similar, but used for concatenating two lists.  */
+tree
+hash_chainon (list1, list2)
+     tree list1, list2;
+{
+  if (list2 == 0)
+    return list1;
+  if (list1 == 0)
+    return list2;
+  if (TREE_CHAIN (list1) == NULL_TREE)
+    return hash_tree_chain (TREE_VALUE (list1), list2);
+  return hash_tree_chain (TREE_VALUE (list1),
+                         hash_chainon (TREE_CHAIN (list1), list2));
+}
+
+tree
+build_decl_list_1 (value)
+     tree value;
+{
+  tree list = NULL_TREE;
+
+  if (TREE_CODE (value) == IDENTIFIER_NODE)
+    {
+      list = IDENTIFIER_AS_LIST (value);
+      if (list != NULL_TREE
+         && (TREE_CODE (list) != TREE_LIST
+             || TREE_VALUE (list) != value))
+       list = NULL_TREE;
+      else if (TREE_TYPE (value) != NULL_TREE
+              && TREE_CODE (TREE_TYPE (TREE_TYPE (value))) == RECORD_TYPE)
+       {
+         tree type = TREE_TYPE (TREE_TYPE (value));
+         if (CLASSTYPE_AS_ID_LIST (type) == NULL_TREE)
+           CLASSTYPE_AS_ID_LIST (type) = perm_tree_cons (NULL_TREE, value, NULL_TREE);
+         list = CLASSTYPE_AS_ID_LIST (type);
+       }
+    }
+  else if (TREE_CODE (value) == RECORD_TYPE
+          && TYPE_LANG_SPECIFIC (value))
+    list = CLASSTYPE_AS_LIST (value);
+
+  if (list != NULL_TREE)
+    {
+      assert (TREE_CHAIN (list) == NULL_TREE);
+      return list;
+    }
+
+  return build_decl_list (NULL_TREE, value);
+}
+
+/* Look in the type hash table for a type isomorphic to
+   `build_tree_list (NULL_TREE, VALUE)'.
+   If one is found, return it.  Otherwise return 0.  */
+
+tree
+list_hash_lookup_or_cons (value)
+     tree value;
+{
+  register int hashcode = TYPE_HASH (value);
+  register struct list_hash *h;
+  struct obstack *ambient_obstack;
+  tree list = NULL_TREE;
+
+  if (TREE_CODE (value) == IDENTIFIER_NODE)
+    {
+      list = IDENTIFIER_AS_LIST (value);
+      if (list != NULL_TREE
+         && (TREE_CODE (list) != TREE_LIST
+             || TREE_VALUE (list) != value))
+       list = NULL_TREE;
+      else if (TREE_TYPE (value) != NULL_TREE
+              && TREE_CODE (TREE_TYPE (TREE_TYPE (value))) == RECORD_TYPE)
+       {
+         tree type = TREE_TYPE (TREE_TYPE (value));
+         if (CLASSTYPE_AS_ID_LIST (type) == NULL_TREE)
+           CLASSTYPE_AS_ID_LIST (type) = perm_tree_cons (NULL_TREE, value, NULL_TREE);
+         list = CLASSTYPE_AS_ID_LIST (type);
+       }
+    }
+  else if (TREE_CODE (value) == TYPE_DECL
+          && TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE
+          && TYPE_LANG_SPECIFIC (TREE_TYPE (value)))
+    list = CLASSTYPE_AS_ID_LIST (TREE_TYPE (value));
+  else if (TREE_CODE (value) == RECORD_TYPE
+          && TYPE_LANG_SPECIFIC (value))
+    list = CLASSTYPE_AS_LIST (value);
+
+  if (list != NULL_TREE)
+    {
+      assert (TREE_CHAIN (list) == NULL_TREE);
+      return list;
+    }
+
+  if (debug_no_list_hash)
+    return hash_tree_chain (value, NULL_TREE);
+
+  for (h = list_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
+    if (h->hashcode == hashcode
+       && TREE_VIA_VIRTUAL (h->list) == 0
+       && TREE_VIA_PUBLIC (h->list) == 0
+       && TREE_PURPOSE (h->list) == 0
+       && TREE_VALUE (h->list) == value)
+      {
+       assert (TREE_TYPE (h->list) == 0);
+       assert (TREE_CHAIN (h->list) == 0);
+       return h->list;
+      }
+
+  ambient_obstack = current_obstack;
+  current_obstack = &class_obstack;
+  list = build_tree_list (NULL_TREE, value);
+  list_hash_add (hashcode, list);
+  current_obstack = ambient_obstack;
+  return list;
+}
+\f
+/* Build an association between TYPE and some parameters:
+
+   OFFSET is the offset added to `this' to convert it to a pointer
+   of type `TYPE *'
+
+   VTABLE is the virtual function table with which to initialize
+   sub-objects of type TYPE.
+
+   VIRTUALS are the virtual functions sitting in VTABLE.
+
+   CHAIN are more associations we must retain.  */
+
+tree
+make_assoc (offset, type, vtable, virtuals, chain)
+     tree offset, type;
+     tree vtable, virtuals;
+     tree chain;
+{
+  tree assoc = make_tree_vec (4);
+
+  TREE_TYPE (assoc) = type;
+  TREE_CHAIN (assoc) = chain;
+  if (chain)
+    TREE_USED (assoc) = TREE_USED (chain);
+
+  /* n.b.: TREE_VEC_ELT (assoc, 0) <=> TREE_VALUE (assoc).  */
+  TREE_VEC_ELT (assoc, 0) = TYPE_MAIN_VARIANT (type);
+  TREE_VEC_ELT (assoc, 1) = offset;
+  TREE_VEC_ELT (assoc, 2) = vtable;
+  TREE_VEC_ELT (assoc, 3) = virtuals;
+  return assoc;
+}
+
+tree
+copy_assoc (list)
+     tree list;
+{
+  tree assoc = copy_list (list);
+  tree rval = assoc;
+  while (assoc)
+    {
+      TREE_USED (assoc) = 0;
+      assoc = TREE_CHAIN (assoc);
+    }
+  return rval;
+}
+
+tree
+assoc_value (elem, type)
+     tree elem;
+     tree type;
+{
+  tree assoc = CLASSTYPE_ASSOC (type);
+  tree rval = NULL_TREE;
+
+  /* Dispose quickly of degenerate case.  */
+  if (elem == type)
+    return assoc;
+
+  while (assoc)
+    {
+      if (elem == ASSOC_VALUE (assoc))
+       /* If we find it on the main spine, then
+          there can be no ambiguity.  */
+       return assoc;
+
+      if (ASSOC_VALUE (assoc) != type)
+       {
+         tree nval = assoc_value (elem, ASSOC_TYPE (assoc));
+
+         if (nval)
+           if (rval && ASSOC_TYPE (rval) != ASSOC_TYPE (nval))
+             /* If we find it underneath, we must make sure that
+                there are no two ways to do it.  */
+             compiler_error ("base class `%s' ambiguous in assoc_value",
+                             TYPE_NAME_STRING (elem));
+           else
+             rval = nval;
+       }
+      assoc = TREE_CHAIN (assoc);
+    }
+  return rval;
+}
+
+tree
+virtual_member (elem, list)
+     tree elem;
+     tree list;
+{
+  tree t;
+  tree rval, nval;
+
+  for (t = list; t; t = TREE_CHAIN (t))
+    if (elem == TREE_VALUE (t))
+      return t;
+  rval = 0;
+  for (t = list; t; t = TREE_CHAIN (t))
+    {
+      int i;
+      for (i = CLASSTYPE_N_BASECLASSES (TREE_TYPE (t)); i > 0; i--)
+       {
+         nval = assoc_value (elem, CLASSTYPE_BASECLASS (TREE_TYPE (t), i));
+         if (nval)
+           {
+             if (rval && TREE_TYPE (nval) != TREE_TYPE (rval))
+               abort ();
+             rval = nval;
+           }
+       }
+    }
+  return rval;
+}
+
+void
+debug_dump_assoc (elem)
+     tree elem;
+{
+  int i;
+  tree virtuals;
+
+  fprintf (stderr, "type \"%s\"; offset = %d\n",
+          IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (ASSOC_VALUE (elem)))),
+          TREE_INT_CST_LOW (ASSOC_OFFSET (elem)));
+  fprintf (stderr, "vtable type:\n");
+  dump_tree (stderr, ASSOC_TYPE (elem));
+  if (ASSOC_VTABLE (elem))
+    fprintf (stderr, "vtable decl \"%s\"\n", IDENTIFIER_POINTER (DECL_NAME (ASSOC_VTABLE (elem))));
+  else
+    fprintf (stderr, "no vtable decl yet\n");
+  fprintf (stderr, "virtuals:\n");
+  virtuals = ASSOC_VIRTUALS (elem);
+  if (virtuals != 0)
+    virtuals = TREE_CHAIN (virtuals);
+  i = 1;
+  while (virtuals)
+    {
+      tree fndecl = TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals)), 0);
+      fprintf (stderr, "%s [%d =? %d]\n",
+              IDENTIFIER_POINTER (DECL_NAME (fndecl)),
+              i, TREE_INT_CST_LOW (DECL_VINDEX (fndecl)));
+      virtuals = TREE_CHAIN (virtuals);
+      i += 1;
+    }
+}
+\f
+char *
+lang_printable_name (decl)
+     tree decl;
+{
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      || DECL_LANG_SPECIFIC (decl) == 0)
+    {
+      if (DECL_NAME (decl))
+       {
+         if (THIS_NAME_P (DECL_NAME (decl)))
+           return "this";
+         return IDENTIFIER_POINTER (DECL_NAME (decl));
+       }
+      return "((anonymous))";
+    }
+  if (DECL_PRINT_NAME (decl) == 0)
+    {
+      int print_ret_type_p
+       = (!DECL_CONSTRUCTOR_P (decl)
+          && !DESTRUCTOR_NAME_P (DECL_NAME (decl)));
+      int temp = allocation_temporary_p ();
+      char *buf = (char *)alloca (8192);
+      char *name = (char *)fndecl_as_string (buf, 0, decl, print_ret_type_p);
+      end_temporary_allocation ();
+      DECL_PRINT_NAME (decl) = oballoc (strlen (name) + 1);
+      strcpy (DECL_PRINT_NAME (decl), name);
+      if (temp)
+       resume_temporary_allocation ();
+    }
+  else if (DECL_NAME (decl) == 0)
+    DECL_PRINT_NAME (decl) = "((anonymous))";
+
+  return DECL_PRINT_NAME (decl);
+}
+
+/* Return truthvalue about whether debugger should
+   output full info about this type or not.
+
+   Current strategy is to permit types which define
+   no member functions to be output normally.  For
+   those which do define member functions, if no
+   member functions have yet been output, then don't
+   output the definition of the type.  If member functions
+   for the type are later seen, a full definition of the
+   type will eventually be output.  */
+int
+lang_output_debug_info (type)
+     tree type;
+{
+  extern tree pending_vtables;
+
+  if (! IS_AGGR_TYPE (type))
+    return 1;
+  if (TYPE_LANG_SPECIFIC (type) == 0)
+    return 1;
+  if (CLASSTYPE_METHOD_VEC (type) == 0)
+    return 1;
+
+  if (flag_minimal_debug)
+    {
+      /* Don't output full info about any type
+        which does not have its implementation defined here.  */
+      if (TYPE_VIRTUAL_P (type) && write_virtuals == 2)
+       return value_member (DECL_NAME (TYPE_NAME (type)), pending_vtables) != 0;
+      if (CLASSTYPE_INTERFACE_ONLY (type))
+       return 0;
+
+      return CLASSTYPE_ASM_WRITTEN (type);
+    }
+  else
+    /* Can't work until GDB is modified.  */
+    return 1;
+}
+\f
+/* Comparison function for sorting identifiers in RAISES lists.
+   Note that because IDENTIFIER_NODEs are unique, we can sort
+   them by address, saving an indirection.  */
+static int
+id_cmp (p1, p2)
+     int *p1, *p2;
+{
+  return *p1 - *p2;
+}
+
+/* Build the FUNCTION_TYPE or METHOD_TYPE which may raise exceptions
+   listed in RAISES.  */
+tree
+build_exception_variant (ctype, type, raises)
+     tree ctype, type;
+     tree raises;
+{
+  int i;
+  tree v = TYPE_MAIN_VARIANT (type);
+  tree t, t2, cname;
+  tree *a = (tree *)alloca ((list_length (raises)+1) * sizeof (tree));
+  int constp = TREE_READONLY (type);
+  int volatilep = TREE_VOLATILE (type);
+
+  if (raises && TREE_CHAIN (raises))
+    {
+      for (i = 0, t = raises; t; t = TREE_CHAIN (t), i++)
+       a[i] = t;
+      /* NULL terminator for list.  */
+      a[i] = NULL_TREE;
+      qsort (a, i, sizeof (tree), id_cmp);
+      while (i--)
+       TREE_CHAIN (a[i]) = a[i+1];
+      raises = a[0];
+    }
+  else if (raises)
+    /* do nothing.  */;
+  else
+    return build_type_variant (v, constp, volatilep);
+
+  if (ctype)
+    {
+      cname = TYPE_NAME (ctype);
+      if (TREE_CODE (cname) == TYPE_DECL)
+       cname = DECL_NAME (cname);
+    }
+  else
+    cname = NULL_TREE;
+
+  for (t = raises; t; t = TREE_CHAIN (t))
+    {
+      /* See that all the exceptions we are thinking about
+        raising have been declared.  */
+      tree this_cname = lookup_exception_cname (ctype, cname, t);
+      tree decl = lookup_exception_object (this_cname, TREE_VALUE (t), 1);
+
+      if (decl == NULL_TREE)
+       decl = lookup_exception_object (this_cname, TREE_VALUE (t), 0);
+      /* Place canonical exception decl into TREE_TYPE of RAISES list.  */
+      TREE_TYPE (t) = decl;
+    }
+
+  for (v = TYPE_NEXT_VARIANT (v); v; v = TYPE_NEXT_VARIANT (v))
+    {
+      if (TREE_READONLY (v) != constp
+         || TREE_VOLATILE (v) != volatilep)
+       continue;
+
+      t = raises;
+      t2 = TYPE_RAISES_EXCEPTIONS (v);
+      while (t && t2)
+       {
+         if (TREE_TYPE (t) == TREE_TYPE (t2))
+           {
+             t = TREE_CHAIN (t);
+             t2 = TREE_CHAIN (t2);
+           }
+         else break;
+       }
+      if (t || t2)
+       continue;
+      /* List of exceptions raised matches previously found list.
+
+         @@ Nice to free up storage used in consing up the
+        @@ list of exceptions raised.  */
+      return v;
+    }
+
+  /* Need to build a new variant.  */
+  v = copy_node (type);
+  TYPE_NEXT_VARIANT (v) = TYPE_NEXT_VARIANT (type);
+  TYPE_NEXT_VARIANT (type) = v;
+  if (raises && ! TREE_PERMANENT (raises))
+    {
+      int temporary = allocation_temporary_p ();
+      if (temporary)
+       end_temporary_allocation ();
+      raises = copy_list (raises);
+      if (temporary)
+       resume_temporary_allocation ();
+    }
+  TYPE_RAISES_EXCEPTIONS (v) = raises;
+  return v;
+}
+
+/* Subroutine of make_permanent_node.
+
+   Assuming T is a node build bottom-up, make it all exist on
+   permanent obstack, if it is not permanent already.  */
+static tree
+make_deep_copy (t)
+     tree t;
+{
+  enum tree_code code;
+
+  if (t == NULL_TREE || TREE_PERMANENT (t))
+    return t;
+
+  switch (code = TREE_CODE (t))
+    {
+    case ERROR_MARK:
+      return error_mark_node;
+
+    case VAR_DECL:
+    case PARM_DECL:
+    case FUNCTION_DECL:
+    case CONST_DECL:
+      break;
+
+    case TREE_LIST:
+      {
+       tree chain = TREE_CHAIN (t);
+       t = copy_node (t);
+       TREE_PURPOSE (t) = make_deep_copy (TREE_PURPOSE (t));
+       TREE_VALUE (t) = make_deep_copy (TREE_VALUE (t));
+       TREE_CHAIN (t) = make_deep_copy (chain);
+       return t;
+      }
+
+    case TREE_VEC:
+      {
+       int len = TREE_VEC_LENGTH (t);
+
+       t = copy_node (t);
+       while (len--)
+         TREE_VEC_ELT (t, len) = make_deep_copy (TREE_VEC_ELT (t, len));
+       return t;
+      }
+
+    case INTEGER_CST:
+    case REAL_CST:
+    case STRING_CST:
+      return copy_node (t);
+
+    case COND_EXPR:
+    case NEW_EXPR:
+      t = copy_node (t);
+      TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+      TREE_OPERAND (t, 1) = make_deep_copy (TREE_OPERAND (t, 1));
+      TREE_OPERAND (t, 2) = make_deep_copy (TREE_OPERAND (t, 2));
+      return t;
+
+    case SAVE_EXPR:
+      t = copy_node (t);
+      TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+      return t;
+
+    case MODIFY_EXPR:
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case TRUNC_DIV_EXPR:
+    case TRUNC_MOD_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+    case LSHIFT_EXPR:
+    case RSHIFT_EXPR:
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+    case BIT_AND_EXPR:
+    case BIT_ANDTC_EXPR:
+    case TRUTH_ANDIF_EXPR:
+    case TRUTH_ORIF_EXPR:
+    case LT_EXPR:
+    case LE_EXPR:
+    case GT_EXPR:
+    case GE_EXPR:
+    case EQ_EXPR:
+    case NE_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case CEIL_MOD_EXPR:
+    case FLOOR_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+    case COMPOUND_EXPR:
+    case PREDECREMENT_EXPR:
+    case PREINCREMENT_EXPR:
+    case POSTDECREMENT_EXPR:
+    case POSTINCREMENT_EXPR:
+    case CALL_EXPR:
+      t = copy_node (t);
+      TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+      TREE_OPERAND (t, 1) = make_deep_copy (TREE_OPERAND (t, 1));
+      return t;
+
+    case CONVERT_EXPR:
+    case ADDR_EXPR:
+    case INDIRECT_REF:
+    case NEGATE_EXPR:
+    case BIT_NOT_EXPR:
+    case TRUTH_NOT_EXPR:
+    case NOP_EXPR:
+    case COMPONENT_REF:
+      t = copy_node (t);
+      TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+      return t;
+
+      /*  This list is incomplete, but should suffice for now.
+         It is very important that `sorry' does not call
+         `report_error_function'.  That could cause an infinite loop.  */
+    default:
+      sorry ("initializer contains unrecognized tree code");
+      return error_mark_node;
+
+    }
+  abort ();
+}
+
+/* Assuming T is a node built bottom-up, make it all exist on
+   permanent obstack, if it is not permanent already.  */
+tree
+copy_to_permanent (t)
+     tree t;
+{
+  register struct obstack *ambient_obstack = current_obstack;
+  register struct obstack *ambient_saveable_obstack = saveable_obstack;
+
+  if (t == NULL_TREE || TREE_PERMANENT (t))
+    return t;
+
+  saveable_obstack = &permanent_obstack;
+  current_obstack = saveable_obstack;
+
+  t = make_deep_copy (t);
+
+  current_obstack = ambient_obstack;
+  saveable_obstack = ambient_saveable_obstack;
+
+  return t;
+}
+
+int
+lang_simple_cst_equal (t1, t2)
+     tree t1, t2;
+{
+  register enum tree_code code1, code2;
+  int cmp;
+
+  if (t1 == t2)
+    return 1;
+  if (t1 == 0 || t2 == 0)
+    return 0;
+
+  code1 = TREE_CODE (t1);
+  code2 = TREE_CODE (t2);
+
+  switch (code1)
+    {
+    case CPLUS_NEW_EXPR:
+      cmp = simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+      if (cmp <= 0)
+       return cmp;
+      return simple_cst_list_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1));
+
+    default:
+      return -1;
+    }
+}