diff gcc/fortran/module.c @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/fortran/module.c	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,7218 @@
+/* Handle modules, which amounts to loading and saving symbols and
+   their attendant structures.
+   Copyright (C) 2000-2017 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/>.  */
+
+/* The syntax of gfortran modules resembles that of lisp lists, i.e. a
+   sequence of atoms, which can be left or right parenthesis, names,
+   integers or strings.  Parenthesis are always matched which allows
+   us to skip over sections at high speed without having to know
+   anything about the internal structure of the lists.  A "name" is
+   usually a fortran 95 identifier, but can also start with '@' in
+   order to reference a hidden symbol.
+
+   The first line of a module is an informational message about what
+   created the module, the file it came from and when it was created.
+   The second line is a warning for people not to edit the module.
+   The rest of the module looks like:
+
+   ( ( <Interface info for UPLUS> )
+     ( <Interface info for UMINUS> )
+     ...
+   )
+   ( ( <name of operator interface> <module of op interface> <i/f1> ... )
+     ...
+   )
+   ( ( <name of generic interface> <module of generic interface> <i/f1> ... )
+     ...
+   )
+   ( ( <common name> <symbol> <saved flag>)
+     ...
+   )
+
+   ( equivalence list )
+
+   ( <Symbol Number (in no particular order)>
+     <True name of symbol>
+     <Module name of symbol>
+     ( <symbol information> )
+     ...
+   )
+   ( <Symtree name>
+     <Ambiguous flag>
+     <Symbol number>
+     ...
+   )
+
+   In general, symbols refer to other symbols by their symbol number,
+   which are zero based.  Symbols are written to the module in no
+   particular order.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "options.h"
+#include "tree.h"
+#include "gfortran.h"
+#include "stringpool.h"
+#include "arith.h"
+#include "match.h"
+#include "parse.h" /* FIXME */
+#include "constructor.h"
+#include "cpp.h"
+#include "scanner.h"
+#include <zlib.h>
+
+#define MODULE_EXTENSION ".mod"
+#define SUBMODULE_EXTENSION ".smod"
+
+/* Don't put any single quote (') in MOD_VERSION, if you want it to be
+   recognized.  */
+#define MOD_VERSION "15"
+
+
+/* Structure that describes a position within a module file.  */
+
+typedef struct
+{
+  int column, line;
+  long pos;
+}
+module_locus;
+
+/* Structure for list of symbols of intrinsic modules.  */
+typedef struct
+{
+  int id;
+  const char *name;
+  int value;
+  int standard;
+}
+intmod_sym;
+
+
+typedef enum
+{
+  P_UNKNOWN = 0, P_OTHER, P_NAMESPACE, P_COMPONENT, P_SYMBOL
+}
+pointer_t;
+
+/* The fixup structure lists pointers to pointers that have to
+   be updated when a pointer value becomes known.  */
+
+typedef struct fixup_t
+{
+  void **pointer;
+  struct fixup_t *next;
+}
+fixup_t;
+
+
+/* Structure for holding extra info needed for pointers being read.  */
+
+enum gfc_rsym_state
+{
+  UNUSED,
+  NEEDED,
+  USED
+};
+
+enum gfc_wsym_state
+{
+  UNREFERENCED = 0,
+  NEEDS_WRITE,
+  WRITTEN
+};
+
+typedef struct pointer_info
+{
+  BBT_HEADER (pointer_info);
+  int integer;
+  pointer_t type;
+
+  /* The first component of each member of the union is the pointer
+     being stored.  */
+
+  fixup_t *fixup;
+
+  union
+  {
+    void *pointer;	/* Member for doing pointer searches.  */
+
+    struct
+    {
+      gfc_symbol *sym;
+      char *true_name, *module, *binding_label;
+      fixup_t *stfixup;
+      gfc_symtree *symtree;
+      enum gfc_rsym_state state;
+      int ns, referenced, renamed;
+      module_locus where;
+    }
+    rsym;
+
+    struct
+    {
+      gfc_symbol *sym;
+      enum gfc_wsym_state state;
+    }
+    wsym;
+  }
+  u;
+
+}
+pointer_info;
+
+#define gfc_get_pointer_info() XCNEW (pointer_info)
+
+
+/* Local variables */
+
+/* The gzFile for the module we're reading or writing.  */
+static gzFile module_fp;
+
+
+/* The name of the module we're reading (USE'ing) or writing.  */
+static const char *module_name;
+/* The name of the .smod file that the submodule will write to.  */
+static const char *submodule_name;
+
+static gfc_use_list *module_list;
+
+/* If we're reading an intrinsic module, this is its ID.  */
+static intmod_id current_intmod;
+
+/* Content of module.  */
+static char* module_content;
+
+static long module_pos;
+static int module_line, module_column, only_flag;
+static int prev_module_line, prev_module_column;
+
+static enum
+{ IO_INPUT, IO_OUTPUT }
+iomode;
+
+static gfc_use_rename *gfc_rename_list;
+static pointer_info *pi_root;
+static int symbol_number;	/* Counter for assigning symbol numbers */
+
+/* Tells mio_expr_ref to make symbols for unused equivalence members.  */
+static bool in_load_equiv;
+
+
+
+/*****************************************************************/
+
+/* Pointer/integer conversion.  Pointers between structures are stored
+   as integers in the module file.  The next couple of subroutines
+   handle this translation for reading and writing.  */
+
+/* Recursively free the tree of pointer structures.  */
+
+static void
+free_pi_tree (pointer_info *p)
+{
+  if (p == NULL)
+    return;
+
+  if (p->fixup != NULL)
+    gfc_internal_error ("free_pi_tree(): Unresolved fixup");
+
+  free_pi_tree (p->left);
+  free_pi_tree (p->right);
+
+  if (iomode == IO_INPUT)
+    {
+      XDELETEVEC (p->u.rsym.true_name);
+      XDELETEVEC (p->u.rsym.module);
+      XDELETEVEC (p->u.rsym.binding_label);
+    }
+
+  free (p);
+}
+
+
+/* Compare pointers when searching by pointer.  Used when writing a
+   module.  */
+
+static int
+compare_pointers (void *_sn1, void *_sn2)
+{
+  pointer_info *sn1, *sn2;
+
+  sn1 = (pointer_info *) _sn1;
+  sn2 = (pointer_info *) _sn2;
+
+  if (sn1->u.pointer < sn2->u.pointer)
+    return -1;
+  if (sn1->u.pointer > sn2->u.pointer)
+    return 1;
+
+  return 0;
+}
+
+
+/* Compare integers when searching by integer.  Used when reading a
+   module.  */
+
+static int
+compare_integers (void *_sn1, void *_sn2)
+{
+  pointer_info *sn1, *sn2;
+
+  sn1 = (pointer_info *) _sn1;
+  sn2 = (pointer_info *) _sn2;
+
+  if (sn1->integer < sn2->integer)
+    return -1;
+  if (sn1->integer > sn2->integer)
+    return 1;
+
+  return 0;
+}
+
+
+/* Initialize the pointer_info tree.  */
+
+static void
+init_pi_tree (void)
+{
+  compare_fn compare;
+  pointer_info *p;
+
+  pi_root = NULL;
+  compare = (iomode == IO_INPUT) ? compare_integers : compare_pointers;
+
+  /* Pointer 0 is the NULL pointer.  */
+  p = gfc_get_pointer_info ();
+  p->u.pointer = NULL;
+  p->integer = 0;
+  p->type = P_OTHER;
+
+  gfc_insert_bbt (&pi_root, p, compare);
+
+  /* Pointer 1 is the current namespace.  */
+  p = gfc_get_pointer_info ();
+  p->u.pointer = gfc_current_ns;
+  p->integer = 1;
+  p->type = P_NAMESPACE;
+
+  gfc_insert_bbt (&pi_root, p, compare);
+
+  symbol_number = 2;
+}
+
+
+/* During module writing, call here with a pointer to something,
+   returning the pointer_info node.  */
+
+static pointer_info *
+find_pointer (void *gp)
+{
+  pointer_info *p;
+
+  p = pi_root;
+  while (p != NULL)
+    {
+      if (p->u.pointer == gp)
+	break;
+      p = (gp < p->u.pointer) ? p->left : p->right;
+    }
+
+  return p;
+}
+
+
+/* Given a pointer while writing, returns the pointer_info tree node,
+   creating it if it doesn't exist.  */
+
+static pointer_info *
+get_pointer (void *gp)
+{
+  pointer_info *p;
+
+  p = find_pointer (gp);
+  if (p != NULL)
+    return p;
+
+  /* Pointer doesn't have an integer.  Give it one.  */
+  p = gfc_get_pointer_info ();
+
+  p->u.pointer = gp;
+  p->integer = symbol_number++;
+
+  gfc_insert_bbt (&pi_root, p, compare_pointers);
+
+  return p;
+}
+
+
+/* Given an integer during reading, find it in the pointer_info tree,
+   creating the node if not found.  */
+
+static pointer_info *
+get_integer (int integer)
+{
+  pointer_info *p, t;
+  int c;
+
+  t.integer = integer;
+
+  p = pi_root;
+  while (p != NULL)
+    {
+      c = compare_integers (&t, p);
+      if (c == 0)
+	break;
+
+      p = (c < 0) ? p->left : p->right;
+    }
+
+  if (p != NULL)
+    return p;
+
+  p = gfc_get_pointer_info ();
+  p->integer = integer;
+  p->u.pointer = NULL;
+
+  gfc_insert_bbt (&pi_root, p, compare_integers);
+
+  return p;
+}
+
+
+/* Resolve any fixups using a known pointer.  */
+
+static void
+resolve_fixups (fixup_t *f, void *gp)
+{
+  fixup_t *next;
+
+  for (; f; f = next)
+    {
+      next = f->next;
+      *(f->pointer) = gp;
+      free (f);
+    }
+}
+
+
+/* Convert a string such that it starts with a lower-case character. Used
+   to convert the symtree name of a derived-type to the symbol name or to
+   the name of the associated generic function.  */
+
+const char *
+gfc_dt_lower_string (const char *name)
+{
+  if (name[0] != (char) TOLOWER ((unsigned char) name[0]))
+    return gfc_get_string ("%c%s", (char) TOLOWER ((unsigned char) name[0]),
+			   &name[1]);
+  return gfc_get_string ("%s", name);
+}
+
+
+/* Convert a string such that it starts with an upper-case character. Used to
+   return the symtree-name for a derived type; the symbol name itself and the
+   symtree/symbol name of the associated generic function start with a lower-
+   case character.  */
+
+const char *
+gfc_dt_upper_string (const char *name)
+{
+  if (name[0] != (char) TOUPPER ((unsigned char) name[0]))
+    return gfc_get_string ("%c%s", (char) TOUPPER ((unsigned char) name[0]),
+			   &name[1]);
+  return gfc_get_string ("%s", name);
+}
+
+/* Call here during module reading when we know what pointer to
+   associate with an integer.  Any fixups that exist are resolved at
+   this time.  */
+
+static void
+associate_integer_pointer (pointer_info *p, void *gp)
+{
+  if (p->u.pointer != NULL)
+    gfc_internal_error ("associate_integer_pointer(): Already associated");
+
+  p->u.pointer = gp;
+
+  resolve_fixups (p->fixup, gp);
+
+  p->fixup = NULL;
+}
+
+
+/* During module reading, given an integer and a pointer to a pointer,
+   either store the pointer from an already-known value or create a
+   fixup structure in order to store things later.  Returns zero if
+   the reference has been actually stored, or nonzero if the reference
+   must be fixed later (i.e., associate_integer_pointer must be called
+   sometime later.  Returns the pointer_info structure.  */
+
+static pointer_info *
+add_fixup (int integer, void *gp)
+{
+  pointer_info *p;
+  fixup_t *f;
+  char **cp;
+
+  p = get_integer (integer);
+
+  if (p->integer == 0 || p->u.pointer != NULL)
+    {
+      cp = (char **) gp;
+      *cp = (char *) p->u.pointer;
+    }
+  else
+    {
+      f = XCNEW (fixup_t);
+
+      f->next = p->fixup;
+      p->fixup = f;
+
+      f->pointer = (void **) gp;
+    }
+
+  return p;
+}
+
+
+/*****************************************************************/
+
+/* Parser related subroutines */
+
+/* Free the rename list left behind by a USE statement.  */
+
+static void
+free_rename (gfc_use_rename *list)
+{
+  gfc_use_rename *next;
+
+  for (; list; list = next)
+    {
+      next = list->next;
+      free (list);
+    }
+}
+
+
+/* Match a USE statement.  */
+
+match
+gfc_match_use (void)
+{
+  char name[GFC_MAX_SYMBOL_LEN + 1], module_nature[GFC_MAX_SYMBOL_LEN + 1];
+  gfc_use_rename *tail = NULL, *new_use;
+  interface_type type, type2;
+  gfc_intrinsic_op op;
+  match m;
+  gfc_use_list *use_list;
+
+  use_list = gfc_get_use_list ();
+
+  if (gfc_match (" , ") == MATCH_YES)
+    {
+      if ((m = gfc_match (" %n ::", module_nature)) == MATCH_YES)
+	{
+	  if (!gfc_notify_std (GFC_STD_F2003, "module "
+			       "nature in USE statement at %C"))
+	    goto cleanup;
+
+	  if (strcmp (module_nature, "intrinsic") == 0)
+	    use_list->intrinsic = true;
+	  else
+	    {
+	      if (strcmp (module_nature, "non_intrinsic") == 0)
+		use_list->non_intrinsic = true;
+	      else
+		{
+		  gfc_error ("Module nature in USE statement at %C shall "
+			     "be either INTRINSIC or NON_INTRINSIC");
+		  goto cleanup;
+		}
+	    }
+	}
+      else
+	{
+	  /* Help output a better error message than "Unclassifiable
+	     statement".  */
+	  gfc_match (" %n", module_nature);
+	  if (strcmp (module_nature, "intrinsic") == 0
+	      || strcmp (module_nature, "non_intrinsic") == 0)
+	    gfc_error ("\"::\" was expected after module nature at %C "
+		       "but was not found");
+	  free (use_list);
+	  return m;
+	}
+    }
+  else
+    {
+      m = gfc_match (" ::");
+      if (m == MATCH_YES &&
+	  !gfc_notify_std(GFC_STD_F2003, "\"USE :: module\" at %C"))
+	goto cleanup;
+
+      if (m != MATCH_YES)
+	{
+	  m = gfc_match ("% ");
+	  if (m != MATCH_YES)
+	    {
+	      free (use_list);
+	      return m;
+	    }
+	}
+    }
+
+  use_list->where = gfc_current_locus;
+
+  m = gfc_match_name (name);
+  if (m != MATCH_YES)
+    {
+      free (use_list);
+      return m;
+    }
+
+  use_list->module_name = gfc_get_string ("%s", name);
+
+  if (gfc_match_eos () == MATCH_YES)
+    goto done;
+
+  if (gfc_match_char (',') != MATCH_YES)
+    goto syntax;
+
+  if (gfc_match (" only :") == MATCH_YES)
+    use_list->only_flag = true;
+
+  if (gfc_match_eos () == MATCH_YES)
+    goto done;
+
+  for (;;)
+    {
+      /* Get a new rename struct and add it to the rename list.  */
+      new_use = gfc_get_use_rename ();
+      new_use->where = gfc_current_locus;
+      new_use->found = 0;
+
+      if (use_list->rename == NULL)
+	use_list->rename = new_use;
+      else
+	tail->next = new_use;
+      tail = new_use;
+
+      /* See what kind of interface we're dealing with.  Assume it is
+	 not an operator.  */
+      new_use->op = INTRINSIC_NONE;
+      if (gfc_match_generic_spec (&type, name, &op) == MATCH_ERROR)
+	goto cleanup;
+
+      switch (type)
+	{
+	case INTERFACE_NAMELESS:
+	  gfc_error ("Missing generic specification in USE statement at %C");
+	  goto cleanup;
+
+	case INTERFACE_USER_OP:
+	case INTERFACE_GENERIC:
+	case INTERFACE_DTIO:
+	  m = gfc_match (" =>");
+
+	  if (type == INTERFACE_USER_OP && m == MATCH_YES
+	      && (!gfc_notify_std(GFC_STD_F2003, "Renaming "
+				  "operators in USE statements at %C")))
+	    goto cleanup;
+
+	  if (type == INTERFACE_USER_OP)
+	    new_use->op = INTRINSIC_USER;
+
+	  if (use_list->only_flag)
+	    {
+	      if (m != MATCH_YES)
+		strcpy (new_use->use_name, name);
+	      else
+		{
+		  strcpy (new_use->local_name, name);
+		  m = gfc_match_generic_spec (&type2, new_use->use_name, &op);
+		  if (type != type2)
+		    goto syntax;
+		  if (m == MATCH_NO)
+		    goto syntax;
+		  if (m == MATCH_ERROR)
+		    goto cleanup;
+		}
+	    }
+	  else
+	    {
+	      if (m != MATCH_YES)
+		goto syntax;
+	      strcpy (new_use->local_name, name);
+
+	      m = gfc_match_generic_spec (&type2, new_use->use_name, &op);
+	      if (type != type2)
+		goto syntax;
+	      if (m == MATCH_NO)
+		goto syntax;
+	      if (m == MATCH_ERROR)
+		goto cleanup;
+	    }
+
+	  if (strcmp (new_use->use_name, use_list->module_name) == 0
+	      || strcmp (new_use->local_name, use_list->module_name) == 0)
+	    {
+	      gfc_error ("The name %qs at %C has already been used as "
+			 "an external module name", use_list->module_name);
+	      goto cleanup;
+	    }
+	  break;
+
+	case INTERFACE_INTRINSIC_OP:
+	  new_use->op = op;
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+
+      if (gfc_match_eos () == MATCH_YES)
+	break;
+      if (gfc_match_char (',') != MATCH_YES)
+	goto syntax;
+    }
+
+done:
+  if (module_list)
+    {
+      gfc_use_list *last = module_list;
+      while (last->next)
+	last = last->next;
+      last->next = use_list;
+    }
+  else
+    module_list = use_list;
+
+  return MATCH_YES;
+
+syntax:
+  gfc_syntax_error (ST_USE);
+
+cleanup:
+  free_rename (use_list->rename);
+  free (use_list);
+  return MATCH_ERROR;
+}
+
+
+/* Match a SUBMODULE statement.
+
+   According to F2008:11.2.3.2, "The submodule identifier is the
+   ordered pair whose first element is the ancestor module name and
+   whose second element is the submodule name. 'Submodule_name' is
+   used for the submodule filename and uses '@' as a separator, whilst
+   the name of the symbol for the module uses '.' as a a separator.
+   The reasons for these choices are:
+   (i) To follow another leading brand in the submodule filenames;
+   (ii) Since '.' is not particularly visible in the filenames; and
+   (iii) The linker does not permit '@' in mnemonics.  */
+
+match
+gfc_match_submodule (void)
+{
+  match m;
+  char name[GFC_MAX_SYMBOL_LEN + 1];
+  gfc_use_list *use_list;
+  bool seen_colon = false;
+
+  if (!gfc_notify_std (GFC_STD_F2008, "SUBMODULE declaration at %C"))
+    return MATCH_ERROR;
+
+  if (gfc_current_state () != COMP_NONE)
+    {
+      gfc_error ("SUBMODULE declaration at %C cannot appear within "
+		 "another scoping unit");
+      return MATCH_ERROR;
+    }
+
+  gfc_new_block = NULL;
+  gcc_assert (module_list == NULL);
+
+  if (gfc_match_char ('(') != MATCH_YES)
+    goto syntax;
+
+  while (1)
+    {
+      m = gfc_match (" %n", name);
+      if (m != MATCH_YES)
+	goto syntax;
+
+      use_list = gfc_get_use_list ();
+      use_list->where = gfc_current_locus;
+
+      if (module_list)
+	{
+	  gfc_use_list *last = module_list;
+	  while (last->next)
+	    last = last->next;
+	  last->next = use_list;
+	  use_list->module_name
+		= gfc_get_string ("%s.%s", module_list->module_name, name);
+	  use_list->submodule_name
+		= gfc_get_string ("%s@%s", module_list->module_name, name);
+	}
+      else
+	{
+	  module_list = use_list;
+	  use_list->module_name = gfc_get_string ("%s", name);
+	  use_list->submodule_name = use_list->module_name;
+	}
+
+      if (gfc_match_char (')') == MATCH_YES)
+	break;
+
+      if (gfc_match_char (':') != MATCH_YES
+	  || seen_colon)
+	goto syntax;
+
+      seen_colon = true;
+    }
+
+  m = gfc_match (" %s%t", &gfc_new_block);
+  if (m != MATCH_YES)
+    goto syntax;
+
+  submodule_name = gfc_get_string ("%s@%s", module_list->module_name,
+				   gfc_new_block->name);
+
+  gfc_new_block->name = gfc_get_string ("%s.%s",
+					module_list->module_name,
+					gfc_new_block->name);
+
+  if (!gfc_add_flavor (&gfc_new_block->attr, FL_MODULE,
+		       gfc_new_block->name, NULL))
+    return MATCH_ERROR;
+
+  /* Just retain the ultimate .(s)mod file for reading, since it
+     contains all the information in its ancestors.  */
+  use_list = module_list;
+  for (; module_list->next; use_list = module_list)
+    {
+      module_list = use_list->next;
+      free (use_list);
+    }
+
+  return MATCH_YES;
+
+syntax:
+  gfc_error ("Syntax error in SUBMODULE statement at %C");
+  return MATCH_ERROR;
+}
+
+
+/* Given a name and a number, inst, return the inst name
+   under which to load this symbol. Returns NULL if this
+   symbol shouldn't be loaded. If inst is zero, returns
+   the number of instances of this name. If interface is
+   true, a user-defined operator is sought, otherwise only
+   non-operators are sought.  */
+
+static const char *
+find_use_name_n (const char *name, int *inst, bool interface)
+{
+  gfc_use_rename *u;
+  const char *low_name = NULL;
+  int i;
+
+  /* For derived types.  */
+  if (name[0] != (char) TOLOWER ((unsigned char) name[0]))
+    low_name = gfc_dt_lower_string (name);
+
+  i = 0;
+  for (u = gfc_rename_list; u; u = u->next)
+    {
+      if ((!low_name && strcmp (u->use_name, name) != 0)
+	  || (low_name && strcmp (u->use_name, low_name) != 0)
+	  || (u->op == INTRINSIC_USER && !interface)
+	  || (u->op != INTRINSIC_USER &&  interface))
+	continue;
+      if (++i == *inst)
+	break;
+    }
+
+  if (!*inst)
+    {
+      *inst = i;
+      return NULL;
+    }
+
+  if (u == NULL)
+    return only_flag ? NULL : name;
+
+  u->found = 1;
+
+  if (low_name)
+    {
+      if (u->local_name[0] == '\0')
+	return name;
+      return gfc_dt_upper_string (u->local_name);
+    }
+
+  return (u->local_name[0] != '\0') ? u->local_name : name;
+}
+
+
+/* Given a name, return the name under which to load this symbol.
+   Returns NULL if this symbol shouldn't be loaded.  */
+
+static const char *
+find_use_name (const char *name, bool interface)
+{
+  int i = 1;
+  return find_use_name_n (name, &i, interface);
+}
+
+
+/* Given a real name, return the number of use names associated with it.  */
+
+static int
+number_use_names (const char *name, bool interface)
+{
+  int i = 0;
+  find_use_name_n (name, &i, interface);
+  return i;
+}
+
+
+/* Try to find the operator in the current list.  */
+
+static gfc_use_rename *
+find_use_operator (gfc_intrinsic_op op)
+{
+  gfc_use_rename *u;
+
+  for (u = gfc_rename_list; u; u = u->next)
+    if (u->op == op)
+      return u;
+
+  return NULL;
+}
+
+
+/*****************************************************************/
+
+/* The next couple of subroutines maintain a tree used to avoid a
+   brute-force search for a combination of true name and module name.
+   While symtree names, the name that a particular symbol is known by
+   can changed with USE statements, we still have to keep track of the
+   true names to generate the correct reference, and also avoid
+   loading the same real symbol twice in a program unit.
+
+   When we start reading, the true name tree is built and maintained
+   as symbols are read.  The tree is searched as we load new symbols
+   to see if it already exists someplace in the namespace.  */
+
+typedef struct true_name
+{
+  BBT_HEADER (true_name);
+  const char *name;
+  gfc_symbol *sym;
+}
+true_name;
+
+static true_name *true_name_root;
+
+
+/* Compare two true_name structures.  */
+
+static int
+compare_true_names (void *_t1, void *_t2)
+{
+  true_name *t1, *t2;
+  int c;
+
+  t1 = (true_name *) _t1;
+  t2 = (true_name *) _t2;
+
+  c = ((t1->sym->module > t2->sym->module)
+       - (t1->sym->module < t2->sym->module));
+  if (c != 0)
+    return c;
+
+  return strcmp (t1->name, t2->name);
+}
+
+
+/* Given a true name, search the true name tree to see if it exists
+   within the main namespace.  */
+
+static gfc_symbol *
+find_true_name (const char *name, const char *module)
+{
+  true_name t, *p;
+  gfc_symbol sym;
+  int c;
+
+  t.name = gfc_get_string ("%s", name);
+  if (module != NULL)
+    sym.module = gfc_get_string ("%s", module);
+  else
+    sym.module = NULL;
+  t.sym = &sym;
+
+  p = true_name_root;
+  while (p != NULL)
+    {
+      c = compare_true_names ((void *) (&t), (void *) p);
+      if (c == 0)
+	return p->sym;
+
+      p = (c < 0) ? p->left : p->right;
+    }
+
+  return NULL;
+}
+
+
+/* Given a gfc_symbol pointer that is not in the true name tree, add it.  */
+
+static void
+add_true_name (gfc_symbol *sym)
+{
+  true_name *t;
+
+  t = XCNEW (true_name);
+  t->sym = sym;
+  if (gfc_fl_struct (sym->attr.flavor))
+    t->name = gfc_dt_upper_string (sym->name);
+  else
+    t->name = sym->name;
+
+  gfc_insert_bbt (&true_name_root, t, compare_true_names);
+}
+
+
+/* Recursive function to build the initial true name tree by
+   recursively traversing the current namespace.  */
+
+static void
+build_tnt (gfc_symtree *st)
+{
+  const char *name;
+  if (st == NULL)
+    return;
+
+  build_tnt (st->left);
+  build_tnt (st->right);
+
+  if (gfc_fl_struct (st->n.sym->attr.flavor))
+    name = gfc_dt_upper_string (st->n.sym->name);
+  else
+    name = st->n.sym->name;
+
+  if (find_true_name (name, st->n.sym->module) != NULL)
+    return;
+
+  add_true_name (st->n.sym);
+}
+
+
+/* Initialize the true name tree with the current namespace.  */
+
+static void
+init_true_name_tree (void)
+{
+  true_name_root = NULL;
+  build_tnt (gfc_current_ns->sym_root);
+}
+
+
+/* Recursively free a true name tree node.  */
+
+static void
+free_true_name (true_name *t)
+{
+  if (t == NULL)
+    return;
+  free_true_name (t->left);
+  free_true_name (t->right);
+
+  free (t);
+}
+
+
+/*****************************************************************/
+
+/* Module reading and writing.  */
+
+/* The following are versions similar to the ones in scanner.c, but
+   for dealing with compressed module files.  */
+
+static gzFile
+gzopen_included_file_1 (const char *name, gfc_directorylist *list,
+                     bool module, bool system)
+{
+  char *fullname;
+  gfc_directorylist *p;
+  gzFile f;
+
+  for (p = list; p; p = p->next)
+    {
+      if (module && !p->use_for_modules)
+       continue;
+
+      fullname = (char *) alloca(strlen (p->path) + strlen (name) + 1);
+      strcpy (fullname, p->path);
+      strcat (fullname, name);
+
+      f = gzopen (fullname, "r");
+      if (f != NULL)
+       {
+         if (gfc_cpp_makedep ())
+           gfc_cpp_add_dep (fullname, system);
+
+         return f;
+       }
+    }
+
+  return NULL;
+}
+
+static gzFile
+gzopen_included_file (const char *name, bool include_cwd, bool module)
+{
+  gzFile f = NULL;
+
+  if (IS_ABSOLUTE_PATH (name) || include_cwd)
+    {
+      f = gzopen (name, "r");
+      if (f && gfc_cpp_makedep ())
+       gfc_cpp_add_dep (name, false);
+    }
+
+  if (!f)
+    f = gzopen_included_file_1 (name, include_dirs, module, false);
+
+  return f;
+}
+
+static gzFile
+gzopen_intrinsic_module (const char* name)
+{
+  gzFile f = NULL;
+
+  if (IS_ABSOLUTE_PATH (name))
+    {
+      f = gzopen (name, "r");
+      if (f && gfc_cpp_makedep ())
+        gfc_cpp_add_dep (name, true);
+    }
+
+  if (!f)
+    f = gzopen_included_file_1 (name, intrinsic_modules_dirs, true, true);
+
+  return f;
+}
+
+
+enum atom_type
+{
+  ATOM_NAME, ATOM_LPAREN, ATOM_RPAREN, ATOM_INTEGER, ATOM_STRING
+};
+
+static atom_type last_atom;
+
+
+/* The name buffer must be at least as long as a symbol name.  Right
+   now it's not clear how we're going to store numeric constants--
+   probably as a hexadecimal string, since this will allow the exact
+   number to be preserved (this can't be done by a decimal
+   representation).  Worry about that later.  TODO!  */
+
+#define MAX_ATOM_SIZE 100
+
+static int atom_int;
+static char *atom_string, atom_name[MAX_ATOM_SIZE];
+
+
+/* Report problems with a module.  Error reporting is not very
+   elaborate, since this sorts of errors shouldn't really happen.
+   This subroutine never returns.  */
+
+static void bad_module (const char *) ATTRIBUTE_NORETURN;
+
+static void
+bad_module (const char *msgid)
+{
+  XDELETEVEC (module_content);
+  module_content = NULL;
+
+  switch (iomode)
+    {
+    case IO_INPUT:
+      gfc_fatal_error ("Reading module %qs at line %d column %d: %s",
+	  	       module_name, module_line, module_column, msgid);
+      break;
+    case IO_OUTPUT:
+      gfc_fatal_error ("Writing module %qs at line %d column %d: %s",
+	  	       module_name, module_line, module_column, msgid);
+      break;
+    default:
+      gfc_fatal_error ("Module %qs at line %d column %d: %s",
+	  	       module_name, module_line, module_column, msgid);
+      break;
+    }
+}
+
+
+/* Set the module's input pointer.  */
+
+static void
+set_module_locus (module_locus *m)
+{
+  module_column = m->column;
+  module_line = m->line;
+  module_pos = m->pos;
+}
+
+
+/* Get the module's input pointer so that we can restore it later.  */
+
+static void
+get_module_locus (module_locus *m)
+{
+  m->column = module_column;
+  m->line = module_line;
+  m->pos = module_pos;
+}
+
+
+/* Get the next character in the module, updating our reckoning of
+   where we are.  */
+
+static int
+module_char (void)
+{
+  const char c = module_content[module_pos++];
+  if (c == '\0')
+    bad_module ("Unexpected EOF");
+
+  prev_module_line = module_line;
+  prev_module_column = module_column;
+
+  if (c == '\n')
+    {
+      module_line++;
+      module_column = 0;
+    }
+
+  module_column++;
+  return c;
+}
+
+/* Unget a character while remembering the line and column.  Works for
+   a single character only.  */
+
+static void
+module_unget_char (void)
+{
+  module_line = prev_module_line;
+  module_column = prev_module_column;
+  module_pos--;
+}
+
+/* Parse a string constant.  The delimiter is guaranteed to be a
+   single quote.  */
+
+static void
+parse_string (void)
+{
+  int c;
+  size_t cursz = 30;
+  size_t len = 0;
+
+  atom_string = XNEWVEC (char, cursz);
+
+  for ( ; ; )
+    {
+      c = module_char ();
+
+      if (c == '\'')
+	{
+	  int c2 = module_char ();
+	  if (c2 != '\'')
+	    {
+	      module_unget_char ();
+	      break;
+	    }
+	}
+
+      if (len >= cursz)
+	{
+	  cursz *= 2;
+	  atom_string = XRESIZEVEC (char, atom_string, cursz);
+	}
+      atom_string[len] = c;
+      len++;
+    }
+
+  atom_string = XRESIZEVEC (char, atom_string, len + 1);
+  atom_string[len] = '\0'; 	/* C-style string for debug purposes.  */
+}
+
+
+/* Parse a small integer.  */
+
+static void
+parse_integer (int c)
+{
+  atom_int = c - '0';
+
+  for (;;)
+    {
+      c = module_char ();
+      if (!ISDIGIT (c))
+	{
+	  module_unget_char ();
+	  break;
+	}
+
+      atom_int = 10 * atom_int + c - '0';
+      if (atom_int > 99999999)
+	bad_module ("Integer overflow");
+    }
+
+}
+
+
+/* Parse a name.  */
+
+static void
+parse_name (int c)
+{
+  char *p;
+  int len;
+
+  p = atom_name;
+
+  *p++ = c;
+  len = 1;
+
+  for (;;)
+    {
+      c = module_char ();
+      if (!ISALNUM (c) && c != '_' && c != '-')
+	{
+	  module_unget_char ();
+	  break;
+	}
+
+      *p++ = c;
+      if (++len > GFC_MAX_SYMBOL_LEN)
+	bad_module ("Name too long");
+    }
+
+  *p = '\0';
+
+}
+
+
+/* Read the next atom in the module's input stream.  */
+
+static atom_type
+parse_atom (void)
+{
+  int c;
+
+  do
+    {
+      c = module_char ();
+    }
+  while (c == ' ' || c == '\r' || c == '\n');
+
+  switch (c)
+    {
+    case '(':
+      return ATOM_LPAREN;
+
+    case ')':
+      return ATOM_RPAREN;
+
+    case '\'':
+      parse_string ();
+      return ATOM_STRING;
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      parse_integer (c);
+      return ATOM_INTEGER;
+
+    case 'a':
+    case 'b':
+    case 'c':
+    case 'd':
+    case 'e':
+    case 'f':
+    case 'g':
+    case 'h':
+    case 'i':
+    case 'j':
+    case 'k':
+    case 'l':
+    case 'm':
+    case 'n':
+    case 'o':
+    case 'p':
+    case 'q':
+    case 'r':
+    case 's':
+    case 't':
+    case 'u':
+    case 'v':
+    case 'w':
+    case 'x':
+    case 'y':
+    case 'z':
+    case 'A':
+    case 'B':
+    case 'C':
+    case 'D':
+    case 'E':
+    case 'F':
+    case 'G':
+    case 'H':
+    case 'I':
+    case 'J':
+    case 'K':
+    case 'L':
+    case 'M':
+    case 'N':
+    case 'O':
+    case 'P':
+    case 'Q':
+    case 'R':
+    case 'S':
+    case 'T':
+    case 'U':
+    case 'V':
+    case 'W':
+    case 'X':
+    case 'Y':
+    case 'Z':
+      parse_name (c);
+      return ATOM_NAME;
+
+    default:
+      bad_module ("Bad name");
+    }
+
+  /* Not reached.  */
+}
+
+
+/* Peek at the next atom on the input.  */
+
+static atom_type
+peek_atom (void)
+{
+  int c;
+
+  do
+    {
+      c = module_char ();
+    }
+  while (c == ' ' || c == '\r' || c == '\n');
+
+  switch (c)
+    {
+    case '(':
+      module_unget_char ();
+      return ATOM_LPAREN;
+
+    case ')':
+      module_unget_char ();
+      return ATOM_RPAREN;
+
+    case '\'':
+      module_unget_char ();
+      return ATOM_STRING;
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      module_unget_char ();
+      return ATOM_INTEGER;
+
+    case 'a':
+    case 'b':
+    case 'c':
+    case 'd':
+    case 'e':
+    case 'f':
+    case 'g':
+    case 'h':
+    case 'i':
+    case 'j':
+    case 'k':
+    case 'l':
+    case 'm':
+    case 'n':
+    case 'o':
+    case 'p':
+    case 'q':
+    case 'r':
+    case 's':
+    case 't':
+    case 'u':
+    case 'v':
+    case 'w':
+    case 'x':
+    case 'y':
+    case 'z':
+    case 'A':
+    case 'B':
+    case 'C':
+    case 'D':
+    case 'E':
+    case 'F':
+    case 'G':
+    case 'H':
+    case 'I':
+    case 'J':
+    case 'K':
+    case 'L':
+    case 'M':
+    case 'N':
+    case 'O':
+    case 'P':
+    case 'Q':
+    case 'R':
+    case 'S':
+    case 'T':
+    case 'U':
+    case 'V':
+    case 'W':
+    case 'X':
+    case 'Y':
+    case 'Z':
+      module_unget_char ();
+      return ATOM_NAME;
+
+    default:
+      bad_module ("Bad name");
+    }
+}
+
+
+/* Read the next atom from the input, requiring that it be a
+   particular kind.  */
+
+static void
+require_atom (atom_type type)
+{
+  atom_type t;
+  const char *p;
+  int column, line;
+
+  column = module_column;
+  line = module_line;
+
+  t = parse_atom ();
+  if (t != type)
+    {
+      switch (type)
+	{
+	case ATOM_NAME:
+	  p = _("Expected name");
+	  break;
+	case ATOM_LPAREN:
+	  p = _("Expected left parenthesis");
+	  break;
+	case ATOM_RPAREN:
+	  p = _("Expected right parenthesis");
+	  break;
+	case ATOM_INTEGER:
+	  p = _("Expected integer");
+	  break;
+	case ATOM_STRING:
+	  p = _("Expected string");
+	  break;
+	default:
+	  gfc_internal_error ("require_atom(): bad atom type required");
+	}
+
+      module_column = column;
+      module_line = line;
+      bad_module (p);
+    }
+}
+
+
+/* Given a pointer to an mstring array, require that the current input
+   be one of the strings in the array.  We return the enum value.  */
+
+static int
+find_enum (const mstring *m)
+{
+  int i;
+
+  i = gfc_string2code (m, atom_name);
+  if (i >= 0)
+    return i;
+
+  bad_module ("find_enum(): Enum not found");
+
+  /* Not reached.  */
+}
+
+
+/* Read a string. The caller is responsible for freeing.  */
+
+static char*
+read_string (void)
+{
+  char* p;
+  require_atom (ATOM_STRING);
+  p = atom_string;
+  atom_string = NULL;
+  return p;
+}
+
+
+/**************** Module output subroutines ***************************/
+
+/* Output a character to a module file.  */
+
+static void
+write_char (char out)
+{
+  if (gzputc (module_fp, out) == EOF)
+    gfc_fatal_error ("Error writing modules file: %s", xstrerror (errno));
+
+  if (out != '\n')
+    module_column++;
+  else
+    {
+      module_column = 1;
+      module_line++;
+    }
+}
+
+
+/* Write an atom to a module.  The line wrapping isn't perfect, but it
+   should work most of the time.  This isn't that big of a deal, since
+   the file really isn't meant to be read by people anyway.  */
+
+static void
+write_atom (atom_type atom, const void *v)
+{
+  char buffer[20];
+
+  /* Workaround -Wmaybe-uninitialized false positive during
+     profiledbootstrap by initializing them.  */
+  int i = 0, len;
+  const char *p;
+
+  switch (atom)
+    {
+    case ATOM_STRING:
+    case ATOM_NAME:
+      p = (const char *) v;
+      break;
+
+    case ATOM_LPAREN:
+      p = "(";
+      break;
+
+    case ATOM_RPAREN:
+      p = ")";
+      break;
+
+    case ATOM_INTEGER:
+      i = *((const int *) v);
+      if (i < 0)
+	gfc_internal_error ("write_atom(): Writing negative integer");
+
+      sprintf (buffer, "%d", i);
+      p = buffer;
+      break;
+
+    default:
+      gfc_internal_error ("write_atom(): Trying to write dab atom");
+
+    }
+
+  if(p == NULL || *p == '\0')
+     len = 0;
+  else
+  len = strlen (p);
+
+  if (atom != ATOM_RPAREN)
+    {
+      if (module_column + len > 72)
+	write_char ('\n');
+      else
+	{
+
+	  if (last_atom != ATOM_LPAREN && module_column != 1)
+	    write_char (' ');
+	}
+    }
+
+  if (atom == ATOM_STRING)
+    write_char ('\'');
+
+  while (p != NULL && *p)
+    {
+      if (atom == ATOM_STRING && *p == '\'')
+	write_char ('\'');
+      write_char (*p++);
+    }
+
+  if (atom == ATOM_STRING)
+    write_char ('\'');
+
+  last_atom = atom;
+}
+
+
+
+/***************** Mid-level I/O subroutines *****************/
+
+/* These subroutines let their caller read or write atoms without
+   caring about which of the two is actually happening.  This lets a
+   subroutine concentrate on the actual format of the data being
+   written.  */
+
+static void mio_expr (gfc_expr **);
+pointer_info *mio_symbol_ref (gfc_symbol **);
+pointer_info *mio_interface_rest (gfc_interface **);
+static void mio_symtree_ref (gfc_symtree **);
+
+/* Read or write an enumerated value.  On writing, we return the input
+   value for the convenience of callers.  We avoid using an integer
+   pointer because enums are sometimes inside bitfields.  */
+
+static int
+mio_name (int t, const mstring *m)
+{
+  if (iomode == IO_OUTPUT)
+    write_atom (ATOM_NAME, gfc_code2string (m, t));
+  else
+    {
+      require_atom (ATOM_NAME);
+      t = find_enum (m);
+    }
+
+  return t;
+}
+
+/* Specialization of mio_name.  */
+
+#define DECL_MIO_NAME(TYPE) \
+ static inline TYPE \
+ MIO_NAME(TYPE) (TYPE t, const mstring *m) \
+ { \
+   return (TYPE) mio_name ((int) t, m); \
+ }
+#define MIO_NAME(TYPE) mio_name_##TYPE
+
+static void
+mio_lparen (void)
+{
+  if (iomode == IO_OUTPUT)
+    write_atom (ATOM_LPAREN, NULL);
+  else
+    require_atom (ATOM_LPAREN);
+}
+
+
+static void
+mio_rparen (void)
+{
+  if (iomode == IO_OUTPUT)
+    write_atom (ATOM_RPAREN, NULL);
+  else
+    require_atom (ATOM_RPAREN);
+}
+
+
+static void
+mio_integer (int *ip)
+{
+  if (iomode == IO_OUTPUT)
+    write_atom (ATOM_INTEGER, ip);
+  else
+    {
+      require_atom (ATOM_INTEGER);
+      *ip = atom_int;
+    }
+}
+
+
+/* Read or write a gfc_intrinsic_op value.  */
+
+static void
+mio_intrinsic_op (gfc_intrinsic_op* op)
+{
+  /* FIXME: Would be nicer to do this via the operators symbolic name.  */
+  if (iomode == IO_OUTPUT)
+    {
+      int converted = (int) *op;
+      write_atom (ATOM_INTEGER, &converted);
+    }
+  else
+    {
+      require_atom (ATOM_INTEGER);
+      *op = (gfc_intrinsic_op) atom_int;
+    }
+}
+
+
+/* Read or write a character pointer that points to a string on the heap.  */
+
+static const char *
+mio_allocated_string (const char *s)
+{
+  if (iomode == IO_OUTPUT)
+    {
+      write_atom (ATOM_STRING, s);
+      return s;
+    }
+  else
+    {
+      require_atom (ATOM_STRING);
+      return atom_string;
+    }
+}
+
+
+/* Functions for quoting and unquoting strings.  */
+
+static char *
+quote_string (const gfc_char_t *s, const size_t slength)
+{
+  const gfc_char_t *p;
+  char *res, *q;
+  size_t len = 0, i;
+
+  /* Calculate the length we'll need: a backslash takes two ("\\"),
+     non-printable characters take 10 ("\Uxxxxxxxx") and others take 1.  */
+  for (p = s, i = 0; i < slength; p++, i++)
+    {
+      if (*p == '\\')
+	len += 2;
+      else if (!gfc_wide_is_printable (*p))
+	len += 10;
+      else
+	len++;
+    }
+
+  q = res = XCNEWVEC (char, len + 1);
+  for (p = s, i = 0; i < slength; p++, i++)
+    {
+      if (*p == '\\')
+	*q++ = '\\', *q++ = '\\';
+      else if (!gfc_wide_is_printable (*p))
+	{
+	  sprintf (q, "\\U%08" HOST_WIDE_INT_PRINT "x",
+		   (unsigned HOST_WIDE_INT) *p);
+	  q += 10;
+	}
+      else
+	*q++ = (unsigned char) *p;
+    }
+
+  res[len] = '\0';
+  return res;
+}
+
+static gfc_char_t *
+unquote_string (const char *s)
+{
+  size_t len, i;
+  const char *p;
+  gfc_char_t *res;
+
+  for (p = s, len = 0; *p; p++, len++)
+    {
+      if (*p != '\\')
+	continue;
+
+      if (p[1] == '\\')
+	p++;
+      else if (p[1] == 'U')
+	p += 9; /* That is a "\U????????".  */
+      else
+	gfc_internal_error ("unquote_string(): got bad string");
+    }
+
+  res = gfc_get_wide_string (len + 1);
+  for (i = 0, p = s; i < len; i++, p++)
+    {
+      gcc_assert (*p);
+
+      if (*p != '\\')
+	res[i] = (unsigned char) *p;
+      else if (p[1] == '\\')
+	{
+	  res[i] = (unsigned char) '\\';
+	  p++;
+	}
+      else
+	{
+	  /* We read the 8-digits hexadecimal constant that follows.  */
+	  int j;
+	  unsigned n;
+	  gfc_char_t c = 0;
+
+	  gcc_assert (p[1] == 'U');
+	  for (j = 0; j < 8; j++)
+	    {
+	      c = c << 4;
+	      gcc_assert (sscanf (&p[j+2], "%01x", &n) == 1);
+	      c += n;
+	    }
+
+	  res[i] = c;
+	  p += 9;
+	}
+    }
+
+  res[len] = '\0';
+  return res;
+}
+
+
+/* Read or write a character pointer that points to a wide string on the
+   heap, performing quoting/unquoting of nonprintable characters using the
+   form \U???????? (where each ? is a hexadecimal digit).
+   Length is the length of the string, only known and used in output mode.  */
+
+static const gfc_char_t *
+mio_allocated_wide_string (const gfc_char_t *s, const size_t length)
+{
+  if (iomode == IO_OUTPUT)
+    {
+      char *quoted = quote_string (s, length);
+      write_atom (ATOM_STRING, quoted);
+      free (quoted);
+      return s;
+    }
+  else
+    {
+      gfc_char_t *unquoted;
+
+      require_atom (ATOM_STRING);
+      unquoted = unquote_string (atom_string);
+      free (atom_string);
+      return unquoted;
+    }
+}
+
+
+/* Read or write a string that is in static memory.  */
+
+static void
+mio_pool_string (const char **stringp)
+{
+  /* TODO: one could write the string only once, and refer to it via a
+     fixup pointer.  */
+
+  /* As a special case we have to deal with a NULL string.  This
+     happens for the 'module' member of 'gfc_symbol's that are not in a
+     module.  We read / write these as the empty string.  */
+  if (iomode == IO_OUTPUT)
+    {
+      const char *p = *stringp == NULL ? "" : *stringp;
+      write_atom (ATOM_STRING, p);
+    }
+  else
+    {
+      require_atom (ATOM_STRING);
+      *stringp = (atom_string[0] == '\0'
+		  ? NULL : gfc_get_string ("%s", atom_string));
+      free (atom_string);
+    }
+}
+
+
+/* Read or write a string that is inside of some already-allocated
+   structure.  */
+
+static void
+mio_internal_string (char *string)
+{
+  if (iomode == IO_OUTPUT)
+    write_atom (ATOM_STRING, string);
+  else
+    {
+      require_atom (ATOM_STRING);
+      strcpy (string, atom_string);
+      free (atom_string);
+    }
+}
+
+
+enum ab_attribute
+{ AB_ALLOCATABLE, AB_DIMENSION, AB_EXTERNAL, AB_INTRINSIC, AB_OPTIONAL,
+  AB_POINTER, AB_TARGET, AB_DUMMY, AB_RESULT, AB_DATA,
+  AB_IN_NAMELIST, AB_IN_COMMON, AB_FUNCTION, AB_SUBROUTINE, AB_SEQUENCE,
+  AB_ELEMENTAL, AB_PURE, AB_RECURSIVE, AB_GENERIC, AB_ALWAYS_EXPLICIT,
+  AB_CRAY_POINTER, AB_CRAY_POINTEE, AB_THREADPRIVATE,
+  AB_ALLOC_COMP, AB_POINTER_COMP, AB_PROC_POINTER_COMP, AB_PRIVATE_COMP,
+  AB_VALUE, AB_VOLATILE, AB_PROTECTED, AB_LOCK_COMP, AB_EVENT_COMP,
+  AB_IS_BIND_C, AB_IS_C_INTEROP, AB_IS_ISO_C, AB_ABSTRACT, AB_ZERO_COMP,
+  AB_IS_CLASS, AB_PROCEDURE, AB_PROC_POINTER, AB_ASYNCHRONOUS, AB_CODIMENSION,
+  AB_COARRAY_COMP, AB_VTYPE, AB_VTAB, AB_CONTIGUOUS, AB_CLASS_POINTER,
+  AB_IMPLICIT_PURE, AB_ARTIFICIAL, AB_UNLIMITED_POLY, AB_OMP_DECLARE_TARGET,
+  AB_ARRAY_OUTER_DEPENDENCY, AB_MODULE_PROCEDURE, AB_OACC_DECLARE_CREATE,
+  AB_OACC_DECLARE_COPYIN, AB_OACC_DECLARE_DEVICEPTR,
+  AB_OACC_DECLARE_DEVICE_RESIDENT, AB_OACC_DECLARE_LINK,
+  AB_OMP_DECLARE_TARGET_LINK, AB_PDT_KIND, AB_PDT_LEN, AB_PDT_TYPE,
+  AB_PDT_TEMPLATE, AB_PDT_ARRAY, AB_PDT_STRING
+};
+
+static const mstring attr_bits[] =
+{
+    minit ("ALLOCATABLE", AB_ALLOCATABLE),
+    minit ("ARTIFICIAL", AB_ARTIFICIAL),
+    minit ("ASYNCHRONOUS", AB_ASYNCHRONOUS),
+    minit ("DIMENSION", AB_DIMENSION),
+    minit ("CODIMENSION", AB_CODIMENSION),
+    minit ("CONTIGUOUS", AB_CONTIGUOUS),
+    minit ("EXTERNAL", AB_EXTERNAL),
+    minit ("INTRINSIC", AB_INTRINSIC),
+    minit ("OPTIONAL", AB_OPTIONAL),
+    minit ("POINTER", AB_POINTER),
+    minit ("VOLATILE", AB_VOLATILE),
+    minit ("TARGET", AB_TARGET),
+    minit ("THREADPRIVATE", AB_THREADPRIVATE),
+    minit ("DUMMY", AB_DUMMY),
+    minit ("RESULT", AB_RESULT),
+    minit ("DATA", AB_DATA),
+    minit ("IN_NAMELIST", AB_IN_NAMELIST),
+    minit ("IN_COMMON", AB_IN_COMMON),
+    minit ("FUNCTION", AB_FUNCTION),
+    minit ("SUBROUTINE", AB_SUBROUTINE),
+    minit ("SEQUENCE", AB_SEQUENCE),
+    minit ("ELEMENTAL", AB_ELEMENTAL),
+    minit ("PURE", AB_PURE),
+    minit ("RECURSIVE", AB_RECURSIVE),
+    minit ("GENERIC", AB_GENERIC),
+    minit ("ALWAYS_EXPLICIT", AB_ALWAYS_EXPLICIT),
+    minit ("CRAY_POINTER", AB_CRAY_POINTER),
+    minit ("CRAY_POINTEE", AB_CRAY_POINTEE),
+    minit ("IS_BIND_C", AB_IS_BIND_C),
+    minit ("IS_C_INTEROP", AB_IS_C_INTEROP),
+    minit ("IS_ISO_C", AB_IS_ISO_C),
+    minit ("VALUE", AB_VALUE),
+    minit ("ALLOC_COMP", AB_ALLOC_COMP),
+    minit ("COARRAY_COMP", AB_COARRAY_COMP),
+    minit ("LOCK_COMP", AB_LOCK_COMP),
+    minit ("EVENT_COMP", AB_EVENT_COMP),
+    minit ("POINTER_COMP", AB_POINTER_COMP),
+    minit ("PROC_POINTER_COMP", AB_PROC_POINTER_COMP),
+    minit ("PRIVATE_COMP", AB_PRIVATE_COMP),
+    minit ("ZERO_COMP", AB_ZERO_COMP),
+    minit ("PROTECTED", AB_PROTECTED),
+    minit ("ABSTRACT", AB_ABSTRACT),
+    minit ("IS_CLASS", AB_IS_CLASS),
+    minit ("PROCEDURE", AB_PROCEDURE),
+    minit ("PROC_POINTER", AB_PROC_POINTER),
+    minit ("VTYPE", AB_VTYPE),
+    minit ("VTAB", AB_VTAB),
+    minit ("CLASS_POINTER", AB_CLASS_POINTER),
+    minit ("IMPLICIT_PURE", AB_IMPLICIT_PURE),
+    minit ("UNLIMITED_POLY", AB_UNLIMITED_POLY),
+    minit ("OMP_DECLARE_TARGET", AB_OMP_DECLARE_TARGET),
+    minit ("ARRAY_OUTER_DEPENDENCY", AB_ARRAY_OUTER_DEPENDENCY),
+    minit ("MODULE_PROCEDURE", AB_MODULE_PROCEDURE),
+    minit ("OACC_DECLARE_CREATE", AB_OACC_DECLARE_CREATE),
+    minit ("OACC_DECLARE_COPYIN", AB_OACC_DECLARE_COPYIN),
+    minit ("OACC_DECLARE_DEVICEPTR", AB_OACC_DECLARE_DEVICEPTR),
+    minit ("OACC_DECLARE_DEVICE_RESIDENT", AB_OACC_DECLARE_DEVICE_RESIDENT),
+    minit ("OACC_DECLARE_LINK", AB_OACC_DECLARE_LINK),
+    minit ("OMP_DECLARE_TARGET_LINK", AB_OMP_DECLARE_TARGET_LINK),
+    minit ("PDT_KIND", AB_PDT_KIND),
+    minit ("PDT_LEN", AB_PDT_LEN),
+    minit ("PDT_TYPE", AB_PDT_TYPE),
+    minit ("PDT_TEMPLATE", AB_PDT_TEMPLATE),
+    minit ("PDT_ARRAY", AB_PDT_ARRAY),
+    minit ("PDT_STRING", AB_PDT_STRING),
+    minit (NULL, -1)
+};
+
+/* For binding attributes.  */
+static const mstring binding_passing[] =
+{
+    minit ("PASS", 0),
+    minit ("NOPASS", 1),
+    minit (NULL, -1)
+};
+static const mstring binding_overriding[] =
+{
+    minit ("OVERRIDABLE", 0),
+    minit ("NON_OVERRIDABLE", 1),
+    minit ("DEFERRED", 2),
+    minit (NULL, -1)
+};
+static const mstring binding_generic[] =
+{
+    minit ("SPECIFIC", 0),
+    minit ("GENERIC", 1),
+    minit (NULL, -1)
+};
+static const mstring binding_ppc[] =
+{
+    minit ("NO_PPC", 0),
+    minit ("PPC", 1),
+    minit (NULL, -1)
+};
+
+/* Specialization of mio_name.  */
+DECL_MIO_NAME (ab_attribute)
+DECL_MIO_NAME (ar_type)
+DECL_MIO_NAME (array_type)
+DECL_MIO_NAME (bt)
+DECL_MIO_NAME (expr_t)
+DECL_MIO_NAME (gfc_access)
+DECL_MIO_NAME (gfc_intrinsic_op)
+DECL_MIO_NAME (ifsrc)
+DECL_MIO_NAME (save_state)
+DECL_MIO_NAME (procedure_type)
+DECL_MIO_NAME (ref_type)
+DECL_MIO_NAME (sym_flavor)
+DECL_MIO_NAME (sym_intent)
+#undef DECL_MIO_NAME
+
+/* Symbol attributes are stored in list with the first three elements
+   being the enumerated fields, while the remaining elements (if any)
+   indicate the individual attribute bits.  The access field is not
+   saved-- it controls what symbols are exported when a module is
+   written.  */
+
+static void
+mio_symbol_attribute (symbol_attribute *attr)
+{
+  atom_type t;
+  unsigned ext_attr,extension_level;
+
+  mio_lparen ();
+
+  attr->flavor = MIO_NAME (sym_flavor) (attr->flavor, flavors);
+  attr->intent = MIO_NAME (sym_intent) (attr->intent, intents);
+  attr->proc = MIO_NAME (procedure_type) (attr->proc, procedures);
+  attr->if_source = MIO_NAME (ifsrc) (attr->if_source, ifsrc_types);
+  attr->save = MIO_NAME (save_state) (attr->save, save_status);
+
+  ext_attr = attr->ext_attr;
+  mio_integer ((int *) &ext_attr);
+  attr->ext_attr = ext_attr;
+
+  extension_level = attr->extension;
+  mio_integer ((int *) &extension_level);
+  attr->extension = extension_level;
+
+  if (iomode == IO_OUTPUT)
+    {
+      if (attr->allocatable)
+	MIO_NAME (ab_attribute) (AB_ALLOCATABLE, attr_bits);
+      if (attr->artificial)
+	MIO_NAME (ab_attribute) (AB_ARTIFICIAL, attr_bits);
+      if (attr->asynchronous)
+	MIO_NAME (ab_attribute) (AB_ASYNCHRONOUS, attr_bits);
+      if (attr->dimension)
+	MIO_NAME (ab_attribute) (AB_DIMENSION, attr_bits);
+      if (attr->codimension)
+	MIO_NAME (ab_attribute) (AB_CODIMENSION, attr_bits);
+      if (attr->contiguous)
+	MIO_NAME (ab_attribute) (AB_CONTIGUOUS, attr_bits);
+      if (attr->external)
+	MIO_NAME (ab_attribute) (AB_EXTERNAL, attr_bits);
+      if (attr->intrinsic)
+	MIO_NAME (ab_attribute) (AB_INTRINSIC, attr_bits);
+      if (attr->optional)
+	MIO_NAME (ab_attribute) (AB_OPTIONAL, attr_bits);
+      if (attr->pointer)
+	MIO_NAME (ab_attribute) (AB_POINTER, attr_bits);
+      if (attr->class_pointer)
+	MIO_NAME (ab_attribute) (AB_CLASS_POINTER, attr_bits);
+      if (attr->is_protected)
+	MIO_NAME (ab_attribute) (AB_PROTECTED, attr_bits);
+      if (attr->value)
+	MIO_NAME (ab_attribute) (AB_VALUE, attr_bits);
+      if (attr->volatile_)
+	MIO_NAME (ab_attribute) (AB_VOLATILE, attr_bits);
+      if (attr->target)
+	MIO_NAME (ab_attribute) (AB_TARGET, attr_bits);
+      if (attr->threadprivate)
+	MIO_NAME (ab_attribute) (AB_THREADPRIVATE, attr_bits);
+      if (attr->dummy)
+	MIO_NAME (ab_attribute) (AB_DUMMY, attr_bits);
+      if (attr->result)
+	MIO_NAME (ab_attribute) (AB_RESULT, attr_bits);
+      /* We deliberately don't preserve the "entry" flag.  */
+
+      if (attr->data)
+	MIO_NAME (ab_attribute) (AB_DATA, attr_bits);
+      if (attr->in_namelist)
+	MIO_NAME (ab_attribute) (AB_IN_NAMELIST, attr_bits);
+      if (attr->in_common)
+	MIO_NAME (ab_attribute) (AB_IN_COMMON, attr_bits);
+
+      if (attr->function)
+	MIO_NAME (ab_attribute) (AB_FUNCTION, attr_bits);
+      if (attr->subroutine)
+	MIO_NAME (ab_attribute) (AB_SUBROUTINE, attr_bits);
+      if (attr->generic)
+	MIO_NAME (ab_attribute) (AB_GENERIC, attr_bits);
+      if (attr->abstract)
+	MIO_NAME (ab_attribute) (AB_ABSTRACT, attr_bits);
+
+      if (attr->sequence)
+	MIO_NAME (ab_attribute) (AB_SEQUENCE, attr_bits);
+      if (attr->elemental)
+	MIO_NAME (ab_attribute) (AB_ELEMENTAL, attr_bits);
+      if (attr->pure)
+	MIO_NAME (ab_attribute) (AB_PURE, attr_bits);
+      if (attr->implicit_pure)
+	MIO_NAME (ab_attribute) (AB_IMPLICIT_PURE, attr_bits);
+      if (attr->unlimited_polymorphic)
+	MIO_NAME (ab_attribute) (AB_UNLIMITED_POLY, attr_bits);
+      if (attr->recursive)
+	MIO_NAME (ab_attribute) (AB_RECURSIVE, attr_bits);
+      if (attr->always_explicit)
+	MIO_NAME (ab_attribute) (AB_ALWAYS_EXPLICIT, attr_bits);
+      if (attr->cray_pointer)
+	MIO_NAME (ab_attribute) (AB_CRAY_POINTER, attr_bits);
+      if (attr->cray_pointee)
+	MIO_NAME (ab_attribute) (AB_CRAY_POINTEE, attr_bits);
+      if (attr->is_bind_c)
+	MIO_NAME(ab_attribute) (AB_IS_BIND_C, attr_bits);
+      if (attr->is_c_interop)
+	MIO_NAME(ab_attribute) (AB_IS_C_INTEROP, attr_bits);
+      if (attr->is_iso_c)
+	MIO_NAME(ab_attribute) (AB_IS_ISO_C, attr_bits);
+      if (attr->alloc_comp)
+	MIO_NAME (ab_attribute) (AB_ALLOC_COMP, attr_bits);
+      if (attr->pointer_comp)
+	MIO_NAME (ab_attribute) (AB_POINTER_COMP, attr_bits);
+      if (attr->proc_pointer_comp)
+	MIO_NAME (ab_attribute) (AB_PROC_POINTER_COMP, attr_bits);
+      if (attr->private_comp)
+	MIO_NAME (ab_attribute) (AB_PRIVATE_COMP, attr_bits);
+      if (attr->coarray_comp)
+	MIO_NAME (ab_attribute) (AB_COARRAY_COMP, attr_bits);
+      if (attr->lock_comp)
+	MIO_NAME (ab_attribute) (AB_LOCK_COMP, attr_bits);
+      if (attr->event_comp)
+	MIO_NAME (ab_attribute) (AB_EVENT_COMP, attr_bits);
+      if (attr->zero_comp)
+	MIO_NAME (ab_attribute) (AB_ZERO_COMP, attr_bits);
+      if (attr->is_class)
+	MIO_NAME (ab_attribute) (AB_IS_CLASS, attr_bits);
+      if (attr->procedure)
+	MIO_NAME (ab_attribute) (AB_PROCEDURE, attr_bits);
+      if (attr->proc_pointer)
+	MIO_NAME (ab_attribute) (AB_PROC_POINTER, attr_bits);
+      if (attr->vtype)
+	MIO_NAME (ab_attribute) (AB_VTYPE, attr_bits);
+      if (attr->vtab)
+	MIO_NAME (ab_attribute) (AB_VTAB, attr_bits);
+      if (attr->omp_declare_target)
+	MIO_NAME (ab_attribute) (AB_OMP_DECLARE_TARGET, attr_bits);
+      if (attr->array_outer_dependency)
+	MIO_NAME (ab_attribute) (AB_ARRAY_OUTER_DEPENDENCY, attr_bits);
+      if (attr->module_procedure)
+	MIO_NAME (ab_attribute) (AB_MODULE_PROCEDURE, attr_bits);
+      if (attr->oacc_declare_create)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_CREATE, attr_bits);
+      if (attr->oacc_declare_copyin)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_COPYIN, attr_bits);
+      if (attr->oacc_declare_deviceptr)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICEPTR, attr_bits);
+      if (attr->oacc_declare_device_resident)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_DEVICE_RESIDENT, attr_bits);
+      if (attr->oacc_declare_link)
+	MIO_NAME (ab_attribute) (AB_OACC_DECLARE_LINK, attr_bits);
+      if (attr->omp_declare_target_link)
+	MIO_NAME (ab_attribute) (AB_OMP_DECLARE_TARGET_LINK, attr_bits);
+      if (attr->pdt_kind)
+	MIO_NAME (ab_attribute) (AB_PDT_KIND, attr_bits);
+      if (attr->pdt_len)
+	MIO_NAME (ab_attribute) (AB_PDT_LEN, attr_bits);
+      if (attr->pdt_type)
+	MIO_NAME (ab_attribute) (AB_PDT_TYPE, attr_bits);
+      if (attr->pdt_template)
+	MIO_NAME (ab_attribute) (AB_PDT_TEMPLATE, attr_bits);
+      if (attr->pdt_array)
+	MIO_NAME (ab_attribute) (AB_PDT_ARRAY, attr_bits);
+      if (attr->pdt_string)
+	MIO_NAME (ab_attribute) (AB_PDT_STRING, attr_bits);
+
+      mio_rparen ();
+
+    }
+  else
+    {
+      for (;;)
+	{
+	  t = parse_atom ();
+	  if (t == ATOM_RPAREN)
+	    break;
+	  if (t != ATOM_NAME)
+	    bad_module ("Expected attribute bit name");
+
+	  switch ((ab_attribute) find_enum (attr_bits))
+	    {
+	    case AB_ALLOCATABLE:
+	      attr->allocatable = 1;
+	      break;
+	    case AB_ARTIFICIAL:
+	      attr->artificial = 1;
+	      break;
+	    case AB_ASYNCHRONOUS:
+	      attr->asynchronous = 1;
+	      break;
+	    case AB_DIMENSION:
+	      attr->dimension = 1;
+	      break;
+	    case AB_CODIMENSION:
+	      attr->codimension = 1;
+	      break;
+	    case AB_CONTIGUOUS:
+	      attr->contiguous = 1;
+	      break;
+	    case AB_EXTERNAL:
+	      attr->external = 1;
+	      break;
+	    case AB_INTRINSIC:
+	      attr->intrinsic = 1;
+	      break;
+	    case AB_OPTIONAL:
+	      attr->optional = 1;
+	      break;
+	    case AB_POINTER:
+	      attr->pointer = 1;
+	      break;
+	    case AB_CLASS_POINTER:
+	      attr->class_pointer = 1;
+	      break;
+	    case AB_PROTECTED:
+	      attr->is_protected = 1;
+	      break;
+	    case AB_VALUE:
+	      attr->value = 1;
+	      break;
+	    case AB_VOLATILE:
+	      attr->volatile_ = 1;
+	      break;
+	    case AB_TARGET:
+	      attr->target = 1;
+	      break;
+	    case AB_THREADPRIVATE:
+	      attr->threadprivate = 1;
+	      break;
+	    case AB_DUMMY:
+	      attr->dummy = 1;
+	      break;
+	    case AB_RESULT:
+	      attr->result = 1;
+	      break;
+	    case AB_DATA:
+	      attr->data = 1;
+	      break;
+	    case AB_IN_NAMELIST:
+	      attr->in_namelist = 1;
+	      break;
+	    case AB_IN_COMMON:
+	      attr->in_common = 1;
+	      break;
+	    case AB_FUNCTION:
+	      attr->function = 1;
+	      break;
+	    case AB_SUBROUTINE:
+	      attr->subroutine = 1;
+	      break;
+	    case AB_GENERIC:
+	      attr->generic = 1;
+	      break;
+	    case AB_ABSTRACT:
+	      attr->abstract = 1;
+	      break;
+	    case AB_SEQUENCE:
+	      attr->sequence = 1;
+	      break;
+	    case AB_ELEMENTAL:
+	      attr->elemental = 1;
+	      break;
+	    case AB_PURE:
+	      attr->pure = 1;
+	      break;
+	    case AB_IMPLICIT_PURE:
+	      attr->implicit_pure = 1;
+	      break;
+	    case AB_UNLIMITED_POLY:
+	      attr->unlimited_polymorphic = 1;
+	      break;
+	    case AB_RECURSIVE:
+	      attr->recursive = 1;
+	      break;
+	    case AB_ALWAYS_EXPLICIT:
+	      attr->always_explicit = 1;
+	      break;
+	    case AB_CRAY_POINTER:
+	      attr->cray_pointer = 1;
+	      break;
+	    case AB_CRAY_POINTEE:
+	      attr->cray_pointee = 1;
+	      break;
+	    case AB_IS_BIND_C:
+	      attr->is_bind_c = 1;
+	      break;
+	    case AB_IS_C_INTEROP:
+	      attr->is_c_interop = 1;
+	      break;
+	    case AB_IS_ISO_C:
+	      attr->is_iso_c = 1;
+	      break;
+	    case AB_ALLOC_COMP:
+	      attr->alloc_comp = 1;
+	      break;
+	    case AB_COARRAY_COMP:
+	      attr->coarray_comp = 1;
+	      break;
+	    case AB_LOCK_COMP:
+	      attr->lock_comp = 1;
+	      break;
+	    case AB_EVENT_COMP:
+	      attr->event_comp = 1;
+	      break;
+	    case AB_POINTER_COMP:
+	      attr->pointer_comp = 1;
+	      break;
+	    case AB_PROC_POINTER_COMP:
+	      attr->proc_pointer_comp = 1;
+	      break;
+	    case AB_PRIVATE_COMP:
+	      attr->private_comp = 1;
+	      break;
+	    case AB_ZERO_COMP:
+	      attr->zero_comp = 1;
+	      break;
+	    case AB_IS_CLASS:
+	      attr->is_class = 1;
+	      break;
+	    case AB_PROCEDURE:
+	      attr->procedure = 1;
+	      break;
+	    case AB_PROC_POINTER:
+	      attr->proc_pointer = 1;
+	      break;
+	    case AB_VTYPE:
+	      attr->vtype = 1;
+	      break;
+	    case AB_VTAB:
+	      attr->vtab = 1;
+	      break;
+	    case AB_OMP_DECLARE_TARGET:
+	      attr->omp_declare_target = 1;
+	      break;
+	    case AB_OMP_DECLARE_TARGET_LINK:
+	      attr->omp_declare_target_link = 1;
+	      break;
+	    case AB_ARRAY_OUTER_DEPENDENCY:
+	      attr->array_outer_dependency =1;
+	      break;
+	    case AB_MODULE_PROCEDURE:
+	      attr->module_procedure =1;
+	      break;
+	    case AB_OACC_DECLARE_CREATE:
+	      attr->oacc_declare_create = 1;
+	      break;
+	    case AB_OACC_DECLARE_COPYIN:
+	      attr->oacc_declare_copyin = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICEPTR:
+	      attr->oacc_declare_deviceptr = 1;
+	      break;
+	    case AB_OACC_DECLARE_DEVICE_RESIDENT:
+	      attr->oacc_declare_device_resident = 1;
+	      break;
+	    case AB_OACC_DECLARE_LINK:
+	      attr->oacc_declare_link = 1;
+	      break;
+	    case AB_PDT_KIND:
+	      attr->pdt_kind = 1;
+	      break;
+	    case AB_PDT_LEN:
+	      attr->pdt_len = 1;
+	      break;
+	    case AB_PDT_TYPE:
+	      attr->pdt_type = 1;
+	      break;
+	    case AB_PDT_TEMPLATE:
+	      attr->pdt_template = 1;
+	      break;
+	    case AB_PDT_ARRAY:
+	      attr->pdt_array = 1;
+	      break;
+	    case AB_PDT_STRING:
+	      attr->pdt_string = 1;
+	      break;
+	    }
+	}
+    }
+}
+
+
+static const mstring bt_types[] = {
+    minit ("INTEGER", BT_INTEGER),
+    minit ("REAL", BT_REAL),
+    minit ("COMPLEX", BT_COMPLEX),
+    minit ("LOGICAL", BT_LOGICAL),
+    minit ("CHARACTER", BT_CHARACTER),
+    minit ("UNION", BT_UNION),
+    minit ("DERIVED", BT_DERIVED),
+    minit ("CLASS", BT_CLASS),
+    minit ("PROCEDURE", BT_PROCEDURE),
+    minit ("UNKNOWN", BT_UNKNOWN),
+    minit ("VOID", BT_VOID),
+    minit ("ASSUMED", BT_ASSUMED),
+    minit (NULL, -1)
+};
+
+
+static void
+mio_charlen (gfc_charlen **clp)
+{
+  gfc_charlen *cl;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      cl = *clp;
+      if (cl != NULL)
+	mio_expr (&cl->length);
+    }
+  else
+    {
+      if (peek_atom () != ATOM_RPAREN)
+	{
+	  cl = gfc_new_charlen (gfc_current_ns, NULL);
+	  mio_expr (&cl->length);
+	  *clp = cl;
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+/* See if a name is a generated name.  */
+
+static int
+check_unique_name (const char *name)
+{
+  return *name == '@';
+}
+
+
+static void
+mio_typespec (gfc_typespec *ts)
+{
+  mio_lparen ();
+
+  ts->type = MIO_NAME (bt) (ts->type, bt_types);
+
+  if (!gfc_bt_struct (ts->type) && ts->type != BT_CLASS)
+    mio_integer (&ts->kind);
+  else
+    mio_symbol_ref (&ts->u.derived);
+
+  mio_symbol_ref (&ts->interface);
+
+  /* Add info for C interop and is_iso_c.  */
+  mio_integer (&ts->is_c_interop);
+  mio_integer (&ts->is_iso_c);
+
+  /* If the typespec is for an identifier either from iso_c_binding, or
+     a constant that was initialized to an identifier from it, use the
+     f90_type.  Otherwise, use the ts->type, since it shouldn't matter.  */
+  if (ts->is_iso_c)
+    ts->f90_type = MIO_NAME (bt) (ts->f90_type, bt_types);
+  else
+    ts->f90_type = MIO_NAME (bt) (ts->type, bt_types);
+
+  if (ts->type != BT_CHARACTER)
+    {
+      /* ts->u.cl is only valid for BT_CHARACTER.  */
+      mio_lparen ();
+      mio_rparen ();
+    }
+  else
+    mio_charlen (&ts->u.cl);
+
+  /* So as not to disturb the existing API, use an ATOM_NAME to
+     transmit deferred characteristic for characters (F2003).  */
+  if (iomode == IO_OUTPUT)
+    {
+      if (ts->type == BT_CHARACTER && ts->deferred)
+	write_atom (ATOM_NAME, "DEFERRED_CL");
+    }
+  else if (peek_atom () != ATOM_RPAREN)
+    {
+      if (parse_atom () != ATOM_NAME)
+	bad_module ("Expected string");
+      ts->deferred = 1;
+    }
+
+  mio_rparen ();
+}
+
+
+static const mstring array_spec_types[] = {
+    minit ("EXPLICIT", AS_EXPLICIT),
+    minit ("ASSUMED_RANK", AS_ASSUMED_RANK),
+    minit ("ASSUMED_SHAPE", AS_ASSUMED_SHAPE),
+    minit ("DEFERRED", AS_DEFERRED),
+    minit ("ASSUMED_SIZE", AS_ASSUMED_SIZE),
+    minit (NULL, -1)
+};
+
+
+static void
+mio_array_spec (gfc_array_spec **asp)
+{
+  gfc_array_spec *as;
+  int i;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      int rank;
+
+      if (*asp == NULL)
+	goto done;
+      as = *asp;
+
+      /* mio_integer expects nonnegative values.  */
+      rank = as->rank > 0 ? as->rank : 0;
+      mio_integer (&rank);
+    }
+  else
+    {
+      if (peek_atom () == ATOM_RPAREN)
+	{
+	  *asp = NULL;
+	  goto done;
+	}
+
+      *asp = as = gfc_get_array_spec ();
+      mio_integer (&as->rank);
+    }
+
+  mio_integer (&as->corank);
+  as->type = MIO_NAME (array_type) (as->type, array_spec_types);
+
+  if (iomode == IO_INPUT && as->type == AS_ASSUMED_RANK)
+    as->rank = -1;
+  if (iomode == IO_INPUT && as->corank)
+    as->cotype = (as->type == AS_DEFERRED) ? AS_DEFERRED : AS_EXPLICIT;
+
+  if (as->rank + as->corank > 0)
+    for (i = 0; i < as->rank + as->corank; i++)
+      {
+	mio_expr (&as->lower[i]);
+	mio_expr (&as->upper[i]);
+      }
+
+done:
+  mio_rparen ();
+}
+
+
+/* Given a pointer to an array reference structure (which lives in a
+   gfc_ref structure), find the corresponding array specification
+   structure.  Storing the pointer in the ref structure doesn't quite
+   work when loading from a module. Generating code for an array
+   reference also needs more information than just the array spec.  */
+
+static const mstring array_ref_types[] = {
+    minit ("FULL", AR_FULL),
+    minit ("ELEMENT", AR_ELEMENT),
+    minit ("SECTION", AR_SECTION),
+    minit (NULL, -1)
+};
+
+
+static void
+mio_array_ref (gfc_array_ref *ar)
+{
+  int i;
+
+  mio_lparen ();
+  ar->type = MIO_NAME (ar_type) (ar->type, array_ref_types);
+  mio_integer (&ar->dimen);
+
+  switch (ar->type)
+    {
+    case AR_FULL:
+      break;
+
+    case AR_ELEMENT:
+      for (i = 0; i < ar->dimen; i++)
+	mio_expr (&ar->start[i]);
+
+      break;
+
+    case AR_SECTION:
+      for (i = 0; i < ar->dimen; i++)
+	{
+	  mio_expr (&ar->start[i]);
+	  mio_expr (&ar->end[i]);
+	  mio_expr (&ar->stride[i]);
+	}
+
+      break;
+
+    case AR_UNKNOWN:
+      gfc_internal_error ("mio_array_ref(): Unknown array ref");
+    }
+
+  /* Unfortunately, ar->dimen_type is an anonymous enumerated type so
+     we can't call mio_integer directly.  Instead loop over each element
+     and cast it to/from an integer.  */
+  if (iomode == IO_OUTPUT)
+    {
+      for (i = 0; i < ar->dimen; i++)
+	{
+	  int tmp = (int)ar->dimen_type[i];
+	  write_atom (ATOM_INTEGER, &tmp);
+	}
+    }
+  else
+    {
+      for (i = 0; i < ar->dimen; i++)
+	{
+	  require_atom (ATOM_INTEGER);
+	  ar->dimen_type[i] = (enum gfc_array_ref_dimen_type) atom_int;
+	}
+    }
+
+  if (iomode == IO_INPUT)
+    {
+      ar->where = gfc_current_locus;
+
+      for (i = 0; i < ar->dimen; i++)
+	ar->c_where[i] = gfc_current_locus;
+    }
+
+  mio_rparen ();
+}
+
+
+/* Saves or restores a pointer.  The pointer is converted back and
+   forth from an integer.  We return the pointer_info pointer so that
+   the caller can take additional action based on the pointer type.  */
+
+static pointer_info *
+mio_pointer_ref (void *gp)
+{
+  pointer_info *p;
+
+  if (iomode == IO_OUTPUT)
+    {
+      p = get_pointer (*((char **) gp));
+      write_atom (ATOM_INTEGER, &p->integer);
+    }
+  else
+    {
+      require_atom (ATOM_INTEGER);
+      p = add_fixup (atom_int, gp);
+    }
+
+  return p;
+}
+
+
+/* Save and load references to components that occur within
+   expressions.  We have to describe these references by a number and
+   by name.  The number is necessary for forward references during
+   reading, and the name is necessary if the symbol already exists in
+   the namespace and is not loaded again.  */
+
+static void
+mio_component_ref (gfc_component **cp)
+{
+  pointer_info *p;
+
+  p = mio_pointer_ref (cp);
+  if (p->type == P_UNKNOWN)
+    p->type = P_COMPONENT;
+}
+
+
+static void mio_namespace_ref (gfc_namespace **nsp);
+static void mio_formal_arglist (gfc_formal_arglist **formal);
+static void mio_typebound_proc (gfc_typebound_proc** proc);
+static void mio_actual_arglist (gfc_actual_arglist **ap, bool pdt);
+
+static void
+mio_component (gfc_component *c, int vtype)
+{
+  pointer_info *p;
+  int n;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      p = get_pointer (c);
+      mio_integer (&p->integer);
+    }
+  else
+    {
+      mio_integer (&n);
+      p = get_integer (n);
+      associate_integer_pointer (p, c);
+    }
+
+  if (p->type == P_UNKNOWN)
+    p->type = P_COMPONENT;
+
+  mio_pool_string (&c->name);
+  mio_typespec (&c->ts);
+  mio_array_spec (&c->as);
+
+  /* PDT templates store the expression for the kind of a component here.  */
+  mio_expr (&c->kind_expr);
+
+  /* PDT types store the component specification list here. */
+  mio_actual_arglist (&c->param_list, true);
+
+  mio_symbol_attribute (&c->attr);
+  if (c->ts.type == BT_CLASS)
+    c->attr.class_ok = 1;
+  c->attr.access = MIO_NAME (gfc_access) (c->attr.access, access_types);
+
+  if (!vtype || strcmp (c->name, "_final") == 0
+      || strcmp (c->name, "_hash") == 0)
+    mio_expr (&c->initializer);
+
+  if (c->attr.proc_pointer)
+    mio_typebound_proc (&c->tb);
+
+  mio_rparen ();
+}
+
+
+static void
+mio_component_list (gfc_component **cp, int vtype)
+{
+  gfc_component *c, *tail;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      for (c = *cp; c; c = c->next)
+	mio_component (c, vtype);
+    }
+  else
+    {
+      *cp = NULL;
+      tail = NULL;
+
+      for (;;)
+	{
+	  if (peek_atom () == ATOM_RPAREN)
+	    break;
+
+	  c = gfc_get_component ();
+	  mio_component (c, vtype);
+
+	  if (tail == NULL)
+	    *cp = c;
+	  else
+	    tail->next = c;
+
+	  tail = c;
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+static void
+mio_actual_arg (gfc_actual_arglist *a, bool pdt)
+{
+  mio_lparen ();
+  mio_pool_string (&a->name);
+  mio_expr (&a->expr);
+  if (pdt)
+    mio_integer ((int *)&a->spec_type);
+  mio_rparen ();
+}
+
+
+static void
+mio_actual_arglist (gfc_actual_arglist **ap, bool pdt)
+{
+  gfc_actual_arglist *a, *tail;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      for (a = *ap; a; a = a->next)
+	mio_actual_arg (a, pdt);
+
+    }
+  else
+    {
+      tail = NULL;
+
+      for (;;)
+	{
+	  if (peek_atom () != ATOM_LPAREN)
+	    break;
+
+	  a = gfc_get_actual_arglist ();
+
+	  if (tail == NULL)
+	    *ap = a;
+	  else
+	    tail->next = a;
+
+	  tail = a;
+	  mio_actual_arg (a, pdt);
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+/* Read and write formal argument lists.  */
+
+static void
+mio_formal_arglist (gfc_formal_arglist **formal)
+{
+  gfc_formal_arglist *f, *tail;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      for (f = *formal; f; f = f->next)
+	mio_symbol_ref (&f->sym);
+    }
+  else
+    {
+      *formal = tail = NULL;
+
+      while (peek_atom () != ATOM_RPAREN)
+	{
+	  f = gfc_get_formal_arglist ();
+	  mio_symbol_ref (&f->sym);
+
+	  if (*formal == NULL)
+	    *formal = f;
+	  else
+	    tail->next = f;
+
+	  tail = f;
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+/* Save or restore a reference to a symbol node.  */
+
+pointer_info *
+mio_symbol_ref (gfc_symbol **symp)
+{
+  pointer_info *p;
+
+  p = mio_pointer_ref (symp);
+  if (p->type == P_UNKNOWN)
+    p->type = P_SYMBOL;
+
+  if (iomode == IO_OUTPUT)
+    {
+      if (p->u.wsym.state == UNREFERENCED)
+	p->u.wsym.state = NEEDS_WRITE;
+    }
+  else
+    {
+      if (p->u.rsym.state == UNUSED)
+	p->u.rsym.state = NEEDED;
+    }
+  return p;
+}
+
+
+/* Save or restore a reference to a symtree node.  */
+
+static void
+mio_symtree_ref (gfc_symtree **stp)
+{
+  pointer_info *p;
+  fixup_t *f;
+
+  if (iomode == IO_OUTPUT)
+    mio_symbol_ref (&(*stp)->n.sym);
+  else
+    {
+      require_atom (ATOM_INTEGER);
+      p = get_integer (atom_int);
+
+      /* An unused equivalence member; make a symbol and a symtree
+	 for it.  */
+      if (in_load_equiv && p->u.rsym.symtree == NULL)
+	{
+	  /* Since this is not used, it must have a unique name.  */
+	  p->u.rsym.symtree = gfc_get_unique_symtree (gfc_current_ns);
+
+	  /* Make the symbol.  */
+	  if (p->u.rsym.sym == NULL)
+	    {
+	      p->u.rsym.sym = gfc_new_symbol (p->u.rsym.true_name,
+					      gfc_current_ns);
+	      p->u.rsym.sym->module = gfc_get_string ("%s", p->u.rsym.module);
+	    }
+
+	  p->u.rsym.symtree->n.sym = p->u.rsym.sym;
+	  p->u.rsym.symtree->n.sym->refs++;
+	  p->u.rsym.referenced = 1;
+
+	  /* If the symbol is PRIVATE and in COMMON, load_commons will
+	     generate a fixup symbol, which must be associated.  */
+	  if (p->fixup)
+	    resolve_fixups (p->fixup, p->u.rsym.sym);
+	  p->fixup = NULL;
+	}
+
+      if (p->type == P_UNKNOWN)
+	p->type = P_SYMBOL;
+
+      if (p->u.rsym.state == UNUSED)
+	p->u.rsym.state = NEEDED;
+
+      if (p->u.rsym.symtree != NULL)
+	{
+	  *stp = p->u.rsym.symtree;
+	}
+      else
+	{
+	  f = XCNEW (fixup_t);
+
+	  f->next = p->u.rsym.stfixup;
+	  p->u.rsym.stfixup = f;
+
+	  f->pointer = (void **) stp;
+	}
+    }
+}
+
+
+static void
+mio_iterator (gfc_iterator **ip)
+{
+  gfc_iterator *iter;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      if (*ip == NULL)
+	goto done;
+    }
+  else
+    {
+      if (peek_atom () == ATOM_RPAREN)
+	{
+	  *ip = NULL;
+	  goto done;
+	}
+
+      *ip = gfc_get_iterator ();
+    }
+
+  iter = *ip;
+
+  mio_expr (&iter->var);
+  mio_expr (&iter->start);
+  mio_expr (&iter->end);
+  mio_expr (&iter->step);
+
+done:
+  mio_rparen ();
+}
+
+
+static void
+mio_constructor (gfc_constructor_base *cp)
+{
+  gfc_constructor *c;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      for (c = gfc_constructor_first (*cp); c; c = gfc_constructor_next (c))
+	{
+	  mio_lparen ();
+	  mio_expr (&c->expr);
+	  mio_iterator (&c->iterator);
+	  mio_rparen ();
+	}
+    }
+  else
+    {
+      while (peek_atom () != ATOM_RPAREN)
+	{
+	  c = gfc_constructor_append_expr (cp, NULL, NULL);
+
+	  mio_lparen ();
+	  mio_expr (&c->expr);
+	  mio_iterator (&c->iterator);
+	  mio_rparen ();
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+static const mstring ref_types[] = {
+    minit ("ARRAY", REF_ARRAY),
+    minit ("COMPONENT", REF_COMPONENT),
+    minit ("SUBSTRING", REF_SUBSTRING),
+    minit (NULL, -1)
+};
+
+
+static void
+mio_ref (gfc_ref **rp)
+{
+  gfc_ref *r;
+
+  mio_lparen ();
+
+  r = *rp;
+  r->type = MIO_NAME (ref_type) (r->type, ref_types);
+
+  switch (r->type)
+    {
+    case REF_ARRAY:
+      mio_array_ref (&r->u.ar);
+      break;
+
+    case REF_COMPONENT:
+      mio_symbol_ref (&r->u.c.sym);
+      mio_component_ref (&r->u.c.component);
+      break;
+
+    case REF_SUBSTRING:
+      mio_expr (&r->u.ss.start);
+      mio_expr (&r->u.ss.end);
+      mio_charlen (&r->u.ss.length);
+      break;
+    }
+
+  mio_rparen ();
+}
+
+
+static void
+mio_ref_list (gfc_ref **rp)
+{
+  gfc_ref *ref, *head, *tail;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      for (ref = *rp; ref; ref = ref->next)
+	mio_ref (&ref);
+    }
+  else
+    {
+      head = tail = NULL;
+
+      while (peek_atom () != ATOM_RPAREN)
+	{
+	  if (head == NULL)
+	    head = tail = gfc_get_ref ();
+	  else
+	    {
+	      tail->next = gfc_get_ref ();
+	      tail = tail->next;
+	    }
+
+	  mio_ref (&tail);
+	}
+
+      *rp = head;
+    }
+
+  mio_rparen ();
+}
+
+
+/* Read and write an integer value.  */
+
+static void
+mio_gmp_integer (mpz_t *integer)
+{
+  char *p;
+
+  if (iomode == IO_INPUT)
+    {
+      if (parse_atom () != ATOM_STRING)
+	bad_module ("Expected integer string");
+
+      mpz_init (*integer);
+      if (mpz_set_str (*integer, atom_string, 10))
+	bad_module ("Error converting integer");
+
+      free (atom_string);
+    }
+  else
+    {
+      p = mpz_get_str (NULL, 10, *integer);
+      write_atom (ATOM_STRING, p);
+      free (p);
+    }
+}
+
+
+static void
+mio_gmp_real (mpfr_t *real)
+{
+  mp_exp_t exponent;
+  char *p;
+
+  if (iomode == IO_INPUT)
+    {
+      if (parse_atom () != ATOM_STRING)
+	bad_module ("Expected real string");
+
+      mpfr_init (*real);
+      mpfr_set_str (*real, atom_string, 16, GFC_RND_MODE);
+      free (atom_string);
+    }
+  else
+    {
+      p = mpfr_get_str (NULL, &exponent, 16, 0, *real, GFC_RND_MODE);
+
+      if (mpfr_nan_p (*real) || mpfr_inf_p (*real))
+	{
+	  write_atom (ATOM_STRING, p);
+	  free (p);
+	  return;
+	}
+
+      atom_string = XCNEWVEC (char, strlen (p) + 20);
+
+      sprintf (atom_string, "0.%s@%ld", p, exponent);
+
+      /* Fix negative numbers.  */
+      if (atom_string[2] == '-')
+	{
+	  atom_string[0] = '-';
+	  atom_string[1] = '0';
+	  atom_string[2] = '.';
+	}
+
+      write_atom (ATOM_STRING, atom_string);
+
+      free (atom_string);
+      free (p);
+    }
+}
+
+
+/* Save and restore the shape of an array constructor.  */
+
+static void
+mio_shape (mpz_t **pshape, int rank)
+{
+  mpz_t *shape;
+  atom_type t;
+  int n;
+
+  /* A NULL shape is represented by ().  */
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      shape = *pshape;
+      if (!shape)
+	{
+	  mio_rparen ();
+	  return;
+	}
+    }
+  else
+    {
+      t = peek_atom ();
+      if (t == ATOM_RPAREN)
+	{
+	  *pshape = NULL;
+	  mio_rparen ();
+	  return;
+	}
+
+      shape = gfc_get_shape (rank);
+      *pshape = shape;
+    }
+
+  for (n = 0; n < rank; n++)
+    mio_gmp_integer (&shape[n]);
+
+  mio_rparen ();
+}
+
+
+static const mstring expr_types[] = {
+    minit ("OP", EXPR_OP),
+    minit ("FUNCTION", EXPR_FUNCTION),
+    minit ("CONSTANT", EXPR_CONSTANT),
+    minit ("VARIABLE", EXPR_VARIABLE),
+    minit ("SUBSTRING", EXPR_SUBSTRING),
+    minit ("STRUCTURE", EXPR_STRUCTURE),
+    minit ("ARRAY", EXPR_ARRAY),
+    minit ("NULL", EXPR_NULL),
+    minit ("COMPCALL", EXPR_COMPCALL),
+    minit (NULL, -1)
+};
+
+/* INTRINSIC_ASSIGN is missing because it is used as an index for
+   generic operators, not in expressions.  INTRINSIC_USER is also
+   replaced by the correct function name by the time we see it.  */
+
+static const mstring intrinsics[] =
+{
+    minit ("UPLUS", INTRINSIC_UPLUS),
+    minit ("UMINUS", INTRINSIC_UMINUS),
+    minit ("PLUS", INTRINSIC_PLUS),
+    minit ("MINUS", INTRINSIC_MINUS),
+    minit ("TIMES", INTRINSIC_TIMES),
+    minit ("DIVIDE", INTRINSIC_DIVIDE),
+    minit ("POWER", INTRINSIC_POWER),
+    minit ("CONCAT", INTRINSIC_CONCAT),
+    minit ("AND", INTRINSIC_AND),
+    minit ("OR", INTRINSIC_OR),
+    minit ("EQV", INTRINSIC_EQV),
+    minit ("NEQV", INTRINSIC_NEQV),
+    minit ("EQ_SIGN", INTRINSIC_EQ),
+    minit ("EQ", INTRINSIC_EQ_OS),
+    minit ("NE_SIGN", INTRINSIC_NE),
+    minit ("NE", INTRINSIC_NE_OS),
+    minit ("GT_SIGN", INTRINSIC_GT),
+    minit ("GT", INTRINSIC_GT_OS),
+    minit ("GE_SIGN", INTRINSIC_GE),
+    minit ("GE", INTRINSIC_GE_OS),
+    minit ("LT_SIGN", INTRINSIC_LT),
+    minit ("LT", INTRINSIC_LT_OS),
+    minit ("LE_SIGN", INTRINSIC_LE),
+    minit ("LE", INTRINSIC_LE_OS),
+    minit ("NOT", INTRINSIC_NOT),
+    minit ("PARENTHESES", INTRINSIC_PARENTHESES),
+    minit ("USER", INTRINSIC_USER),
+    minit (NULL, -1)
+};
+
+
+/* Remedy a couple of situations where the gfc_expr's can be defective.  */
+
+static void
+fix_mio_expr (gfc_expr *e)
+{
+  gfc_symtree *ns_st = NULL;
+  const char *fname;
+
+  if (iomode != IO_OUTPUT)
+    return;
+
+  if (e->symtree)
+    {
+      /* If this is a symtree for a symbol that came from a contained module
+	 namespace, it has a unique name and we should look in the current
+	 namespace to see if the required, non-contained symbol is available
+	 yet. If so, the latter should be written.  */
+      if (e->symtree->n.sym && check_unique_name (e->symtree->name))
+	{
+          const char *name = e->symtree->n.sym->name;
+	  if (gfc_fl_struct (e->symtree->n.sym->attr.flavor))
+	    name = gfc_dt_upper_string (name);
+	  ns_st = gfc_find_symtree (gfc_current_ns->sym_root, name);
+	}
+
+      /* On the other hand, if the existing symbol is the module name or the
+	 new symbol is a dummy argument, do not do the promotion.  */
+      if (ns_st && ns_st->n.sym
+	  && ns_st->n.sym->attr.flavor != FL_MODULE
+	  && !e->symtree->n.sym->attr.dummy)
+	e->symtree = ns_st;
+    }
+  else if (e->expr_type == EXPR_FUNCTION
+	   && (e->value.function.name || e->value.function.isym))
+    {
+      gfc_symbol *sym;
+
+      /* In some circumstances, a function used in an initialization
+	 expression, in one use associated module, can fail to be
+	 coupled to its symtree when used in a specification
+	 expression in another module.  */
+      fname = e->value.function.esym ? e->value.function.esym->name
+				     : e->value.function.isym->name;
+      e->symtree = gfc_find_symtree (gfc_current_ns->sym_root, fname);
+
+      if (e->symtree)
+	return;
+
+      /* This is probably a reference to a private procedure from another
+	 module.  To prevent a segfault, make a generic with no specific
+	 instances.  If this module is used, without the required
+	 specific coming from somewhere, the appropriate error message
+	 is issued.  */
+      gfc_get_symbol (fname, gfc_current_ns, &sym);
+      sym->attr.flavor = FL_PROCEDURE;
+      sym->attr.generic = 1;
+      e->symtree = gfc_find_symtree (gfc_current_ns->sym_root, fname);
+      gfc_commit_symbol (sym);
+    }
+}
+
+
+/* Read and write expressions.  The form "()" is allowed to indicate a
+   NULL expression.  */
+
+static void
+mio_expr (gfc_expr **ep)
+{
+  gfc_expr *e;
+  atom_type t;
+  int flag;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      if (*ep == NULL)
+	{
+	  mio_rparen ();
+	  return;
+	}
+
+      e = *ep;
+      MIO_NAME (expr_t) (e->expr_type, expr_types);
+    }
+  else
+    {
+      t = parse_atom ();
+      if (t == ATOM_RPAREN)
+	{
+	  *ep = NULL;
+	  return;
+	}
+
+      if (t != ATOM_NAME)
+	bad_module ("Expected expression type");
+
+      e = *ep = gfc_get_expr ();
+      e->where = gfc_current_locus;
+      e->expr_type = (expr_t) find_enum (expr_types);
+    }
+
+  mio_typespec (&e->ts);
+  mio_integer (&e->rank);
+
+  fix_mio_expr (e);
+
+  switch (e->expr_type)
+    {
+    case EXPR_OP:
+      e->value.op.op
+	= MIO_NAME (gfc_intrinsic_op) (e->value.op.op, intrinsics);
+
+      switch (e->value.op.op)
+	{
+	case INTRINSIC_UPLUS:
+	case INTRINSIC_UMINUS:
+	case INTRINSIC_NOT:
+	case INTRINSIC_PARENTHESES:
+	  mio_expr (&e->value.op.op1);
+	  break;
+
+	case INTRINSIC_PLUS:
+	case INTRINSIC_MINUS:
+	case INTRINSIC_TIMES:
+	case INTRINSIC_DIVIDE:
+	case INTRINSIC_POWER:
+	case INTRINSIC_CONCAT:
+	case INTRINSIC_AND:
+	case INTRINSIC_OR:
+	case INTRINSIC_EQV:
+	case INTRINSIC_NEQV:
+	case INTRINSIC_EQ:
+	case INTRINSIC_EQ_OS:
+	case INTRINSIC_NE:
+	case INTRINSIC_NE_OS:
+	case INTRINSIC_GT:
+	case INTRINSIC_GT_OS:
+	case INTRINSIC_GE:
+	case INTRINSIC_GE_OS:
+	case INTRINSIC_LT:
+	case INTRINSIC_LT_OS:
+	case INTRINSIC_LE:
+	case INTRINSIC_LE_OS:
+	  mio_expr (&e->value.op.op1);
+	  mio_expr (&e->value.op.op2);
+	  break;
+
+	case INTRINSIC_USER:
+	  /* INTRINSIC_USER should not appear in resolved expressions,
+	     though for UDRs we need to stream unresolved ones.  */
+	  if (iomode == IO_OUTPUT)
+	    write_atom (ATOM_STRING, e->value.op.uop->name);
+	  else
+	    {
+	      char *name = read_string ();
+	      const char *uop_name = find_use_name (name, true);
+	      if (uop_name == NULL)
+		{
+		  size_t len = strlen (name);
+		  char *name2 = XCNEWVEC (char, len + 2);
+		  memcpy (name2, name, len);
+		  name2[len] = ' ';
+		  name2[len + 1] = '\0';
+		  free (name);
+		  uop_name = name = name2;
+		}
+	      e->value.op.uop = gfc_get_uop (uop_name);
+	      free (name);
+	    }
+	  mio_expr (&e->value.op.op1);
+	  mio_expr (&e->value.op.op2);
+	  break;
+
+	default:
+	  bad_module ("Bad operator");
+	}
+
+      break;
+
+    case EXPR_FUNCTION:
+      mio_symtree_ref (&e->symtree);
+      mio_actual_arglist (&e->value.function.actual, false);
+
+      if (iomode == IO_OUTPUT)
+	{
+	  e->value.function.name
+	    = mio_allocated_string (e->value.function.name);
+	  if (e->value.function.esym)
+	    flag = 1;
+	  else if (e->ref)
+	    flag = 2;
+	  else if (e->value.function.isym == NULL)
+	    flag = 3;
+	  else
+	    flag = 0;
+	  mio_integer (&flag);
+	  switch (flag)
+	    {
+	    case 1:
+	      mio_symbol_ref (&e->value.function.esym);
+	      break;
+	    case 2:
+	      mio_ref_list (&e->ref);
+	      break;
+	    case 3:
+	      break;
+	    default:
+	      write_atom (ATOM_STRING, e->value.function.isym->name);
+	    }
+	}
+      else
+	{
+	  require_atom (ATOM_STRING);
+	  if (atom_string[0] == '\0')
+	    e->value.function.name = NULL;
+	  else
+	    e->value.function.name = gfc_get_string ("%s", atom_string);
+	  free (atom_string);
+
+	  mio_integer (&flag);
+	  switch (flag)
+	    {
+	    case 1:
+	      mio_symbol_ref (&e->value.function.esym);
+	      break;
+	    case 2:
+	      mio_ref_list (&e->ref);
+	      break;
+	    case 3:
+	      break;
+	    default:
+	      require_atom (ATOM_STRING);
+	      e->value.function.isym = gfc_find_function (atom_string);
+	      free (atom_string);
+	    }
+	}
+
+      break;
+
+    case EXPR_VARIABLE:
+      mio_symtree_ref (&e->symtree);
+      mio_ref_list (&e->ref);
+      break;
+
+    case EXPR_SUBSTRING:
+      e->value.character.string
+	= CONST_CAST (gfc_char_t *,
+		      mio_allocated_wide_string (e->value.character.string,
+						 e->value.character.length));
+      mio_ref_list (&e->ref);
+      break;
+
+    case EXPR_STRUCTURE:
+    case EXPR_ARRAY:
+      mio_constructor (&e->value.constructor);
+      mio_shape (&e->shape, e->rank);
+      break;
+
+    case EXPR_CONSTANT:
+      switch (e->ts.type)
+	{
+	case BT_INTEGER:
+	  mio_gmp_integer (&e->value.integer);
+	  break;
+
+	case BT_REAL:
+	  gfc_set_model_kind (e->ts.kind);
+	  mio_gmp_real (&e->value.real);
+	  break;
+
+	case BT_COMPLEX:
+	  gfc_set_model_kind (e->ts.kind);
+	  mio_gmp_real (&mpc_realref (e->value.complex));
+	  mio_gmp_real (&mpc_imagref (e->value.complex));
+	  break;
+
+	case BT_LOGICAL:
+	  mio_integer (&e->value.logical);
+	  break;
+
+	case BT_CHARACTER:
+	  mio_integer (&e->value.character.length);
+	  e->value.character.string
+	    = CONST_CAST (gfc_char_t *,
+			  mio_allocated_wide_string (e->value.character.string,
+						     e->value.character.length));
+	  break;
+
+	default:
+	  bad_module ("Bad type in constant expression");
+	}
+
+      break;
+
+    case EXPR_NULL:
+      break;
+
+    case EXPR_COMPCALL:
+    case EXPR_PPC:
+      gcc_unreachable ();
+      break;
+    }
+
+  /* PDT types store the expression specification list here. */
+  mio_actual_arglist (&e->param_list, true);
+
+  mio_rparen ();
+}
+
+
+/* Read and write namelists.  */
+
+static void
+mio_namelist (gfc_symbol *sym)
+{
+  gfc_namelist *n, *m;
+  const char *check_name;
+
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      for (n = sym->namelist; n; n = n->next)
+	mio_symbol_ref (&n->sym);
+    }
+  else
+    {
+      /* This departure from the standard is flagged as an error.
+	 It does, in fact, work correctly. TODO: Allow it
+	 conditionally?  */
+      if (sym->attr.flavor == FL_NAMELIST)
+	{
+	  check_name = find_use_name (sym->name, false);
+	  if (check_name && strcmp (check_name, sym->name) != 0)
+	    gfc_error ("Namelist %s cannot be renamed by USE "
+		       "association to %s", sym->name, check_name);
+	}
+
+      m = NULL;
+      while (peek_atom () != ATOM_RPAREN)
+	{
+	  n = gfc_get_namelist ();
+	  mio_symbol_ref (&n->sym);
+
+	  if (sym->namelist == NULL)
+	    sym->namelist = n;
+	  else
+	    m->next = n;
+
+	  m = n;
+	}
+      sym->namelist_tail = m;
+    }
+
+  mio_rparen ();
+}
+
+
+/* Save/restore lists of gfc_interface structures.  When loading an
+   interface, we are really appending to the existing list of
+   interfaces.  Checking for duplicate and ambiguous interfaces has to
+   be done later when all symbols have been loaded.  */
+
+pointer_info *
+mio_interface_rest (gfc_interface **ip)
+{
+  gfc_interface *tail, *p;
+  pointer_info *pi = NULL;
+
+  if (iomode == IO_OUTPUT)
+    {
+      if (ip != NULL)
+	for (p = *ip; p; p = p->next)
+	  mio_symbol_ref (&p->sym);
+    }
+  else
+    {
+      if (*ip == NULL)
+	tail = NULL;
+      else
+	{
+	  tail = *ip;
+	  while (tail->next)
+	    tail = tail->next;
+	}
+
+      for (;;)
+	{
+	  if (peek_atom () == ATOM_RPAREN)
+	    break;
+
+	  p = gfc_get_interface ();
+	  p->where = gfc_current_locus;
+	  pi = mio_symbol_ref (&p->sym);
+
+	  if (tail == NULL)
+	    *ip = p;
+	  else
+	    tail->next = p;
+
+	  tail = p;
+	}
+    }
+
+  mio_rparen ();
+  return pi;
+}
+
+
+/* Save/restore a nameless operator interface.  */
+
+static void
+mio_interface (gfc_interface **ip)
+{
+  mio_lparen ();
+  mio_interface_rest (ip);
+}
+
+
+/* Save/restore a named operator interface.  */
+
+static void
+mio_symbol_interface (const char **name, const char **module,
+		      gfc_interface **ip)
+{
+  mio_lparen ();
+  mio_pool_string (name);
+  mio_pool_string (module);
+  mio_interface_rest (ip);
+}
+
+
+static void
+mio_namespace_ref (gfc_namespace **nsp)
+{
+  gfc_namespace *ns;
+  pointer_info *p;
+
+  p = mio_pointer_ref (nsp);
+
+  if (p->type == P_UNKNOWN)
+    p->type = P_NAMESPACE;
+
+  if (iomode == IO_INPUT && p->integer != 0)
+    {
+      ns = (gfc_namespace *) p->u.pointer;
+      if (ns == NULL)
+	{
+	  ns = gfc_get_namespace (NULL, 0);
+	  associate_integer_pointer (p, ns);
+	}
+      else
+	ns->refs++;
+    }
+}
+
+
+/* Save/restore the f2k_derived namespace of a derived-type symbol.  */
+
+static gfc_namespace* current_f2k_derived;
+
+static void
+mio_typebound_proc (gfc_typebound_proc** proc)
+{
+  int flag;
+  int overriding_flag;
+
+  if (iomode == IO_INPUT)
+    {
+      *proc = gfc_get_typebound_proc (NULL);
+      (*proc)->where = gfc_current_locus;
+    }
+  gcc_assert (*proc);
+
+  mio_lparen ();
+
+  (*proc)->access = MIO_NAME (gfc_access) ((*proc)->access, access_types);
+
+  /* IO the NON_OVERRIDABLE/DEFERRED combination.  */
+  gcc_assert (!((*proc)->deferred && (*proc)->non_overridable));
+  overriding_flag = ((*proc)->deferred << 1) | (*proc)->non_overridable;
+  overriding_flag = mio_name (overriding_flag, binding_overriding);
+  (*proc)->deferred = ((overriding_flag & 2) != 0);
+  (*proc)->non_overridable = ((overriding_flag & 1) != 0);
+  gcc_assert (!((*proc)->deferred && (*proc)->non_overridable));
+
+  (*proc)->nopass = mio_name ((*proc)->nopass, binding_passing);
+  (*proc)->is_generic = mio_name ((*proc)->is_generic, binding_generic);
+  (*proc)->ppc = mio_name((*proc)->ppc, binding_ppc);
+
+  mio_pool_string (&((*proc)->pass_arg));
+
+  flag = (int) (*proc)->pass_arg_num;
+  mio_integer (&flag);
+  (*proc)->pass_arg_num = (unsigned) flag;
+
+  if ((*proc)->is_generic)
+    {
+      gfc_tbp_generic* g;
+      int iop;
+
+      mio_lparen ();
+
+      if (iomode == IO_OUTPUT)
+	for (g = (*proc)->u.generic; g; g = g->next)
+	  {
+	    iop = (int) g->is_operator;
+	    mio_integer (&iop);
+	    mio_allocated_string (g->specific_st->name);
+	  }
+      else
+	{
+	  (*proc)->u.generic = NULL;
+	  while (peek_atom () != ATOM_RPAREN)
+	    {
+	      gfc_symtree** sym_root;
+
+	      g = gfc_get_tbp_generic ();
+	      g->specific = NULL;
+
+	      mio_integer (&iop);
+	      g->is_operator = (bool) iop;
+
+	      require_atom (ATOM_STRING);
+	      sym_root = &current_f2k_derived->tb_sym_root;
+	      g->specific_st = gfc_get_tbp_symtree (sym_root, atom_string);
+	      free (atom_string);
+
+	      g->next = (*proc)->u.generic;
+	      (*proc)->u.generic = g;
+	    }
+	}
+
+      mio_rparen ();
+    }
+  else if (!(*proc)->ppc)
+    mio_symtree_ref (&(*proc)->u.specific);
+
+  mio_rparen ();
+}
+
+/* Walker-callback function for this purpose.  */
+static void
+mio_typebound_symtree (gfc_symtree* st)
+{
+  if (iomode == IO_OUTPUT && !st->n.tb)
+    return;
+
+  if (iomode == IO_OUTPUT)
+    {
+      mio_lparen ();
+      mio_allocated_string (st->name);
+    }
+  /* For IO_INPUT, the above is done in mio_f2k_derived.  */
+
+  mio_typebound_proc (&st->n.tb);
+  mio_rparen ();
+}
+
+/* IO a full symtree (in all depth).  */
+static void
+mio_full_typebound_tree (gfc_symtree** root)
+{
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    gfc_traverse_symtree (*root, &mio_typebound_symtree);
+  else
+    {
+      while (peek_atom () == ATOM_LPAREN)
+	{
+	  gfc_symtree* st;
+
+	  mio_lparen ();
+
+	  require_atom (ATOM_STRING);
+	  st = gfc_get_tbp_symtree (root, atom_string);
+	  free (atom_string);
+
+	  mio_typebound_symtree (st);
+	}
+    }
+
+  mio_rparen ();
+}
+
+static void
+mio_finalizer (gfc_finalizer **f)
+{
+  if (iomode == IO_OUTPUT)
+    {
+      gcc_assert (*f);
+      gcc_assert ((*f)->proc_tree); /* Should already be resolved.  */
+      mio_symtree_ref (&(*f)->proc_tree);
+    }
+  else
+    {
+      *f = gfc_get_finalizer ();
+      (*f)->where = gfc_current_locus; /* Value should not matter.  */
+      (*f)->next = NULL;
+
+      mio_symtree_ref (&(*f)->proc_tree);
+      (*f)->proc_sym = NULL;
+    }
+}
+
+static void
+mio_f2k_derived (gfc_namespace *f2k)
+{
+  current_f2k_derived = f2k;
+
+  /* Handle the list of finalizer procedures.  */
+  mio_lparen ();
+  if (iomode == IO_OUTPUT)
+    {
+      gfc_finalizer *f;
+      for (f = f2k->finalizers; f; f = f->next)
+	mio_finalizer (&f);
+    }
+  else
+    {
+      f2k->finalizers = NULL;
+      while (peek_atom () != ATOM_RPAREN)
+	{
+	  gfc_finalizer *cur = NULL;
+	  mio_finalizer (&cur);
+	  cur->next = f2k->finalizers;
+	  f2k->finalizers = cur;
+	}
+    }
+  mio_rparen ();
+
+  /* Handle type-bound procedures.  */
+  mio_full_typebound_tree (&f2k->tb_sym_root);
+
+  /* Type-bound user operators.  */
+  mio_full_typebound_tree (&f2k->tb_uop_root);
+
+  /* Type-bound intrinsic operators.  */
+  mio_lparen ();
+  if (iomode == IO_OUTPUT)
+    {
+      int op;
+      for (op = GFC_INTRINSIC_BEGIN; op != GFC_INTRINSIC_END; ++op)
+	{
+	  gfc_intrinsic_op realop;
+
+	  if (op == INTRINSIC_USER || !f2k->tb_op[op])
+	    continue;
+
+	  mio_lparen ();
+	  realop = (gfc_intrinsic_op) op;
+	  mio_intrinsic_op (&realop);
+	  mio_typebound_proc (&f2k->tb_op[op]);
+	  mio_rparen ();
+	}
+    }
+  else
+    while (peek_atom () != ATOM_RPAREN)
+      {
+	gfc_intrinsic_op op = GFC_INTRINSIC_BEGIN; /* Silence GCC.  */
+
+	mio_lparen ();
+	mio_intrinsic_op (&op);
+	mio_typebound_proc (&f2k->tb_op[op]);
+	mio_rparen ();
+      }
+  mio_rparen ();
+}
+
+static void
+mio_full_f2k_derived (gfc_symbol *sym)
+{
+  mio_lparen ();
+
+  if (iomode == IO_OUTPUT)
+    {
+      if (sym->f2k_derived)
+	mio_f2k_derived (sym->f2k_derived);
+    }
+  else
+    {
+      if (peek_atom () != ATOM_RPAREN)
+	{
+	  gfc_namespace *ns;
+
+	  sym->f2k_derived = gfc_get_namespace (NULL, 0);
+
+	  /* PDT templates make use of the mechanisms for formal args
+	     and so the parameter symbols are stored in the formal
+	     namespace.  Transfer the sym_root to f2k_derived and then
+	     free the formal namespace since it is uneeded.  */
+	  if (sym->attr.pdt_template && sym->formal && sym->formal->sym)
+	    {
+	      ns = sym->formal->sym->ns;
+	      sym->f2k_derived->sym_root = ns->sym_root;
+	      ns->sym_root = NULL;
+	      ns->refs++;
+	      gfc_free_namespace (ns);
+	      ns = NULL;
+	    }
+
+	  mio_f2k_derived (sym->f2k_derived);
+	}
+      else
+	gcc_assert (!sym->f2k_derived);
+    }
+
+  mio_rparen ();
+}
+
+static const mstring omp_declare_simd_clauses[] =
+{
+    minit ("INBRANCH", 0),
+    minit ("NOTINBRANCH", 1),
+    minit ("SIMDLEN", 2),
+    minit ("UNIFORM", 3),
+    minit ("LINEAR", 4),
+    minit ("ALIGNED", 5),
+    minit (NULL, -1)
+};
+
+/* Handle !$omp declare simd.  */
+
+static void
+mio_omp_declare_simd (gfc_namespace *ns, gfc_omp_declare_simd **odsp)
+{
+  if (iomode == IO_OUTPUT)
+    {
+      if (*odsp == NULL)
+	return;
+    }
+  else if (peek_atom () != ATOM_LPAREN)
+    return;
+
+  gfc_omp_declare_simd *ods = *odsp;
+
+  mio_lparen ();
+  if (iomode == IO_OUTPUT)
+    {
+      write_atom (ATOM_NAME, "OMP_DECLARE_SIMD");
+      if (ods->clauses)
+	{
+	  gfc_omp_namelist *n;
+
+	  if (ods->clauses->inbranch)
+	    mio_name (0, omp_declare_simd_clauses);
+	  if (ods->clauses->notinbranch)
+	    mio_name (1, omp_declare_simd_clauses);
+	  if (ods->clauses->simdlen_expr)
+	    {
+	      mio_name (2, omp_declare_simd_clauses);
+	      mio_expr (&ods->clauses->simdlen_expr);
+	    }
+	  for (n = ods->clauses->lists[OMP_LIST_UNIFORM]; n; n = n->next)
+	    {
+	      mio_name (3, omp_declare_simd_clauses);
+	      mio_symbol_ref (&n->sym);
+	    }
+	  for (n = ods->clauses->lists[OMP_LIST_LINEAR]; n; n = n->next)
+	    {
+	      mio_name (4, omp_declare_simd_clauses);
+	      mio_symbol_ref (&n->sym);
+	      mio_expr (&n->expr);
+	    }
+	  for (n = ods->clauses->lists[OMP_LIST_ALIGNED]; n; n = n->next)
+	    {
+	      mio_name (5, omp_declare_simd_clauses);
+	      mio_symbol_ref (&n->sym);
+	      mio_expr (&n->expr);
+	    }
+	}
+    }
+  else
+    {
+      gfc_omp_namelist **ptrs[3] = { NULL, NULL, NULL };
+
+      require_atom (ATOM_NAME);
+      *odsp = ods = gfc_get_omp_declare_simd ();
+      ods->where = gfc_current_locus;
+      ods->proc_name = ns->proc_name;
+      if (peek_atom () == ATOM_NAME)
+	{
+	  ods->clauses = gfc_get_omp_clauses ();
+	  ptrs[0] = &ods->clauses->lists[OMP_LIST_UNIFORM];
+	  ptrs[1] = &ods->clauses->lists[OMP_LIST_LINEAR];
+	  ptrs[2] = &ods->clauses->lists[OMP_LIST_ALIGNED];
+	}
+      while (peek_atom () == ATOM_NAME)
+	{
+	  gfc_omp_namelist *n;
+	  int t = mio_name (0, omp_declare_simd_clauses);
+
+	  switch (t)
+	    {
+	    case 0: ods->clauses->inbranch = true; break;
+	    case 1: ods->clauses->notinbranch = true; break;
+	    case 2: mio_expr (&ods->clauses->simdlen_expr); break;
+	    case 3:
+	    case 4:
+	    case 5:
+	      *ptrs[t - 3] = n = gfc_get_omp_namelist ();
+	      ptrs[t - 3] = &n->next;
+	      mio_symbol_ref (&n->sym);
+	      if (t != 3)
+		mio_expr (&n->expr);
+	      break;
+	    }
+	}
+    }
+
+  mio_omp_declare_simd (ns, &ods->next);
+
+  mio_rparen ();
+}
+
+
+static const mstring omp_declare_reduction_stmt[] =
+{
+    minit ("ASSIGN", 0),
+    minit ("CALL", 1),
+    minit (NULL, -1)
+};
+
+
+static void
+mio_omp_udr_expr (gfc_omp_udr *udr, gfc_symbol **sym1, gfc_symbol **sym2,
+		  gfc_namespace *ns, bool is_initializer)
+{
+  if (iomode == IO_OUTPUT)
+    {
+      if ((*sym1)->module == NULL)
+	{
+	  (*sym1)->module = module_name;
+	  (*sym2)->module = module_name;
+	}
+      mio_symbol_ref (sym1);
+      mio_symbol_ref (sym2);
+      if (ns->code->op == EXEC_ASSIGN)
+	{
+	  mio_name (0, omp_declare_reduction_stmt);
+	  mio_expr (&ns->code->expr1);
+	  mio_expr (&ns->code->expr2);
+	}
+      else
+	{
+	  int flag;
+	  mio_name (1, omp_declare_reduction_stmt);
+	  mio_symtree_ref (&ns->code->symtree);
+	  mio_actual_arglist (&ns->code->ext.actual, false);
+
+	  flag = ns->code->resolved_isym != NULL;
+	  mio_integer (&flag);
+	  if (flag)
+	    write_atom (ATOM_STRING, ns->code->resolved_isym->name);
+	  else
+	    mio_symbol_ref (&ns->code->resolved_sym);
+	}
+    }
+  else
+    {
+      pointer_info *p1 = mio_symbol_ref (sym1);
+      pointer_info *p2 = mio_symbol_ref (sym2);
+      gfc_symbol *sym;
+      gcc_assert (p1->u.rsym.ns == p2->u.rsym.ns);
+      gcc_assert (p1->u.rsym.sym == NULL);
+      /* Add hidden symbols to the symtree.  */
+      pointer_info *q = get_integer (p1->u.rsym.ns);
+      q->u.pointer = (void *) ns;
+      sym = gfc_new_symbol (is_initializer ? "omp_priv" : "omp_out", ns);
+      sym->ts = udr->ts;
+      sym->module = gfc_get_string ("%s", p1->u.rsym.module);
+      associate_integer_pointer (p1, sym);
+      sym->attr.omp_udr_artificial_var = 1;
+      gcc_assert (p2->u.rsym.sym == NULL);
+      sym = gfc_new_symbol (is_initializer ? "omp_orig" : "omp_in", ns);
+      sym->ts = udr->ts;
+      sym->module = gfc_get_string ("%s", p2->u.rsym.module);
+      associate_integer_pointer (p2, sym);
+      sym->attr.omp_udr_artificial_var = 1;
+      if (mio_name (0, omp_declare_reduction_stmt) == 0)
+	{
+	  ns->code = gfc_get_code (EXEC_ASSIGN);
+	  mio_expr (&ns->code->expr1);
+	  mio_expr (&ns->code->expr2);
+	}
+      else
+	{
+	  int flag;
+	  ns->code = gfc_get_code (EXEC_CALL);
+	  mio_symtree_ref (&ns->code->symtree);
+	  mio_actual_arglist (&ns->code->ext.actual, false);
+
+	  mio_integer (&flag);
+	  if (flag)
+	    {
+	      require_atom (ATOM_STRING);
+	      ns->code->resolved_isym = gfc_find_subroutine (atom_string);
+	      free (atom_string);
+	    }
+	  else
+	    mio_symbol_ref (&ns->code->resolved_sym);
+	}
+      ns->code->loc = gfc_current_locus;
+      ns->omp_udr_ns = 1;
+    }
+}
+
+
+/* Unlike most other routines, the address of the symbol node is already
+   fixed on input and the name/module has already been filled in.
+   If you update the symbol format here, don't forget to update read_module
+   as well (look for "seek to the symbol's component list").   */
+
+static void
+mio_symbol (gfc_symbol *sym)
+{
+  int intmod = INTMOD_NONE;
+
+  mio_lparen ();
+
+  mio_symbol_attribute (&sym->attr);
+
+  /* Note that components are always saved, even if they are supposed
+     to be private.  Component access is checked during searching.  */
+  mio_component_list (&sym->components, sym->attr.vtype);
+  if (sym->components != NULL)
+    sym->component_access
+      = MIO_NAME (gfc_access) (sym->component_access, access_types);
+
+  mio_typespec (&sym->ts);
+  if (sym->ts.type == BT_CLASS)
+    sym->attr.class_ok = 1;
+
+  if (iomode == IO_OUTPUT)
+    mio_namespace_ref (&sym->formal_ns);
+  else
+    {
+      mio_namespace_ref (&sym->formal_ns);
+      if (sym->formal_ns)
+	sym->formal_ns->proc_name = sym;
+    }
+
+  /* Save/restore common block links.  */
+  mio_symbol_ref (&sym->common_next);
+
+  mio_formal_arglist (&sym->formal);
+
+  if (sym->attr.flavor == FL_PARAMETER)
+    mio_expr (&sym->value);
+
+  mio_array_spec (&sym->as);
+
+  mio_symbol_ref (&sym->result);
+
+  if (sym->attr.cray_pointee)
+    mio_symbol_ref (&sym->cp_pointer);
+
+  /* Load/save the f2k_derived namespace of a derived-type symbol.  */
+  mio_full_f2k_derived (sym);
+
+  /* PDT types store the symbol specification list here. */
+  mio_actual_arglist (&sym->param_list, true);
+
+  mio_namelist (sym);
+
+  /* Add the fields that say whether this is from an intrinsic module,
+     and if so, what symbol it is within the module.  */
+/*   mio_integer (&(sym->from_intmod)); */
+  if (iomode == IO_OUTPUT)
+    {
+      intmod = sym->from_intmod;
+      mio_integer (&intmod);
+    }
+  else
+    {
+      mio_integer (&intmod);
+      if (current_intmod)
+	sym->from_intmod = current_intmod;
+      else
+	sym->from_intmod = (intmod_id) intmod;
+    }
+
+  mio_integer (&(sym->intmod_sym_id));
+
+  if (gfc_fl_struct (sym->attr.flavor))
+    mio_integer (&(sym->hash_value));
+
+  if (sym->formal_ns
+      && sym->formal_ns->proc_name == sym
+      && sym->formal_ns->entries == NULL)
+    mio_omp_declare_simd (sym->formal_ns, &sym->formal_ns->omp_declare_simd);
+
+  mio_rparen ();
+}
+
+
+/************************* Top level subroutines *************************/
+
+/* A recursive function to look for a specific symbol by name and by
+   module.  Whilst several symtrees might point to one symbol, its
+   is sufficient for the purposes here than one exist.  Note that
+   generic interfaces are distinguished as are symbols that have been
+   renamed in another module.  */
+static gfc_symtree *
+find_symbol (gfc_symtree *st, const char *name,
+	     const char *module, int generic)
+{
+  int c;
+  gfc_symtree *retval, *s;
+
+  if (st == NULL || st->n.sym == NULL)
+    return NULL;
+
+  c = strcmp (name, st->n.sym->name);
+  if (c == 0 && st->n.sym->module
+	     && strcmp (module, st->n.sym->module) == 0
+	     && !check_unique_name (st->name))
+    {
+      s = gfc_find_symtree (gfc_current_ns->sym_root, name);
+
+      /* Detect symbols that are renamed by use association in another
+	 module by the absence of a symtree and null attr.use_rename,
+	 since the latter is not transmitted in the module file.  */
+      if (((!generic && !st->n.sym->attr.generic)
+		|| (generic && st->n.sym->attr.generic))
+	    && !(s == NULL && !st->n.sym->attr.use_rename))
+	return st;
+    }
+
+  retval = find_symbol (st->left, name, module, generic);
+
+  if (retval == NULL)
+    retval = find_symbol (st->right, name, module, generic);
+
+  return retval;
+}
+
+
+/* Skip a list between balanced left and right parens.
+   By setting NEST_LEVEL one assumes that a number of NEST_LEVEL opening parens
+   have been already parsed by hand, and the remaining of the content is to be
+   skipped here.  The default value is 0 (balanced parens).  */
+
+static void
+skip_list (int nest_level = 0)
+{
+  int level;
+
+  level = nest_level;
+  do
+    {
+      switch (parse_atom ())
+	{
+	case ATOM_LPAREN:
+	  level++;
+	  break;
+
+	case ATOM_RPAREN:
+	  level--;
+	  break;
+
+	case ATOM_STRING:
+	  free (atom_string);
+	  break;
+
+	case ATOM_NAME:
+	case ATOM_INTEGER:
+	  break;
+	}
+    }
+  while (level > 0);
+}
+
+
+/* Load operator interfaces from the module.  Interfaces are unusual
+   in that they attach themselves to existing symbols.  */
+
+static void
+load_operator_interfaces (void)
+{
+  const char *p;
+  char name[GFC_MAX_SYMBOL_LEN + 1], module[GFC_MAX_SYMBOL_LEN + 1];
+  gfc_user_op *uop;
+  pointer_info *pi = NULL;
+  int n, i;
+
+  mio_lparen ();
+
+  while (peek_atom () != ATOM_RPAREN)
+    {
+      mio_lparen ();
+
+      mio_internal_string (name);
+      mio_internal_string (module);
+
+      n = number_use_names (name, true);
+      n = n ? n : 1;
+
+      for (i = 1; i <= n; i++)
+	{
+	  /* Decide if we need to load this one or not.  */
+	  p = find_use_name_n (name, &i, true);
+
+	  if (p == NULL)
+	    {
+	      while (parse_atom () != ATOM_RPAREN);
+	      continue;
+	    }
+
+	  if (i == 1)
+	    {
+	      uop = gfc_get_uop (p);
+	      pi = mio_interface_rest (&uop->op);
+	    }
+	  else
+	    {
+	      if (gfc_find_uop (p, NULL))
+		continue;
+	      uop = gfc_get_uop (p);
+	      uop->op = gfc_get_interface ();
+	      uop->op->where = gfc_current_locus;
+	      add_fixup (pi->integer, &uop->op->sym);
+	    }
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+/* Load interfaces from the module.  Interfaces are unusual in that
+   they attach themselves to existing symbols.  */
+
+static void
+load_generic_interfaces (void)
+{
+  const char *p;
+  char name[GFC_MAX_SYMBOL_LEN + 1], module[GFC_MAX_SYMBOL_LEN + 1];
+  gfc_symbol *sym;
+  gfc_interface *generic = NULL, *gen = NULL;
+  int n, i, renamed;
+  bool ambiguous_set = false;
+
+  mio_lparen ();
+
+  while (peek_atom () != ATOM_RPAREN)
+    {
+      mio_lparen ();
+
+      mio_internal_string (name);
+      mio_internal_string (module);
+
+      n = number_use_names (name, false);
+      renamed = n ? 1 : 0;
+      n = n ? n : 1;
+
+      for (i = 1; i <= n; i++)
+	{
+	  gfc_symtree *st;
+	  /* Decide if we need to load this one or not.  */
+	  p = find_use_name_n (name, &i, false);
+
+	  st = find_symbol (gfc_current_ns->sym_root,
+			    name, module_name, 1);
+
+	  if (!p || gfc_find_symbol (p, NULL, 0, &sym))
+	    {
+	      /* Skip the specific names for these cases.  */
+	      while (i == 1 && parse_atom () != ATOM_RPAREN);
+
+	      continue;
+	    }
+
+	  /* If the symbol exists already and is being USEd without being
+	     in an ONLY clause, do not load a new symtree(11.3.2).  */
+	  if (!only_flag && st)
+	    sym = st->n.sym;
+
+	  if (!sym)
+	    {
+	      if (st)
+		{
+		  sym = st->n.sym;
+		  if (strcmp (st->name, p) != 0)
+		    {
+	              st = gfc_new_symtree (&gfc_current_ns->sym_root, p);
+		      st->n.sym = sym;
+		      sym->refs++;
+		    }
+		}
+
+	      /* Since we haven't found a valid generic interface, we had
+		 better make one.  */
+	      if (!sym)
+		{
+		  gfc_get_symbol (p, NULL, &sym);
+		  sym->name = gfc_get_string ("%s", name);
+		  sym->module = module_name;
+		  sym->attr.flavor = FL_PROCEDURE;
+		  sym->attr.generic = 1;
+		  sym->attr.use_assoc = 1;
+		}
+	    }
+	  else
+	    {
+	      /* Unless sym is a generic interface, this reference
+		 is ambiguous.  */
+	      if (st == NULL)
+	        st = gfc_find_symtree (gfc_current_ns->sym_root, p);
+
+	      sym = st->n.sym;
+
+	      if (st && !sym->attr.generic
+		     && !st->ambiguous
+		     && sym->module
+		     && strcmp (module, sym->module))
+		{
+		  ambiguous_set = true;
+		  st->ambiguous = 1;
+		}
+	    }
+
+	  sym->attr.use_only = only_flag;
+	  sym->attr.use_rename = renamed;
+
+	  if (i == 1)
+	    {
+	      mio_interface_rest (&sym->generic);
+	      generic = sym->generic;
+	    }
+	  else if (!sym->generic)
+	    {
+	      sym->generic = generic;
+	      sym->attr.generic_copy = 1;
+	    }
+
+	  /* If a procedure that is not generic has generic interfaces
+	     that include itself, it is generic! We need to take care
+	     to retain symbols ambiguous that were already so.  */
+	  if (sym->attr.use_assoc
+		&& !sym->attr.generic
+		&& sym->attr.flavor == FL_PROCEDURE)
+	    {
+	      for (gen = generic; gen; gen = gen->next)
+		{
+		  if (gen->sym == sym)
+		    {
+		      sym->attr.generic = 1;
+		      if (ambiguous_set)
+		        st->ambiguous = 0;
+		      break;
+		    }
+		}
+	    }
+
+	}
+    }
+
+  mio_rparen ();
+}
+
+
+/* Load common blocks.  */
+
+static void
+load_commons (void)
+{
+  char name[GFC_MAX_SYMBOL_LEN + 1];
+  gfc_common_head *p;
+
+  mio_lparen ();
+
+  while (peek_atom () != ATOM_RPAREN)
+    {
+      int flags;
+      char* label;
+      mio_lparen ();
+      mio_internal_string (name);
+
+      p = gfc_get_common (name, 1);
+
+      mio_symbol_ref (&p->head);
+      mio_integer (&flags);
+      if (flags & 1)
+	p->saved = 1;
+      if (flags & 2)
+	p->threadprivate = 1;
+      p->use_assoc = 1;
+
+      /* Get whether this was a bind(c) common or not.  */
+      mio_integer (&p->is_bind_c);
+      /* Get the binding label.  */
+      label = read_string ();
+      if (strlen (label))
+	p->binding_label = IDENTIFIER_POINTER (get_identifier (label));
+      XDELETEVEC (label);
+
+      mio_rparen ();
+    }
+
+  mio_rparen ();
+}
+
+
+/* Load equivalences.  The flag in_load_equiv informs mio_expr_ref of this
+   so that unused variables are not loaded and so that the expression can
+   be safely freed.  */
+
+static void
+load_equiv (void)
+{
+  gfc_equiv *head, *tail, *end, *eq, *equiv;
+  bool duplicate;
+
+  mio_lparen ();
+  in_load_equiv = true;
+
+  end = gfc_current_ns->equiv;
+  while (end != NULL && end->next != NULL)
+    end = end->next;
+
+  while (peek_atom () != ATOM_RPAREN) {
+    mio_lparen ();
+    head = tail = NULL;
+
+    while(peek_atom () != ATOM_RPAREN)
+      {
+	if (head == NULL)
+	  head = tail = gfc_get_equiv ();
+	else
+	  {
+	    tail->eq = gfc_get_equiv ();
+	    tail = tail->eq;
+	  }
+
+	mio_pool_string (&tail->module);
+	mio_expr (&tail->expr);
+      }
+
+    /* Check for duplicate equivalences being loaded from different modules */
+    duplicate = false;
+    for (equiv = gfc_current_ns->equiv; equiv; equiv = equiv->next)
+      {
+	if (equiv->module && head->module
+	    && strcmp (equiv->module, head->module) == 0)
+	  {
+	    duplicate = true;
+	    break;
+	  }
+      }
+
+    if (duplicate)
+      {
+	for (eq = head; eq; eq = head)
+	  {
+	    head = eq->eq;
+	    gfc_free_expr (eq->expr);
+	    free (eq);
+	  }
+      }
+
+    if (end == NULL)
+      gfc_current_ns->equiv = head;
+    else
+      end->next = head;
+
+    if (head != NULL)
+      end = head;
+
+    mio_rparen ();
+  }
+
+  mio_rparen ();
+  in_load_equiv = false;
+}
+
+
+/* This function loads OpenMP user defined reductions.  */
+static void
+load_omp_udrs (void)
+{
+  mio_lparen ();
+  while (peek_atom () != ATOM_RPAREN)
+    {
+      const char *name = NULL, *newname;
+      char *altname;
+      gfc_typespec ts;
+      gfc_symtree *st;
+      gfc_omp_reduction_op rop = OMP_REDUCTION_USER;
+
+      mio_lparen ();
+      mio_pool_string (&name);
+      gfc_clear_ts (&ts);
+      mio_typespec (&ts);
+      if (strncmp (name, "operator ", sizeof ("operator ") - 1) == 0)
+	{
+	  const char *p = name + sizeof ("operator ") - 1;
+	  if (strcmp (p, "+") == 0)
+	    rop = OMP_REDUCTION_PLUS;
+	  else if (strcmp (p, "*") == 0)
+	    rop = OMP_REDUCTION_TIMES;
+	  else if (strcmp (p, "-") == 0)
+	    rop = OMP_REDUCTION_MINUS;
+	  else if (strcmp (p, ".and.") == 0)
+	    rop = OMP_REDUCTION_AND;
+	  else if (strcmp (p, ".or.") == 0)
+	    rop = OMP_REDUCTION_OR;
+	  else if (strcmp (p, ".eqv.") == 0)
+	    rop = OMP_REDUCTION_EQV;
+	  else if (strcmp (p, ".neqv.") == 0)
+	    rop = OMP_REDUCTION_NEQV;
+	}
+      altname = NULL;
+      if (rop == OMP_REDUCTION_USER && name[0] == '.')
+	{
+	  size_t len = strlen (name + 1);
+	  altname = XALLOCAVEC (char, len);
+	  gcc_assert (name[len] == '.');
+	  memcpy (altname, name + 1, len - 1);
+	  altname[len - 1] = '\0';
+	}
+      newname = name;
+      if (rop == OMP_REDUCTION_USER)
+	newname = find_use_name (altname ? altname : name, !!altname);
+      else if (only_flag && find_use_operator ((gfc_intrinsic_op) rop) == NULL)
+	newname = NULL;
+      if (newname == NULL)
+	{
+	  skip_list (1);
+	  continue;
+	}
+      if (altname && newname != altname)
+	{
+	  size_t len = strlen (newname);
+	  altname = XALLOCAVEC (char, len + 3);
+	  altname[0] = '.';
+	  memcpy (altname + 1, newname, len);
+	  altname[len + 1] = '.';
+	  altname[len + 2] = '\0';
+	  name = gfc_get_string ("%s", altname);
+	}
+      st = gfc_find_symtree (gfc_current_ns->omp_udr_root, name);
+      gfc_omp_udr *udr = gfc_omp_udr_find (st, &ts);
+      if (udr)
+	{
+	  require_atom (ATOM_INTEGER);
+	  pointer_info *p = get_integer (atom_int);
+	  if (strcmp (p->u.rsym.module, udr->omp_out->module))
+	    {
+	      gfc_error ("Ambiguous !$OMP DECLARE REDUCTION from "
+			 "module %s at %L",
+			 p->u.rsym.module, &gfc_current_locus);
+	      gfc_error ("Previous !$OMP DECLARE REDUCTION from module "
+			 "%s at %L",
+			 udr->omp_out->module, &udr->where);
+	    }
+	  skip_list (1);
+	  continue;
+	}
+      udr = gfc_get_omp_udr ();
+      udr->name = name;
+      udr->rop = rop;
+      udr->ts = ts;
+      udr->where = gfc_current_locus;
+      udr->combiner_ns = gfc_get_namespace (gfc_current_ns, 1);
+      udr->combiner_ns->proc_name = gfc_current_ns->proc_name;
+      mio_omp_udr_expr (udr, &udr->omp_out, &udr->omp_in, udr->combiner_ns,
+			false);
+      if (peek_atom () != ATOM_RPAREN)
+	{
+	  udr->initializer_ns = gfc_get_namespace (gfc_current_ns, 1);
+	  udr->initializer_ns->proc_name = gfc_current_ns->proc_name;
+	  mio_omp_udr_expr (udr, &udr->omp_priv, &udr->omp_orig,
+			    udr->initializer_ns, true);
+	}
+      if (st)
+	{
+	  udr->next = st->n.omp_udr;
+	  st->n.omp_udr = udr;
+	}
+      else
+	{
+	  st = gfc_new_symtree (&gfc_current_ns->omp_udr_root, name);
+	  st->n.omp_udr = udr;
+	}
+      mio_rparen ();
+    }
+  mio_rparen ();
+}
+
+
+/* Recursive function to traverse the pointer_info tree and load a
+   needed symbol.  We return nonzero if we load a symbol and stop the
+   traversal, because the act of loading can alter the tree.  */
+
+static int
+load_needed (pointer_info *p)
+{
+  gfc_namespace *ns;
+  pointer_info *q;
+  gfc_symbol *sym;
+  int rv;
+
+  rv = 0;
+  if (p == NULL)
+    return rv;
+
+  rv |= load_needed (p->left);
+  rv |= load_needed (p->right);
+
+  if (p->type != P_SYMBOL || p->u.rsym.state != NEEDED)
+    return rv;
+
+  p->u.rsym.state = USED;
+
+  set_module_locus (&p->u.rsym.where);
+
+  sym = p->u.rsym.sym;
+  if (sym == NULL)
+    {
+      q = get_integer (p->u.rsym.ns);
+
+      ns = (gfc_namespace *) q->u.pointer;
+      if (ns == NULL)
+	{
+	  /* Create an interface namespace if necessary.  These are
+	     the namespaces that hold the formal parameters of module
+	     procedures.  */
+
+	  ns = gfc_get_namespace (NULL, 0);
+	  associate_integer_pointer (q, ns);
+	}
+
+      /* Use the module sym as 'proc_name' so that gfc_get_symbol_decl
+	 doesn't go pear-shaped if the symbol is used.  */
+      if (!ns->proc_name)
+	gfc_find_symbol (p->u.rsym.module, gfc_current_ns,
+				 1, &ns->proc_name);
+
+      sym = gfc_new_symbol (p->u.rsym.true_name, ns);
+      sym->name = gfc_dt_lower_string (p->u.rsym.true_name);
+      sym->module = gfc_get_string ("%s", p->u.rsym.module);
+      if (p->u.rsym.binding_label)
+	sym->binding_label = IDENTIFIER_POINTER (get_identifier
+						 (p->u.rsym.binding_label));
+
+      associate_integer_pointer (p, sym);
+    }
+
+  mio_symbol (sym);
+  sym->attr.use_assoc = 1;
+
+  /* Unliked derived types, a STRUCTURE may share names with other symbols.
+     We greedily converted the the symbol name to lowercase before we knew its
+     type, so now we must fix it. */
+  if (sym->attr.flavor == FL_STRUCT)
+    sym->name = gfc_dt_upper_string (sym->name);
+
+  /* Mark as only or rename for later diagnosis for explicitly imported
+     but not used warnings; don't mark internal symbols such as __vtab,
+     __def_init etc. Only mark them if they have been explicitly loaded.  */
+
+  if (only_flag && sym->name[0] != '_' && sym->name[1] != '_')
+    {
+      gfc_use_rename *u;
+
+      /* Search the use/rename list for the variable; if the variable is
+	 found, mark it.  */
+      for (u = gfc_rename_list; u; u = u->next)
+	{
+	  if (strcmp (u->use_name, sym->name) == 0)
+	    {
+	      sym->attr.use_only = 1;
+	      break;
+	    }
+	}
+    }
+
+  if (p->u.rsym.renamed)
+    sym->attr.use_rename = 1;
+
+  return 1;
+}
+
+
+/* Recursive function for cleaning up things after a module has been read.  */
+
+static void
+read_cleanup (pointer_info *p)
+{
+  gfc_symtree *st;
+  pointer_info *q;
+
+  if (p == NULL)
+    return;
+
+  read_cleanup (p->left);
+  read_cleanup (p->right);
+
+  if (p->type == P_SYMBOL && p->u.rsym.state == USED && !p->u.rsym.referenced)
+    {
+      gfc_namespace *ns;
+      /* Add hidden symbols to the symtree.  */
+      q = get_integer (p->u.rsym.ns);
+      ns = (gfc_namespace *) q->u.pointer;
+
+      if (!p->u.rsym.sym->attr.vtype
+	    && !p->u.rsym.sym->attr.vtab)
+	st = gfc_get_unique_symtree (ns);
+      else
+	{
+	  /* There is no reason to use 'unique_symtrees' for vtabs or
+	     vtypes - their name is fine for a symtree and reduces the
+	     namespace pollution.  */
+	  st = gfc_find_symtree (ns->sym_root, p->u.rsym.sym->name);
+	  if (!st)
+	    st = gfc_new_symtree (&ns->sym_root, p->u.rsym.sym->name);
+	}
+
+      st->n.sym = p->u.rsym.sym;
+      st->n.sym->refs++;
+
+      /* Fixup any symtree references.  */
+      p->u.rsym.symtree = st;
+      resolve_fixups (p->u.rsym.stfixup, st);
+      p->u.rsym.stfixup = NULL;
+    }
+
+  /* Free unused symbols.  */
+  if (p->type == P_SYMBOL && p->u.rsym.state == UNUSED)
+    gfc_free_symbol (p->u.rsym.sym);
+}
+
+
+/* It is not quite enough to check for ambiguity in the symbols by
+   the loaded symbol and the new symbol not being identical.  */
+static bool
+check_for_ambiguous (gfc_symtree *st, pointer_info *info)
+{
+  gfc_symbol *rsym;
+  module_locus locus;
+  symbol_attribute attr;
+  gfc_symbol *st_sym;
+
+  if (gfc_current_ns->proc_name && st->name == gfc_current_ns->proc_name->name)
+    {
+      gfc_error ("%qs of module %qs, imported at %C, is also the name of the "
+		 "current program unit", st->name, module_name);
+      return true;
+    }
+
+  st_sym = st->n.sym;
+  rsym = info->u.rsym.sym;
+  if (st_sym == rsym)
+    return false;
+
+  if (st_sym->attr.vtab || st_sym->attr.vtype)
+    return false;
+
+  /* If the existing symbol is generic from a different module and
+     the new symbol is generic there can be no ambiguity.  */
+  if (st_sym->attr.generic
+	&& st_sym->module
+	&& st_sym->module != module_name)
+    {
+      /* The new symbol's attributes have not yet been read.  Since
+	 we need attr.generic, read it directly.  */
+      get_module_locus (&locus);
+      set_module_locus (&info->u.rsym.where);
+      mio_lparen ();
+      attr.generic = 0;
+      mio_symbol_attribute (&attr);
+      set_module_locus (&locus);
+      if (attr.generic)
+	return false;
+    }
+
+  return true;
+}
+
+
+/* Read a module file.  */
+
+static void
+read_module (void)
+{
+  module_locus operator_interfaces, user_operators, omp_udrs;
+  const char *p;
+  char name[GFC_MAX_SYMBOL_LEN + 1];
+  int i;
+  /* Workaround -Wmaybe-uninitialized false positive during
+     profiledbootstrap by initializing them.  */
+  int ambiguous = 0, j, nuse, symbol = 0;
+  pointer_info *info, *q;
+  gfc_use_rename *u = NULL;
+  gfc_symtree *st;
+  gfc_symbol *sym;
+
+  get_module_locus (&operator_interfaces);	/* Skip these for now.  */
+  skip_list ();
+
+  get_module_locus (&user_operators);
+  skip_list ();
+  skip_list ();
+
+  /* Skip commons and equivalences for now.  */
+  skip_list ();
+  skip_list ();
+
+  /* Skip OpenMP UDRs.  */
+  get_module_locus (&omp_udrs);
+  skip_list ();
+
+  mio_lparen ();
+
+  /* Create the fixup nodes for all the symbols.  */
+
+  while (peek_atom () != ATOM_RPAREN)
+    {
+      char* bind_label;
+      require_atom (ATOM_INTEGER);
+      info = get_integer (atom_int);
+
+      info->type = P_SYMBOL;
+      info->u.rsym.state = UNUSED;
+
+      info->u.rsym.true_name = read_string ();
+      info->u.rsym.module = read_string ();
+      bind_label = read_string ();
+      if (strlen (bind_label))
+	info->u.rsym.binding_label = bind_label;
+      else
+	XDELETEVEC (bind_label);
+
+      require_atom (ATOM_INTEGER);
+      info->u.rsym.ns = atom_int;
+
+      get_module_locus (&info->u.rsym.where);
+
+      /* See if the symbol has already been loaded by a previous module.
+	 If so, we reference the existing symbol and prevent it from
+	 being loaded again.  This should not happen if the symbol being
+	 read is an index for an assumed shape dummy array (ns != 1).  */
+
+      sym = find_true_name (info->u.rsym.true_name, info->u.rsym.module);
+
+      if (sym == NULL
+	  || (sym->attr.flavor == FL_VARIABLE && info->u.rsym.ns !=1))
+	{
+	  skip_list ();
+	  continue;
+	}
+
+      info->u.rsym.state = USED;
+      info->u.rsym.sym = sym;
+      /* The current symbol has already been loaded, so we can avoid loading
+	 it again.  However, if it is a derived type, some of its components
+	 can be used in expressions in the module.  To avoid the module loading
+	 failing, we need to associate the module's component pointer indexes
+	 with the existing symbol's component pointers.  */
+      if (gfc_fl_struct (sym->attr.flavor))
+	{
+	  gfc_component *c;
+
+	  /* First seek to the symbol's component list.  */
+	  mio_lparen (); /* symbol opening.  */
+	  skip_list (); /* skip symbol attribute.  */
+
+	  mio_lparen (); /* component list opening.  */
+	  for (c = sym->components; c; c = c->next)
+	    {
+	      pointer_info *p;
+	      const char *comp_name;
+	      int n;
+
+	      mio_lparen (); /* component opening.  */
+	      mio_integer (&n);
+	      p = get_integer (n);
+	      if (p->u.pointer == NULL)
+		associate_integer_pointer (p, c);
+	      mio_pool_string (&comp_name);
+	      gcc_assert (comp_name == c->name);
+	      skip_list (1); /* component end.  */
+	    }
+	  mio_rparen (); /* component list closing.  */
+
+	  skip_list (1); /* symbol end.  */
+	}
+      else
+	skip_list ();
+
+      /* Some symbols do not have a namespace (eg. formal arguments),
+	 so the automatic "unique symtree" mechanism must be suppressed
+	 by marking them as referenced.  */
+      q = get_integer (info->u.rsym.ns);
+      if (q->u.pointer == NULL)
+	{
+	  info->u.rsym.referenced = 1;
+	  continue;
+	}
+    }
+
+  mio_rparen ();
+
+  /* Parse the symtree lists.  This lets us mark which symbols need to
+     be loaded.  Renaming is also done at this point by replacing the
+     symtree name.  */
+
+  mio_lparen ();
+
+  while (peek_atom () != ATOM_RPAREN)
+    {
+      mio_internal_string (name);
+      mio_integer (&ambiguous);
+      mio_integer (&symbol);
+
+      info = get_integer (symbol);
+
+      /* See how many use names there are.  If none, go through the start
+	 of the loop at least once.  */
+      nuse = number_use_names (name, false);
+      info->u.rsym.renamed = nuse ? 1 : 0;
+
+      if (nuse == 0)
+	nuse = 1;
+
+      for (j = 1; j <= nuse; j++)
+	{
+	  /* Get the jth local name for this symbol.  */
+	  p = find_use_name_n (name, &j, false);
+
+	  if (p == NULL && strcmp (name, module_name) == 0)
+	    p = name;
+
+	  /* Exception: Always import vtabs & vtypes.  */
+	  if (p == NULL && name[0] == '_'
+	      && (strncmp (name, "__vtab_", 5) == 0
+		  || strncmp (name, "__vtype_", 6) == 0))
+	    p = name;
+
+	  /* Skip symtree nodes not in an ONLY clause, unless there
+	     is an existing symtree loaded from another USE statement.  */
+	  if (p == NULL)
+	    {
+	      st = gfc_find_symtree (gfc_current_ns->sym_root, name);
+	      if (st != NULL
+		  && strcmp (st->n.sym->name, info->u.rsym.true_name) == 0
+		  && st->n.sym->module != NULL
+		  && strcmp (st->n.sym->module, info->u.rsym.module) == 0)
+		{
+		  info->u.rsym.symtree = st;
+		  info->u.rsym.sym = st->n.sym;
+		}
+	      continue;
+	    }
+
+	  /* If a symbol of the same name and module exists already,
+	     this symbol, which is not in an ONLY clause, must not be
+	     added to the namespace(11.3.2).  Note that find_symbol
+	     only returns the first occurrence that it finds.  */
+	  if (!only_flag && !info->u.rsym.renamed
+		&& strcmp (name, module_name) != 0
+		&& find_symbol (gfc_current_ns->sym_root, name,
+				module_name, 0))
+	    continue;
+
+	  st = gfc_find_symtree (gfc_current_ns->sym_root, p);
+
+	  if (st != NULL
+	      && !(st->n.sym && st->n.sym->attr.used_in_submodule))
+	    {
+	      /* Check for ambiguous symbols.  */
+	      if (check_for_ambiguous (st, info))
+		st->ambiguous = 1;
+	      else
+		info->u.rsym.symtree = st;
+	    }
+	  else
+	    {
+	      if (st)
+		{
+		  /* This symbol is host associated from a module in a
+		     submodule.  Hide it with a unique symtree.  */
+		  gfc_symtree *s = gfc_get_unique_symtree (gfc_current_ns);
+		  s->n.sym = st->n.sym;
+		  st->n.sym = NULL;
+		}
+	      else
+		{
+		  /* Create a symtree node in the current namespace for this
+		     symbol.  */
+		  st = check_unique_name (p)
+		       ? gfc_get_unique_symtree (gfc_current_ns)
+		       : gfc_new_symtree (&gfc_current_ns->sym_root, p);
+		  st->ambiguous = ambiguous;
+		}
+
+	      sym = info->u.rsym.sym;
+
+	      /* Create a symbol node if it doesn't already exist.  */
+	      if (sym == NULL)
+		{
+		  info->u.rsym.sym = gfc_new_symbol (info->u.rsym.true_name,
+						     gfc_current_ns);
+		  info->u.rsym.sym->name = gfc_dt_lower_string (info->u.rsym.true_name);
+		  sym = info->u.rsym.sym;
+		  sym->module = gfc_get_string ("%s", info->u.rsym.module);
+
+		  if (info->u.rsym.binding_label)
+		    {
+		      tree id = get_identifier (info->u.rsym.binding_label);
+		      sym->binding_label = IDENTIFIER_POINTER (id);
+		    }
+		}
+
+	      st->n.sym = sym;
+	      st->n.sym->refs++;
+
+	      if (strcmp (name, p) != 0)
+		sym->attr.use_rename = 1;
+
+	      if (name[0] != '_'
+		  || (strncmp (name, "__vtab_", 5) != 0
+		      && strncmp (name, "__vtype_", 6) != 0))
+		sym->attr.use_only = only_flag;
+
+	      /* Store the symtree pointing to this symbol.  */
+	      info->u.rsym.symtree = st;
+
+	      if (info->u.rsym.state == UNUSED)
+		info->u.rsym.state = NEEDED;
+	      info->u.rsym.referenced = 1;
+	    }
+	}
+    }
+
+  mio_rparen ();
+
+  /* Load intrinsic operator interfaces.  */
+  set_module_locus (&operator_interfaces);
+  mio_lparen ();
+
+  for (i = GFC_INTRINSIC_BEGIN; i != GFC_INTRINSIC_END; i++)
+    {
+      if (i == INTRINSIC_USER)
+	continue;
+
+      if (only_flag)
+	{
+	  u = find_use_operator ((gfc_intrinsic_op) i);
+
+	  if (u == NULL)
+	    {
+	      skip_list ();
+	      continue;
+	    }
+
+	  u->found = 1;
+	}
+
+      mio_interface (&gfc_current_ns->op[i]);
+      if (u && !gfc_current_ns->op[i])
+	u->found = 0;
+    }
+
+  mio_rparen ();
+
+  /* Load generic and user operator interfaces.  These must follow the
+     loading of symtree because otherwise symbols can be marked as
+     ambiguous.  */
+
+  set_module_locus (&user_operators);
+
+  load_operator_interfaces ();
+  load_generic_interfaces ();
+
+  load_commons ();
+  load_equiv ();
+
+  /* Load OpenMP user defined reductions.  */
+  set_module_locus (&omp_udrs);
+  load_omp_udrs ();
+
+  /* At this point, we read those symbols that are needed but haven't
+     been loaded yet.  If one symbol requires another, the other gets
+     marked as NEEDED if its previous state was UNUSED.  */
+
+  while (load_needed (pi_root));
+
+  /* Make sure all elements of the rename-list were found in the module.  */
+
+  for (u = gfc_rename_list; u; u = u->next)
+    {
+      if (u->found)
+	continue;
+
+      if (u->op == INTRINSIC_NONE)
+	{
+	  gfc_error ("Symbol %qs referenced at %L not found in module %qs",
+		     u->use_name, &u->where, module_name);
+	  continue;
+	}
+
+      if (u->op == INTRINSIC_USER)
+	{
+	  gfc_error ("User operator %qs referenced at %L not found "
+		     "in module %qs", u->use_name, &u->where, module_name);
+	  continue;
+	}
+
+      gfc_error ("Intrinsic operator %qs referenced at %L not found "
+		 "in module %qs", gfc_op2string (u->op), &u->where,
+		 module_name);
+    }
+
+  /* Clean up symbol nodes that were never loaded, create references
+     to hidden symbols.  */
+
+  read_cleanup (pi_root);
+}
+
+
+/* Given an access type that is specific to an entity and the default
+   access, return nonzero if the entity is publicly accessible.  If the
+   element is declared as PUBLIC, then it is public; if declared
+   PRIVATE, then private, and otherwise it is public unless the default
+   access in this context has been declared PRIVATE.  */
+
+static bool dump_smod = false;
+
+static bool
+check_access (gfc_access specific_access, gfc_access default_access)
+{
+  if (dump_smod)
+    return true;
+
+  if (specific_access == ACCESS_PUBLIC)
+    return TRUE;
+  if (specific_access == ACCESS_PRIVATE)
+    return FALSE;
+
+  if (flag_module_private)
+    return default_access == ACCESS_PUBLIC;
+  else
+    return default_access != ACCESS_PRIVATE;
+}
+
+
+bool
+gfc_check_symbol_access (gfc_symbol *sym)
+{
+  if (sym->attr.vtab || sym->attr.vtype)
+    return true;
+  else
+    return check_access (sym->attr.access, sym->ns->default_access);
+}
+
+
+/* A structure to remember which commons we've already written.  */
+
+struct written_common
+{
+  BBT_HEADER(written_common);
+  const char *name, *label;
+};
+
+static struct written_common *written_commons = NULL;
+
+/* Comparison function used for balancing the binary tree.  */
+
+static int
+compare_written_commons (void *a1, void *b1)
+{
+  const char *aname = ((struct written_common *) a1)->name;
+  const char *alabel = ((struct written_common *) a1)->label;
+  const char *bname = ((struct written_common *) b1)->name;
+  const char *blabel = ((struct written_common *) b1)->label;
+  int c = strcmp (aname, bname);
+
+  return (c != 0 ? c : strcmp (alabel, blabel));
+}
+
+/* Free a list of written commons.  */
+
+static void
+free_written_common (struct written_common *w)
+{
+  if (!w)
+    return;
+
+  if (w->left)
+    free_written_common (w->left);
+  if (w->right)
+    free_written_common (w->right);
+
+  free (w);
+}
+
+/* Write a common block to the module -- recursive helper function.  */
+
+static void
+write_common_0 (gfc_symtree *st, bool this_module)
+{
+  gfc_common_head *p;
+  const char * name;
+  int flags;
+  const char *label;
+  struct written_common *w;
+  bool write_me = true;
+
+  if (st == NULL)
+    return;
+
+  write_common_0 (st->left, this_module);
+
+  /* We will write out the binding label, or "" if no label given.  */
+  name = st->n.common->name;
+  p = st->n.common;
+  label = (p->is_bind_c && p->binding_label) ? p->binding_label : "";
+
+  /* Check if we've already output this common.  */
+  w = written_commons;
+  while (w)
+    {
+      int c = strcmp (name, w->name);
+      c = (c != 0 ? c : strcmp (label, w->label));
+      if (c == 0)
+	write_me = false;
+
+      w = (c < 0) ? w->left : w->right;
+    }
+
+  if (this_module && p->use_assoc)
+    write_me = false;
+
+  if (write_me)
+    {
+      /* Write the common to the module.  */
+      mio_lparen ();
+      mio_pool_string (&name);
+
+      mio_symbol_ref (&p->head);
+      flags = p->saved ? 1 : 0;
+      if (p->threadprivate)
+	flags |= 2;
+      mio_integer (&flags);
+
+      /* Write out whether the common block is bind(c) or not.  */
+      mio_integer (&(p->is_bind_c));
+
+      mio_pool_string (&label);
+      mio_rparen ();
+
+      /* Record that we have written this common.  */
+      w = XCNEW (struct written_common);
+      w->name = p->name;
+      w->label = label;
+      gfc_insert_bbt (&written_commons, w, compare_written_commons);
+    }
+
+  write_common_0 (st->right, this_module);
+}
+
+
+/* Write a common, by initializing the list of written commons, calling
+   the recursive function write_common_0() and cleaning up afterwards.  */
+
+static void
+write_common (gfc_symtree *st)
+{
+  written_commons = NULL;
+  write_common_0 (st, true);
+  write_common_0 (st, false);
+  free_written_common (written_commons);
+  written_commons = NULL;
+}
+
+
+/* Write the blank common block to the module.  */
+
+static void
+write_blank_common (void)
+{
+  const char * name = BLANK_COMMON_NAME;
+  int saved;
+  /* TODO: Blank commons are not bind(c).  The F2003 standard probably says
+     this, but it hasn't been checked.  Just making it so for now.  */
+  int is_bind_c = 0;
+
+  if (gfc_current_ns->blank_common.head == NULL)
+    return;
+
+  mio_lparen ();
+
+  mio_pool_string (&name);
+
+  mio_symbol_ref (&gfc_current_ns->blank_common.head);
+  saved = gfc_current_ns->blank_common.saved;
+  mio_integer (&saved);
+
+  /* Write out whether the common block is bind(c) or not.  */
+  mio_integer (&is_bind_c);
+
+  /* Write out an empty binding label.  */
+  write_atom (ATOM_STRING, "");
+
+  mio_rparen ();
+}
+
+
+/* Write equivalences to the module.  */
+
+static void
+write_equiv (void)
+{
+  gfc_equiv *eq, *e;
+  int num;
+
+  num = 0;
+  for (eq = gfc_current_ns->equiv; eq; eq = eq->next)
+    {
+      mio_lparen ();
+
+      for (e = eq; e; e = e->eq)
+	{
+	  if (e->module == NULL)
+	    e->module = gfc_get_string ("%s.eq.%d", module_name, num);
+	  mio_allocated_string (e->module);
+	  mio_expr (&e->expr);
+	}
+
+      num++;
+      mio_rparen ();
+    }
+}
+
+
+/* Write a symbol to the module.  */
+
+static void
+write_symbol (int n, gfc_symbol *sym)
+{
+  const char *label;
+
+  if (sym->attr.flavor == FL_UNKNOWN || sym->attr.flavor == FL_LABEL)
+    gfc_internal_error ("write_symbol(): bad module symbol %qs", sym->name);
+
+  mio_integer (&n);
+
+  if (gfc_fl_struct (sym->attr.flavor))
+    {
+      const char *name;
+      name = gfc_dt_upper_string (sym->name);
+      mio_pool_string (&name);
+    }
+  else
+    mio_pool_string (&sym->name);
+
+  mio_pool_string (&sym->module);
+  if ((sym->attr.is_bind_c || sym->attr.is_iso_c) && sym->binding_label)
+    {
+      label = sym->binding_label;
+      mio_pool_string (&label);
+    }
+  else
+    write_atom (ATOM_STRING, "");
+
+  mio_pointer_ref (&sym->ns);
+
+  mio_symbol (sym);
+  write_char ('\n');
+}
+
+
+/* Recursive traversal function to write the initial set of symbols to
+   the module.  We check to see if the symbol should be written
+   according to the access specification.  */
+
+static void
+write_symbol0 (gfc_symtree *st)
+{
+  gfc_symbol *sym;
+  pointer_info *p;
+  bool dont_write = false;
+
+  if (st == NULL)
+    return;
+
+  write_symbol0 (st->left);
+
+  sym = st->n.sym;
+  if (sym->module == NULL)
+    sym->module = module_name;
+
+  if (sym->attr.flavor == FL_PROCEDURE && sym->attr.generic
+      && !sym->attr.subroutine && !sym->attr.function)
+    dont_write = true;
+
+  if (!gfc_check_symbol_access (sym))
+    dont_write = true;
+
+  if (!dont_write)
+    {
+      p = get_pointer (sym);
+      if (p->type == P_UNKNOWN)
+	p->type = P_SYMBOL;
+
+      if (p->u.wsym.state != WRITTEN)
+	{
+	  write_symbol (p->integer, sym);
+	  p->u.wsym.state = WRITTEN;
+	}
+    }
+
+  write_symbol0 (st->right);
+}
+
+
+static void
+write_omp_udr (gfc_omp_udr *udr)
+{
+  switch (udr->rop)
+    {
+    case OMP_REDUCTION_USER:
+      /* Non-operators can't be used outside of the module.  */
+      if (udr->name[0] != '.')
+	return;
+      else
+	{
+	  gfc_symtree *st;
+	  size_t len = strlen (udr->name + 1);
+	  char *name = XALLOCAVEC (char, len);
+	  memcpy (name, udr->name, len - 1);
+	  name[len - 1] = '\0';
+	  st = gfc_find_symtree (gfc_current_ns->uop_root, name);
+	  /* If corresponding user operator is private, don't write
+	     the UDR.  */
+	  if (st != NULL)
+	    {
+	      gfc_user_op *uop = st->n.uop;
+	      if (!check_access (uop->access, uop->ns->default_access))
+		return;
+	    }
+	}
+      break;
+    case OMP_REDUCTION_PLUS:
+    case OMP_REDUCTION_MINUS:
+    case OMP_REDUCTION_TIMES:
+    case OMP_REDUCTION_AND:
+    case OMP_REDUCTION_OR:
+    case OMP_REDUCTION_EQV:
+    case OMP_REDUCTION_NEQV:
+      /* If corresponding operator is private, don't write the UDR.  */
+      if (!check_access (gfc_current_ns->operator_access[udr->rop],
+			 gfc_current_ns->default_access))
+	return;
+      break;
+    default:
+      break;
+    }
+  if (udr->ts.type == BT_DERIVED || udr->ts.type == BT_CLASS)
+    {
+      /* If derived type is private, don't write the UDR.  */
+      if (!gfc_check_symbol_access (udr->ts.u.derived))
+	return;
+    }
+
+  mio_lparen ();
+  mio_pool_string (&udr->name);
+  mio_typespec (&udr->ts);
+  mio_omp_udr_expr (udr, &udr->omp_out, &udr->omp_in, udr->combiner_ns, false);
+  if (udr->initializer_ns)
+    mio_omp_udr_expr (udr, &udr->omp_priv, &udr->omp_orig,
+		      udr->initializer_ns, true);
+  mio_rparen ();
+}
+
+
+static void
+write_omp_udrs (gfc_symtree *st)
+{
+  if (st == NULL)
+    return;
+
+  write_omp_udrs (st->left);
+  gfc_omp_udr *udr;
+  for (udr = st->n.omp_udr; udr; udr = udr->next)
+    write_omp_udr (udr);
+  write_omp_udrs (st->right);
+}
+
+
+/* Type for the temporary tree used when writing secondary symbols.  */
+
+struct sorted_pointer_info
+{
+  BBT_HEADER (sorted_pointer_info);
+
+  pointer_info *p;
+};
+
+#define gfc_get_sorted_pointer_info() XCNEW (sorted_pointer_info)
+
+/* Recursively traverse the temporary tree, free its contents.  */
+
+static void
+free_sorted_pointer_info_tree (sorted_pointer_info *p)
+{
+  if (!p)
+    return;
+
+  free_sorted_pointer_info_tree (p->left);
+  free_sorted_pointer_info_tree (p->right);
+
+  free (p);
+}
+
+/* Comparison function for the temporary tree.  */
+
+static int
+compare_sorted_pointer_info (void *_spi1, void *_spi2)
+{
+  sorted_pointer_info *spi1, *spi2;
+  spi1 = (sorted_pointer_info *)_spi1;
+  spi2 = (sorted_pointer_info *)_spi2;
+
+  if (spi1->p->integer < spi2->p->integer)
+    return -1;
+  if (spi1->p->integer > spi2->p->integer)
+    return 1;
+  return 0;
+}
+
+
+/* Finds the symbols that need to be written and collects them in the
+   sorted_pi tree so that they can be traversed in an order
+   independent of memory addresses.  */
+
+static void
+find_symbols_to_write(sorted_pointer_info **tree, pointer_info *p)
+{
+  if (!p)
+    return;
+
+  if (p->type == P_SYMBOL && p->u.wsym.state == NEEDS_WRITE)
+    {
+      sorted_pointer_info *sp = gfc_get_sorted_pointer_info();
+      sp->p = p;
+
+      gfc_insert_bbt (tree, sp, compare_sorted_pointer_info);
+   }
+
+  find_symbols_to_write (tree, p->left);
+  find_symbols_to_write (tree, p->right);
+}
+
+
+/* Recursive function that traverses the tree of symbols that need to be
+   written and writes them in order.  */
+
+static void
+write_symbol1_recursion (sorted_pointer_info *sp)
+{
+  if (!sp)
+    return;
+
+  write_symbol1_recursion (sp->left);
+
+  pointer_info *p1 = sp->p;
+  gcc_assert (p1->type == P_SYMBOL && p1->u.wsym.state == NEEDS_WRITE);
+
+  p1->u.wsym.state = WRITTEN;
+  write_symbol (p1->integer, p1->u.wsym.sym);
+  p1->u.wsym.sym->attr.public_used = 1;
+
+  write_symbol1_recursion (sp->right);
+}
+
+
+/* Write the secondary set of symbols to the module file.  These are
+   symbols that were not public yet are needed by the public symbols
+   or another dependent symbol.  The act of writing a symbol can add
+   symbols to the pointer_info tree, so we return nonzero if a symbol
+   was written and pass that information upwards.  The caller will
+   then call this function again until nothing was written.  It uses
+   the utility functions and a temporary tree to ensure a reproducible
+   ordering of the symbol output and thus the module file.  */
+
+static int
+write_symbol1 (pointer_info *p)
+{
+  if (!p)
+    return 0;
+
+  /* Put symbols that need to be written into a tree sorted on the
+     integer field.  */
+
+  sorted_pointer_info *spi_root = NULL;
+  find_symbols_to_write (&spi_root, p);
+
+  /* No symbols to write, return.  */
+  if (!spi_root)
+    return 0;
+
+  /* Otherwise, write and free the tree again.  */
+  write_symbol1_recursion (spi_root);
+  free_sorted_pointer_info_tree (spi_root);
+
+  return 1;
+}
+
+
+/* Write operator interfaces associated with a symbol.  */
+
+static void
+write_operator (gfc_user_op *uop)
+{
+  static char nullstring[] = "";
+  const char *p = nullstring;
+
+  if (uop->op == NULL || !check_access (uop->access, uop->ns->default_access))
+    return;
+
+  mio_symbol_interface (&uop->name, &p, &uop->op);
+}
+
+
+/* Write generic interfaces from the namespace sym_root.  */
+
+static void
+write_generic (gfc_symtree *st)
+{
+  gfc_symbol *sym;
+
+  if (st == NULL)
+    return;
+
+  write_generic (st->left);
+
+  sym = st->n.sym;
+  if (sym && !check_unique_name (st->name)
+      && sym->generic && gfc_check_symbol_access (sym))
+    {
+      if (!sym->module)
+	sym->module = module_name;
+
+      mio_symbol_interface (&st->name, &sym->module, &sym->generic);
+    }
+
+  write_generic (st->right);
+}
+
+
+static void
+write_symtree (gfc_symtree *st)
+{
+  gfc_symbol *sym;
+  pointer_info *p;
+
+  sym = st->n.sym;
+
+  /* A symbol in an interface body must not be visible in the
+     module file.  */
+  if (sym->ns != gfc_current_ns
+	&& sym->ns->proc_name
+	&& sym->ns->proc_name->attr.if_source == IFSRC_IFBODY)
+    return;
+
+  if (!gfc_check_symbol_access (sym)
+      || (sym->attr.flavor == FL_PROCEDURE && sym->attr.generic
+	  && !sym->attr.subroutine && !sym->attr.function))
+    return;
+
+  if (check_unique_name (st->name))
+    return;
+
+  p = find_pointer (sym);
+  if (p == NULL)
+    gfc_internal_error ("write_symtree(): Symbol not written");
+
+  mio_pool_string (&st->name);
+  mio_integer (&st->ambiguous);
+  mio_integer (&p->integer);
+}
+
+
+static void
+write_module (void)
+{
+  int i;
+
+  /* Write the operator interfaces.  */
+  mio_lparen ();
+
+  for (i = GFC_INTRINSIC_BEGIN; i != GFC_INTRINSIC_END; i++)
+    {
+      if (i == INTRINSIC_USER)
+	continue;
+
+      mio_interface (check_access (gfc_current_ns->operator_access[i],
+				   gfc_current_ns->default_access)
+		     ? &gfc_current_ns->op[i] : NULL);
+    }
+
+  mio_rparen ();
+  write_char ('\n');
+  write_char ('\n');
+
+  mio_lparen ();
+  gfc_traverse_user_op (gfc_current_ns, write_operator);
+  mio_rparen ();
+  write_char ('\n');
+  write_char ('\n');
+
+  mio_lparen ();
+  write_generic (gfc_current_ns->sym_root);
+  mio_rparen ();
+  write_char ('\n');
+  write_char ('\n');
+
+  mio_lparen ();
+  write_blank_common ();
+  write_common (gfc_current_ns->common_root);
+  mio_rparen ();
+  write_char ('\n');
+  write_char ('\n');
+
+  mio_lparen ();
+  write_equiv ();
+  mio_rparen ();
+  write_char ('\n');
+  write_char ('\n');
+
+  mio_lparen ();
+  write_omp_udrs (gfc_current_ns->omp_udr_root);
+  mio_rparen ();
+  write_char ('\n');
+  write_char ('\n');
+
+  /* Write symbol information.  First we traverse all symbols in the
+     primary namespace, writing those that need to be written.
+     Sometimes writing one symbol will cause another to need to be
+     written.  A list of these symbols ends up on the write stack, and
+     we end by popping the bottom of the stack and writing the symbol
+     until the stack is empty.  */
+
+  mio_lparen ();
+
+  write_symbol0 (gfc_current_ns->sym_root);
+  while (write_symbol1 (pi_root))
+    /* Nothing.  */;
+
+  mio_rparen ();
+
+  write_char ('\n');
+  write_char ('\n');
+
+  mio_lparen ();
+  gfc_traverse_symtree (gfc_current_ns->sym_root, write_symtree);
+  mio_rparen ();
+}
+
+
+/* Read a CRC32 sum from the gzip trailer of a module file.  Returns
+   true on success, false on failure.  */
+
+static bool
+read_crc32_from_module_file (const char* filename, uLong* crc)
+{
+  FILE *file;
+  char buf[4];
+  unsigned int val;
+
+  /* Open the file in binary mode.  */
+  if ((file = fopen (filename, "rb")) == NULL)
+    return false;
+
+  /* The gzip crc32 value is found in the [END-8, END-4] bytes of the
+     file. See RFC 1952.  */
+  if (fseek (file, -8, SEEK_END) != 0)
+    {
+      fclose (file);
+      return false;
+    }
+
+  /* Read the CRC32.  */
+  if (fread (buf, 1, 4, file) != 4)
+    {
+      fclose (file);
+      return false;
+    }
+
+  /* Close the file.  */
+  fclose (file);
+
+  val = (buf[0] & 0xFF) + ((buf[1] & 0xFF) << 8) + ((buf[2] & 0xFF) << 16)
+    + ((buf[3] & 0xFF) << 24);
+  *crc = val;
+
+  /* For debugging, the CRC value printed in hexadecimal should match
+     the CRC printed by "zcat -l -v filename".
+     printf("CRC of file %s is %x\n", filename, val); */
+
+  return true;
+}
+
+
+/* Given module, dump it to disk.  If there was an error while
+   processing the module, dump_flag will be set to zero and we delete
+   the module file, even if it was already there.  */
+
+static void
+dump_module (const char *name, int dump_flag)
+{
+  int n;
+  char *filename, *filename_tmp;
+  uLong crc, crc_old;
+
+  module_name = gfc_get_string ("%s", name);
+
+  if (dump_smod)
+    {
+      name = submodule_name;
+      n = strlen (name) + strlen (SUBMODULE_EXTENSION) + 1;
+    }
+  else
+    n = strlen (name) + strlen (MODULE_EXTENSION) + 1;
+
+  if (gfc_option.module_dir != NULL)
+    {
+      n += strlen (gfc_option.module_dir);
+      filename = (char *) alloca (n);
+      strcpy (filename, gfc_option.module_dir);
+      strcat (filename, name);
+    }
+  else
+    {
+      filename = (char *) alloca (n);
+      strcpy (filename, name);
+    }
+
+  if (dump_smod)
+    strcat (filename, SUBMODULE_EXTENSION);
+  else
+  strcat (filename, MODULE_EXTENSION);
+
+  /* Name of the temporary file used to write the module.  */
+  filename_tmp = (char *) alloca (n + 1);
+  strcpy (filename_tmp, filename);
+  strcat (filename_tmp, "0");
+
+  /* There was an error while processing the module.  We delete the
+     module file, even if it was already there.  */
+  if (!dump_flag)
+    {
+      remove (filename);
+      return;
+    }
+
+  if (gfc_cpp_makedep ())
+    gfc_cpp_add_target (filename);
+
+  /* Write the module to the temporary file.  */
+  module_fp = gzopen (filename_tmp, "w");
+  if (module_fp == NULL)
+    gfc_fatal_error ("Can't open module file %qs for writing at %C: %s",
+		     filename_tmp, xstrerror (errno));
+
+  gzprintf (module_fp, "GFORTRAN module version '%s' created from %s\n",
+	    MOD_VERSION, gfc_source_file);
+
+  /* Write the module itself.  */
+  iomode = IO_OUTPUT;
+
+  init_pi_tree ();
+
+  write_module ();
+
+  free_pi_tree (pi_root);
+  pi_root = NULL;
+
+  write_char ('\n');
+
+  if (gzclose (module_fp))
+    gfc_fatal_error ("Error writing module file %qs for writing: %s",
+		     filename_tmp, xstrerror (errno));
+
+  /* Read the CRC32 from the gzip trailers of the module files and
+     compare.  */
+  if (!read_crc32_from_module_file (filename_tmp, &crc)
+      || !read_crc32_from_module_file (filename, &crc_old)
+      || crc_old != crc)
+    {
+      /* Module file have changed, replace the old one.  */
+      if (remove (filename) && errno != ENOENT)
+	gfc_fatal_error ("Can't delete module file %qs: %s", filename,
+			 xstrerror (errno));
+      if (rename (filename_tmp, filename))
+	gfc_fatal_error ("Can't rename module file %qs to %qs: %s",
+			 filename_tmp, filename, xstrerror (errno));
+    }
+  else
+    {
+      if (remove (filename_tmp))
+	gfc_fatal_error ("Can't delete temporary module file %qs: %s",
+			 filename_tmp, xstrerror (errno));
+    }
+}
+
+
+/* Suppress the output of a .smod file by module, if no module
+   procedures have been seen.  */
+static bool no_module_procedures;
+
+static void
+check_for_module_procedures (gfc_symbol *sym)
+{
+  if (sym && sym->attr.module_procedure)
+    no_module_procedures = false;
+}
+
+
+void
+gfc_dump_module (const char *name, int dump_flag)
+{
+  if (gfc_state_stack->state == COMP_SUBMODULE)
+    dump_smod = true;
+  else
+    dump_smod =false;
+
+  no_module_procedures = true;
+  gfc_traverse_ns (gfc_current_ns, check_for_module_procedures);
+
+  dump_module (name, dump_flag);
+
+  if (no_module_procedures || dump_smod)
+    return;
+
+  /* Write a submodule file from a module.  The 'dump_smod' flag switches
+     off the check for PRIVATE entities.  */
+  dump_smod = true;
+  submodule_name = module_name;
+  dump_module (name, dump_flag);
+  dump_smod = false;
+}
+
+static void
+create_intrinsic_function (const char *name, int id,
+			   const char *modname, intmod_id module,
+			   bool subroutine, gfc_symbol *result_type)
+{
+  gfc_intrinsic_sym *isym;
+  gfc_symtree *tmp_symtree;
+  gfc_symbol *sym;
+
+  tmp_symtree = gfc_find_symtree (gfc_current_ns->sym_root, name);
+  if (tmp_symtree)
+    {
+      if (tmp_symtree->n.sym && tmp_symtree->n.sym->module
+	  && strcmp (modname, tmp_symtree->n.sym->module) == 0)
+	return;
+      gfc_error ("Symbol %qs at %C already declared", name);
+      return;
+    }
+
+  gfc_get_sym_tree (name, gfc_current_ns, &tmp_symtree, false);
+  sym = tmp_symtree->n.sym;
+
+  if (subroutine)
+    {
+      gfc_isym_id isym_id = gfc_isym_id_by_intmod (module, id);
+      isym = gfc_intrinsic_subroutine_by_id (isym_id);
+      sym->attr.subroutine = 1;
+    }
+  else
+    {
+      gfc_isym_id isym_id = gfc_isym_id_by_intmod (module, id);
+      isym = gfc_intrinsic_function_by_id (isym_id);
+
+      sym->attr.function = 1;
+      if (result_type)
+	{
+	  sym->ts.type = BT_DERIVED;
+	  sym->ts.u.derived = result_type;
+	  sym->ts.is_c_interop = 1;
+	  isym->ts.f90_type = BT_VOID;
+	  isym->ts.type = BT_DERIVED;
+	  isym->ts.f90_type = BT_VOID;
+	  isym->ts.u.derived = result_type;
+	  isym->ts.is_c_interop = 1;
+	}
+    }
+  gcc_assert (isym);
+
+  sym->attr.flavor = FL_PROCEDURE;
+  sym->attr.intrinsic = 1;
+
+  sym->module = gfc_get_string ("%s", modname);
+  sym->attr.use_assoc = 1;
+  sym->from_intmod = module;
+  sym->intmod_sym_id = id;
+}
+
+
+/* Import the intrinsic ISO_C_BINDING module, generating symbols in
+   the current namespace for all named constants, pointer types, and
+   procedures in the module unless the only clause was used or a rename
+   list was provided.  */
+
+static void
+import_iso_c_binding_module (void)
+{
+  gfc_symbol *mod_sym = NULL, *return_type;
+  gfc_symtree *mod_symtree = NULL, *tmp_symtree;
+  gfc_symtree *c_ptr = NULL, *c_funptr = NULL;
+  const char *iso_c_module_name = "__iso_c_binding";
+  gfc_use_rename *u;
+  int i;
+  bool want_c_ptr = false, want_c_funptr = false;
+
+  /* Look only in the current namespace.  */
+  mod_symtree = gfc_find_symtree (gfc_current_ns->sym_root, iso_c_module_name);
+
+  if (mod_symtree == NULL)
+    {
+      /* symtree doesn't already exist in current namespace.  */
+      gfc_get_sym_tree (iso_c_module_name, gfc_current_ns, &mod_symtree,
+			false);
+
+      if (mod_symtree != NULL)
+	mod_sym = mod_symtree->n.sym;
+      else
+	gfc_internal_error ("import_iso_c_binding_module(): Unable to "
+			    "create symbol for %s", iso_c_module_name);
+
+      mod_sym->attr.flavor = FL_MODULE;
+      mod_sym->attr.intrinsic = 1;
+      mod_sym->module = gfc_get_string ("%s", iso_c_module_name);
+      mod_sym->from_intmod = INTMOD_ISO_C_BINDING;
+    }
+
+  /* Check whether C_PTR or C_FUNPTR are in the include list, if so, load it;
+     check also whether C_NULL_(FUN)PTR or C_(FUN)LOC are requested, which
+     need C_(FUN)PTR.  */
+  for (u = gfc_rename_list; u; u = u->next)
+    {
+      if (strcmp (c_interop_kinds_table[ISOCBINDING_NULL_PTR].name,
+		  u->use_name) == 0)
+        want_c_ptr = true;
+      else if (strcmp (c_interop_kinds_table[ISOCBINDING_LOC].name,
+		       u->use_name) == 0)
+        want_c_ptr = true;
+      else if (strcmp (c_interop_kinds_table[ISOCBINDING_NULL_FUNPTR].name,
+		       u->use_name) == 0)
+        want_c_funptr = true;
+      else if (strcmp (c_interop_kinds_table[ISOCBINDING_FUNLOC].name,
+		       u->use_name) == 0)
+        want_c_funptr = true;
+      else if (strcmp (c_interop_kinds_table[ISOCBINDING_PTR].name,
+                       u->use_name) == 0)
+	{
+	  c_ptr = generate_isocbinding_symbol (iso_c_module_name,
+                                               (iso_c_binding_symbol)
+							ISOCBINDING_PTR,
+                                               u->local_name[0] ? u->local_name
+                                                                : u->use_name,
+                                               NULL, false);
+	}
+      else if (strcmp (c_interop_kinds_table[ISOCBINDING_FUNPTR].name,
+                       u->use_name) == 0)
+	{
+	  c_funptr
+	     = generate_isocbinding_symbol (iso_c_module_name,
+					    (iso_c_binding_symbol)
+							ISOCBINDING_FUNPTR,
+					     u->local_name[0] ? u->local_name
+							      : u->use_name,
+					     NULL, false);
+	}
+    }
+
+  if ((want_c_ptr || !only_flag) && !c_ptr)
+    c_ptr = generate_isocbinding_symbol (iso_c_module_name,
+					 (iso_c_binding_symbol)
+							ISOCBINDING_PTR,
+					 NULL, NULL, only_flag);
+  if ((want_c_funptr || !only_flag) && !c_funptr)
+    c_funptr = generate_isocbinding_symbol (iso_c_module_name,
+					    (iso_c_binding_symbol)
+							ISOCBINDING_FUNPTR,
+					    NULL, NULL, only_flag);
+
+  /* Generate the symbols for the named constants representing
+     the kinds for intrinsic data types.  */
+  for (i = 0; i < ISOCBINDING_NUMBER; i++)
+    {
+      bool found = false;
+      for (u = gfc_rename_list; u; u = u->next)
+	if (strcmp (c_interop_kinds_table[i].name, u->use_name) == 0)
+	  {
+	    bool not_in_std;
+	    const char *name;
+	    u->found = 1;
+	    found = true;
+
+	    switch (i)
+	      {
+#define NAMED_FUNCTION(a,b,c,d) \
+	        case a: \
+		  not_in_std = (gfc_option.allow_std & d) == 0; \
+		  name = b; \
+		  break;
+#define NAMED_SUBROUTINE(a,b,c,d) \
+	        case a: \
+		  not_in_std = (gfc_option.allow_std & d) == 0; \
+		  name = b; \
+		  break;
+#define NAMED_INTCST(a,b,c,d) \
+	        case a: \
+		  not_in_std = (gfc_option.allow_std & d) == 0; \
+		  name = b; \
+		  break;
+#define NAMED_REALCST(a,b,c,d) \
+	        case a: \
+		  not_in_std = (gfc_option.allow_std & d) == 0; \
+		  name = b; \
+		  break;
+#define NAMED_CMPXCST(a,b,c,d) \
+	        case a: \
+		  not_in_std = (gfc_option.allow_std & d) == 0; \
+		  name = b; \
+		  break;
+#include "iso-c-binding.def"
+		default:
+		  not_in_std = false;
+		  name = "";
+	      }
+
+	    if (not_in_std)
+	      {
+		gfc_error ("The symbol %qs, referenced at %L, is not "
+			   "in the selected standard", name, &u->where);
+		continue;
+	      }
+
+	    switch (i)
+	      {
+#define NAMED_FUNCTION(a,b,c,d) \
+	        case a: \
+		  if (a == ISOCBINDING_LOC) \
+		    return_type = c_ptr->n.sym; \
+		  else if (a == ISOCBINDING_FUNLOC) \
+		    return_type = c_funptr->n.sym; \
+		  else \
+		    return_type = NULL; \
+		  create_intrinsic_function (u->local_name[0] \
+					     ? u->local_name : u->use_name, \
+					     a, iso_c_module_name, \
+					     INTMOD_ISO_C_BINDING, false, \
+					     return_type); \
+		  break;
+#define NAMED_SUBROUTINE(a,b,c,d) \
+	        case a: \
+		  create_intrinsic_function (u->local_name[0] ? u->local_name \
+							      : u->use_name, \
+                                             a, iso_c_module_name, \
+                                             INTMOD_ISO_C_BINDING, true, NULL); \
+		  break;
+#include "iso-c-binding.def"
+
+		case ISOCBINDING_PTR:
+		case ISOCBINDING_FUNPTR:
+		  /* Already handled above.  */
+		  break;
+		default:
+		  if (i == ISOCBINDING_NULL_PTR)
+		    tmp_symtree = c_ptr;
+		  else if (i == ISOCBINDING_NULL_FUNPTR)
+		    tmp_symtree = c_funptr;
+		  else
+		    tmp_symtree = NULL;
+		  generate_isocbinding_symbol (iso_c_module_name,
+					       (iso_c_binding_symbol) i,
+					       u->local_name[0]
+					       ? u->local_name : u->use_name,
+					       tmp_symtree, false);
+	      }
+	  }
+
+      if (!found && !only_flag)
+	{
+	  /* Skip, if the symbol is not in the enabled standard.  */
+	  switch (i)
+	    {
+#define NAMED_FUNCTION(a,b,c,d) \
+	      case a: \
+		if ((gfc_option.allow_std & d) == 0) \
+		  continue; \
+		break;
+#define NAMED_SUBROUTINE(a,b,c,d) \
+	      case a: \
+		if ((gfc_option.allow_std & d) == 0) \
+		  continue; \
+		break;
+#define NAMED_INTCST(a,b,c,d) \
+	      case a: \
+		if ((gfc_option.allow_std & d) == 0) \
+		  continue; \
+		break;
+#define NAMED_REALCST(a,b,c,d) \
+	      case a: \
+		if ((gfc_option.allow_std & d) == 0) \
+		  continue; \
+		break;
+#define NAMED_CMPXCST(a,b,c,d) \
+	      case a: \
+		if ((gfc_option.allow_std & d) == 0) \
+		  continue; \
+		break;
+#include "iso-c-binding.def"
+	      default:
+		; /* Not GFC_STD_* versioned.  */
+	    }
+
+	  switch (i)
+	    {
+#define NAMED_FUNCTION(a,b,c,d) \
+	      case a: \
+		if (a == ISOCBINDING_LOC) \
+		  return_type = c_ptr->n.sym; \
+		else if (a == ISOCBINDING_FUNLOC) \
+		  return_type = c_funptr->n.sym; \
+		else \
+		  return_type = NULL; \
+		create_intrinsic_function (b, a, iso_c_module_name, \
+					   INTMOD_ISO_C_BINDING, false, \
+					   return_type); \
+		break;
+#define NAMED_SUBROUTINE(a,b,c,d) \
+	      case a: \
+		create_intrinsic_function (b, a, iso_c_module_name, \
+					   INTMOD_ISO_C_BINDING, true, NULL); \
+		  break;
+#include "iso-c-binding.def"
+
+	      case ISOCBINDING_PTR:
+	      case ISOCBINDING_FUNPTR:
+		/* Already handled above.  */
+		break;
+	      default:
+		if (i == ISOCBINDING_NULL_PTR)
+		  tmp_symtree = c_ptr;
+		else if (i == ISOCBINDING_NULL_FUNPTR)
+		  tmp_symtree = c_funptr;
+		else
+		  tmp_symtree = NULL;
+		generate_isocbinding_symbol (iso_c_module_name,
+					     (iso_c_binding_symbol) i, NULL,
+					     tmp_symtree, false);
+	    }
+	}
+   }
+
+   for (u = gfc_rename_list; u; u = u->next)
+     {
+      if (u->found)
+	continue;
+
+      gfc_error ("Symbol %qs referenced at %L not found in intrinsic "
+		 "module ISO_C_BINDING", u->use_name, &u->where);
+     }
+}
+
+
+/* Add an integer named constant from a given module.  */
+
+static void
+create_int_parameter (const char *name, int value, const char *modname,
+		      intmod_id module, int id)
+{
+  gfc_symtree *tmp_symtree;
+  gfc_symbol *sym;
+
+  tmp_symtree = gfc_find_symtree (gfc_current_ns->sym_root, name);
+  if (tmp_symtree != NULL)
+    {
+      if (strcmp (modname, tmp_symtree->n.sym->module) == 0)
+	return;
+      else
+	gfc_error ("Symbol %qs already declared", name);
+    }
+
+  gfc_get_sym_tree (name, gfc_current_ns, &tmp_symtree, false);
+  sym = tmp_symtree->n.sym;
+
+  sym->module = gfc_get_string ("%s", modname);
+  sym->attr.flavor = FL_PARAMETER;
+  sym->ts.type = BT_INTEGER;
+  sym->ts.kind = gfc_default_integer_kind;
+  sym->value = gfc_get_int_expr (gfc_default_integer_kind, NULL, value);
+  sym->attr.use_assoc = 1;
+  sym->from_intmod = module;
+  sym->intmod_sym_id = id;
+}
+
+
+/* Value is already contained by the array constructor, but not
+   yet the shape.  */
+
+static void
+create_int_parameter_array (const char *name, int size, gfc_expr *value,
+			    const char *modname, intmod_id module, int id)
+{
+  gfc_symtree *tmp_symtree;
+  gfc_symbol *sym;
+
+  tmp_symtree = gfc_find_symtree (gfc_current_ns->sym_root, name);
+  if (tmp_symtree != NULL)
+    {
+      if (strcmp (modname, tmp_symtree->n.sym->module) == 0)
+	return;
+      else
+	gfc_error ("Symbol %qs already declared", name);
+    }
+
+  gfc_get_sym_tree (name, gfc_current_ns, &tmp_symtree, false);
+  sym = tmp_symtree->n.sym;
+
+  sym->module = gfc_get_string ("%s", modname);
+  sym->attr.flavor = FL_PARAMETER;
+  sym->ts.type = BT_INTEGER;
+  sym->ts.kind = gfc_default_integer_kind;
+  sym->attr.use_assoc = 1;
+  sym->from_intmod = module;
+  sym->intmod_sym_id = id;
+  sym->attr.dimension = 1;
+  sym->as = gfc_get_array_spec ();
+  sym->as->rank = 1;
+  sym->as->type = AS_EXPLICIT;
+  sym->as->lower[0] = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1);
+  sym->as->upper[0] = gfc_get_int_expr (gfc_default_integer_kind, NULL, size);
+
+  sym->value = value;
+  sym->value->shape = gfc_get_shape (1);
+  mpz_init_set_ui (sym->value->shape[0], size);
+}
+
+
+/* Add an derived type for a given module.  */
+
+static void
+create_derived_type (const char *name, const char *modname,
+		      intmod_id module, int id)
+{
+  gfc_symtree *tmp_symtree;
+  gfc_symbol *sym, *dt_sym;
+  gfc_interface *intr, *head;
+
+  tmp_symtree = gfc_find_symtree (gfc_current_ns->sym_root, name);
+  if (tmp_symtree != NULL)
+    {
+      if (strcmp (modname, tmp_symtree->n.sym->module) == 0)
+	return;
+      else
+	gfc_error ("Symbol %qs already declared", name);
+    }
+
+  gfc_get_sym_tree (name, gfc_current_ns, &tmp_symtree, false);
+  sym = tmp_symtree->n.sym;
+  sym->module = gfc_get_string ("%s", modname);
+  sym->from_intmod = module;
+  sym->intmod_sym_id = id;
+  sym->attr.flavor = FL_PROCEDURE;
+  sym->attr.function = 1;
+  sym->attr.generic = 1;
+
+  gfc_get_sym_tree (gfc_dt_upper_string (sym->name),
+		    gfc_current_ns, &tmp_symtree, false);
+  dt_sym = tmp_symtree->n.sym;
+  dt_sym->name = gfc_get_string ("%s", sym->name);
+  dt_sym->attr.flavor = FL_DERIVED;
+  dt_sym->attr.private_comp = 1;
+  dt_sym->attr.zero_comp = 1;
+  dt_sym->attr.use_assoc = 1;
+  dt_sym->module = gfc_get_string ("%s", modname);
+  dt_sym->from_intmod = module;
+  dt_sym->intmod_sym_id = id;
+
+  head = sym->generic;
+  intr = gfc_get_interface ();
+  intr->sym = dt_sym;
+  intr->where = gfc_current_locus;
+  intr->next = head;
+  sym->generic = intr;
+  sym->attr.if_source = IFSRC_DECL;
+}
+
+
+/* Read the contents of the module file into a temporary buffer.  */
+
+static void
+read_module_to_tmpbuf ()
+{
+  /* We don't know the uncompressed size, so enlarge the buffer as
+     needed.  */
+  int cursz = 4096;
+  int rsize = cursz;
+  int len = 0;
+
+  module_content = XNEWVEC (char, cursz);
+
+  while (1)
+    {
+      int nread = gzread (module_fp, module_content + len, rsize);
+      len += nread;
+      if (nread < rsize)
+	break;
+      cursz *= 2;
+      module_content = XRESIZEVEC (char, module_content, cursz);
+      rsize = cursz - len;
+    }
+
+  module_content = XRESIZEVEC (char, module_content, len + 1);
+  module_content[len] = '\0';
+
+  module_pos = 0;
+}
+
+
+/* USE the ISO_FORTRAN_ENV intrinsic module.  */
+
+static void
+use_iso_fortran_env_module (void)
+{
+  static char mod[] = "iso_fortran_env";
+  gfc_use_rename *u;
+  gfc_symbol *mod_sym;
+  gfc_symtree *mod_symtree;
+  gfc_expr *expr;
+  int i, j;
+
+  intmod_sym symbol[] = {
+#define NAMED_INTCST(a,b,c,d) { a, b, 0, d },
+#define NAMED_KINDARRAY(a,b,c,d) { a, b, 0, d },
+#define NAMED_DERIVED_TYPE(a,b,c,d) { a, b, 0, d },
+#define NAMED_FUNCTION(a,b,c,d) { a, b, c, d },
+#define NAMED_SUBROUTINE(a,b,c,d) { a, b, c, d },
+#include "iso-fortran-env.def"
+    { ISOFORTRANENV_INVALID, NULL, -1234, 0 } };
+
+  i = 0;
+#define NAMED_INTCST(a,b,c,d) symbol[i++].value = c;
+#include "iso-fortran-env.def"
+
+  /* Generate the symbol for the module itself.  */
+  mod_symtree = gfc_find_symtree (gfc_current_ns->sym_root, mod);
+  if (mod_symtree == NULL)
+    {
+      gfc_get_sym_tree (mod, gfc_current_ns, &mod_symtree, false);
+      gcc_assert (mod_symtree);
+      mod_sym = mod_symtree->n.sym;
+
+      mod_sym->attr.flavor = FL_MODULE;
+      mod_sym->attr.intrinsic = 1;
+      mod_sym->module = gfc_get_string ("%s", mod);
+      mod_sym->from_intmod = INTMOD_ISO_FORTRAN_ENV;
+    }
+  else
+    if (!mod_symtree->n.sym->attr.intrinsic)
+      gfc_error ("Use of intrinsic module %qs at %C conflicts with "
+		 "non-intrinsic module name used previously", mod);
+
+  /* Generate the symbols for the module integer named constants.  */
+
+  for (i = 0; symbol[i].name; i++)
+    {
+      bool found = false;
+      for (u = gfc_rename_list; u; u = u->next)
+	{
+	  if (strcmp (symbol[i].name, u->use_name) == 0)
+	    {
+	      found = true;
+	      u->found = 1;
+
+	      if (!gfc_notify_std (symbol[i].standard, "The symbol %qs, "
+				   "referenced at %L, is not in the selected "
+				   "standard", symbol[i].name, &u->where))
+	        continue;
+
+	      if ((flag_default_integer || flag_default_real_8)
+		  && symbol[i].id == ISOFORTRANENV_NUMERIC_STORAGE_SIZE)
+		gfc_warning_now (0, "Use of the NUMERIC_STORAGE_SIZE named "
+				 "constant from intrinsic module "
+				 "ISO_FORTRAN_ENV at %L is incompatible with "
+				 "option %qs", &u->where,
+				 flag_default_integer
+				   ? "-fdefault-integer-8"
+				   : "-fdefault-real-8");
+	      switch (symbol[i].id)
+		{
+#define NAMED_INTCST(a,b,c,d) \
+		case a:
+#include "iso-fortran-env.def"
+		  create_int_parameter (u->local_name[0] ? u->local_name
+							 : u->use_name,
+					symbol[i].value, mod,
+					INTMOD_ISO_FORTRAN_ENV, symbol[i].id);
+		  break;
+
+#define NAMED_KINDARRAY(a,b,KINDS,d) \
+		case a:\
+		  expr = gfc_get_array_expr (BT_INTEGER, \
+					     gfc_default_integer_kind,\
+					     NULL); \
+		  for (j = 0; KINDS[j].kind != 0; j++) \
+		    gfc_constructor_append_expr (&expr->value.constructor, \
+			gfc_get_int_expr (gfc_default_integer_kind, NULL, \
+					  KINDS[j].kind), NULL); \
+		  create_int_parameter_array (u->local_name[0] ? u->local_name \
+							 : u->use_name, \
+					      j, expr, mod, \
+					      INTMOD_ISO_FORTRAN_ENV, \
+					      symbol[i].id); \
+		  break;
+#include "iso-fortran-env.def"
+
+#define NAMED_DERIVED_TYPE(a,b,TYPE,STD) \
+		case a:
+#include "iso-fortran-env.def"
+                  create_derived_type (u->local_name[0] ? u->local_name
+							: u->use_name,
+				       mod, INTMOD_ISO_FORTRAN_ENV,
+				       symbol[i].id);
+		  break;
+
+#define NAMED_FUNCTION(a,b,c,d) \
+		case a:
+#include "iso-fortran-env.def"
+		  create_intrinsic_function (u->local_name[0] ? u->local_name
+							      : u->use_name,
+					     symbol[i].id, mod,
+					     INTMOD_ISO_FORTRAN_ENV, false,
+					     NULL);
+		  break;
+
+		default:
+		  gcc_unreachable ();
+		}
+	    }
+	}
+
+      if (!found && !only_flag)
+	{
+	  if ((gfc_option.allow_std & symbol[i].standard) == 0)
+	    continue;
+
+	  if ((flag_default_integer || flag_default_real_8)
+	      && symbol[i].id == ISOFORTRANENV_NUMERIC_STORAGE_SIZE)
+	    gfc_warning_now (0,
+			     "Use of the NUMERIC_STORAGE_SIZE named constant "
+			     "from intrinsic module ISO_FORTRAN_ENV at %C is "
+			     "incompatible with option %s",
+			     flag_default_integer
+				? "-fdefault-integer-8" : "-fdefault-real-8");
+
+	  switch (symbol[i].id)
+	    {
+#define NAMED_INTCST(a,b,c,d) \
+	    case a:
+#include "iso-fortran-env.def"
+	      create_int_parameter (symbol[i].name, symbol[i].value, mod,
+				    INTMOD_ISO_FORTRAN_ENV, symbol[i].id);
+	      break;
+
+#define NAMED_KINDARRAY(a,b,KINDS,d) \
+	    case a:\
+	      expr = gfc_get_array_expr (BT_INTEGER, gfc_default_integer_kind, \
+					 NULL); \
+	      for (j = 0; KINDS[j].kind != 0; j++) \
+		gfc_constructor_append_expr (&expr->value.constructor, \
+                      gfc_get_int_expr (gfc_default_integer_kind, NULL, \
+                                        KINDS[j].kind), NULL); \
+            create_int_parameter_array (symbol[i].name, j, expr, mod, \
+                                        INTMOD_ISO_FORTRAN_ENV, symbol[i].id);\
+            break;
+#include "iso-fortran-env.def"
+
+#define NAMED_DERIVED_TYPE(a,b,TYPE,STD) \
+	  case a:
+#include "iso-fortran-env.def"
+	    create_derived_type (symbol[i].name, mod, INTMOD_ISO_FORTRAN_ENV,
+				 symbol[i].id);
+	    break;
+
+#define NAMED_FUNCTION(a,b,c,d) \
+		case a:
+#include "iso-fortran-env.def"
+		  create_intrinsic_function (symbol[i].name, symbol[i].id, mod,
+					     INTMOD_ISO_FORTRAN_ENV, false,
+					     NULL);
+		  break;
+
+	  default:
+	    gcc_unreachable ();
+	  }
+	}
+    }
+
+  for (u = gfc_rename_list; u; u = u->next)
+    {
+      if (u->found)
+	continue;
+
+      gfc_error ("Symbol %qs referenced at %L not found in intrinsic "
+		     "module ISO_FORTRAN_ENV", u->use_name, &u->where);
+    }
+}
+
+
+/* Process a USE directive.  */
+
+static void
+gfc_use_module (gfc_use_list *module)
+{
+  char *filename;
+  gfc_state_data *p;
+  int c, line, start;
+  gfc_symtree *mod_symtree;
+  gfc_use_list *use_stmt;
+  locus old_locus = gfc_current_locus;
+
+  gfc_current_locus = module->where;
+  module_name = module->module_name;
+  gfc_rename_list = module->rename;
+  only_flag = module->only_flag;
+  current_intmod = INTMOD_NONE;
+
+  if (!only_flag)
+    gfc_warning_now (OPT_Wuse_without_only,
+		     "USE statement at %C has no ONLY qualifier");
+
+  if (gfc_state_stack->state == COMP_MODULE
+      || module->submodule_name == NULL)
+    {
+      filename = XALLOCAVEC (char, strlen (module_name)
+				   + strlen (MODULE_EXTENSION) + 1);
+      strcpy (filename, module_name);
+      strcat (filename, MODULE_EXTENSION);
+    }
+  else
+    {
+      filename = XALLOCAVEC (char, strlen (module->submodule_name)
+				   + strlen (SUBMODULE_EXTENSION) + 1);
+      strcpy (filename, module->submodule_name);
+      strcat (filename, SUBMODULE_EXTENSION);
+    }
+
+  /* First, try to find an non-intrinsic module, unless the USE statement
+     specified that the module is intrinsic.  */
+  module_fp = NULL;
+  if (!module->intrinsic)
+    module_fp = gzopen_included_file (filename, true, true);
+
+  /* Then, see if it's an intrinsic one, unless the USE statement
+     specified that the module is non-intrinsic.  */
+  if (module_fp == NULL && !module->non_intrinsic)
+    {
+      if (strcmp (module_name, "iso_fortran_env") == 0
+	  && gfc_notify_std (GFC_STD_F2003, "ISO_FORTRAN_ENV "
+			     "intrinsic module at %C"))
+       {
+	 use_iso_fortran_env_module ();
+	 free_rename (module->rename);
+	 module->rename = NULL;
+	 gfc_current_locus = old_locus;
+	 module->intrinsic = true;
+	 return;
+       }
+
+      if (strcmp (module_name, "iso_c_binding") == 0
+	  && gfc_notify_std (GFC_STD_F2003, "ISO_C_BINDING module at %C"))
+	{
+	  import_iso_c_binding_module();
+	  free_rename (module->rename);
+	  module->rename = NULL;
+	  gfc_current_locus = old_locus;
+	  module->intrinsic = true;
+	  return;
+	}
+
+      module_fp = gzopen_intrinsic_module (filename);
+
+      if (module_fp == NULL && module->intrinsic)
+	gfc_fatal_error ("Can't find an intrinsic module named %qs at %C",
+			 module_name);
+
+      /* Check for the IEEE modules, so we can mark their symbols
+	 accordingly when we read them.  */
+      if (strcmp (module_name, "ieee_features") == 0
+	  && gfc_notify_std (GFC_STD_F2003, "IEEE_FEATURES module at %C"))
+	{
+	  current_intmod = INTMOD_IEEE_FEATURES;
+	}
+      else if (strcmp (module_name, "ieee_exceptions") == 0
+	       && gfc_notify_std (GFC_STD_F2003,
+				  "IEEE_EXCEPTIONS module at %C"))
+	{
+	  current_intmod = INTMOD_IEEE_EXCEPTIONS;
+	}
+      else if (strcmp (module_name, "ieee_arithmetic") == 0
+	       && gfc_notify_std (GFC_STD_F2003,
+				  "IEEE_ARITHMETIC module at %C"))
+	{
+	  current_intmod = INTMOD_IEEE_ARITHMETIC;
+	}
+    }
+
+  if (module_fp == NULL)
+    {
+      if (gfc_state_stack->state != COMP_SUBMODULE
+	  && module->submodule_name == NULL)
+	gfc_fatal_error ("Can't open module file %qs for reading at %C: %s",
+			 filename, xstrerror (errno));
+      else
+	gfc_fatal_error ("Module file %qs has not been generated, either "
+			 "because the module does not contain a MODULE "
+			 "PROCEDURE or there is an error in the module.",
+			 filename);
+    }
+
+  /* Check that we haven't already USEd an intrinsic module with the
+     same name.  */
+
+  mod_symtree = gfc_find_symtree (gfc_current_ns->sym_root, module_name);
+  if (mod_symtree && mod_symtree->n.sym->attr.intrinsic)
+    gfc_error ("Use of non-intrinsic module %qs at %C conflicts with "
+	       "intrinsic module name used previously", module_name);
+
+  iomode = IO_INPUT;
+  module_line = 1;
+  module_column = 1;
+  start = 0;
+
+  read_module_to_tmpbuf ();
+  gzclose (module_fp);
+
+  /* Skip the first line of the module, after checking that this is
+     a gfortran module file.  */
+  line = 0;
+  while (line < 1)
+    {
+      c = module_char ();
+      if (c == EOF)
+	bad_module ("Unexpected end of module");
+      if (start++ < 3)
+	parse_name (c);
+      if ((start == 1 && strcmp (atom_name, "GFORTRAN") != 0)
+	  || (start == 2 && strcmp (atom_name, " module") != 0))
+	gfc_fatal_error ("File %qs opened at %C is not a GNU Fortran"
+			 " module file", filename);
+      if (start == 3)
+	{
+	  if (strcmp (atom_name, " version") != 0
+	      || module_char () != ' '
+	      || parse_atom () != ATOM_STRING
+	      || strcmp (atom_string, MOD_VERSION))
+	    gfc_fatal_error ("Cannot read module file %qs opened at %C,"
+			     " because it was created by a different"
+			     " version of GNU Fortran", filename);
+
+	  free (atom_string);
+	}
+
+      if (c == '\n')
+	line++;
+    }
+
+  /* Make sure we're not reading the same module that we may be building.  */
+  for (p = gfc_state_stack; p; p = p->previous)
+    if ((p->state == COMP_MODULE || p->state == COMP_SUBMODULE)
+	 && strcmp (p->sym->name, module_name) == 0)
+      gfc_fatal_error ("Can't USE the same %smodule we're building",
+		       p->state == COMP_SUBMODULE ? "sub" : "");
+
+  init_pi_tree ();
+  init_true_name_tree ();
+
+  read_module ();
+
+  free_true_name (true_name_root);
+  true_name_root = NULL;
+
+  free_pi_tree (pi_root);
+  pi_root = NULL;
+
+  XDELETEVEC (module_content);
+  module_content = NULL;
+
+  use_stmt = gfc_get_use_list ();
+  *use_stmt = *module;
+  use_stmt->next = gfc_current_ns->use_stmts;
+  gfc_current_ns->use_stmts = use_stmt;
+
+  gfc_current_locus = old_locus;
+}
+
+
+/* Remove duplicated intrinsic operators from the rename list.  */
+
+static void
+rename_list_remove_duplicate (gfc_use_rename *list)
+{
+  gfc_use_rename *seek, *last;
+
+  for (; list; list = list->next)
+    if (list->op != INTRINSIC_USER && list->op != INTRINSIC_NONE)
+      {
+	last = list;
+	for (seek = list->next; seek; seek = last->next)
+	  {
+	    if (list->op == seek->op)
+	      {
+		last->next = seek->next;
+		free (seek);
+	      }
+	    else
+	      last = seek;
+	  }
+      }
+}
+
+
+/* Process all USE directives.  */
+
+void
+gfc_use_modules (void)
+{
+  gfc_use_list *next, *seek, *last;
+
+  for (next = module_list; next; next = next->next)
+    {
+      bool non_intrinsic = next->non_intrinsic;
+      bool intrinsic = next->intrinsic;
+      bool neither = !non_intrinsic && !intrinsic;
+
+      for (seek = next->next; seek; seek = seek->next)
+	{
+	  if (next->module_name != seek->module_name)
+	    continue;
+
+	  if (seek->non_intrinsic)
+	    non_intrinsic = true;
+	  else if (seek->intrinsic)
+	    intrinsic = true;
+	  else
+	    neither = true;
+	}
+
+      if (intrinsic && neither && !non_intrinsic)
+	{
+	  char *filename;
+          FILE *fp;
+
+	  filename = XALLOCAVEC (char,
+				 strlen (next->module_name)
+				 + strlen (MODULE_EXTENSION) + 1);
+	  strcpy (filename, next->module_name);
+	  strcat (filename, MODULE_EXTENSION);
+	  fp = gfc_open_included_file (filename, true, true);
+	  if (fp != NULL)
+	    {
+	      non_intrinsic = true;
+	      fclose (fp);
+	    }
+	}
+
+      last = next;
+      for (seek = next->next; seek; seek = last->next)
+	{
+	  if (next->module_name != seek->module_name)
+	    {
+	      last = seek;
+	      continue;
+	    }
+
+	  if ((!next->intrinsic && !seek->intrinsic)
+	      || (next->intrinsic && seek->intrinsic)
+	      || !non_intrinsic)
+	    {
+	      if (!seek->only_flag)
+		next->only_flag = false;
+	      if (seek->rename)
+		{
+		  gfc_use_rename *r = seek->rename;
+		  while (r->next)
+		    r = r->next;
+		  r->next = next->rename;
+		  next->rename = seek->rename;
+		}
+	      last->next = seek->next;
+	      free (seek);
+	    }
+	  else
+	    last = seek;
+	}
+    }
+
+  for (; module_list; module_list = next)
+    {
+      next = module_list->next;
+      rename_list_remove_duplicate (module_list->rename);
+      gfc_use_module (module_list);
+      free (module_list);
+    }
+  gfc_rename_list = NULL;
+}
+
+
+void
+gfc_free_use_stmts (gfc_use_list *use_stmts)
+{
+  gfc_use_list *next;
+  for (; use_stmts; use_stmts = next)
+    {
+      gfc_use_rename *next_rename;
+
+      for (; use_stmts->rename; use_stmts->rename = next_rename)
+	{
+	  next_rename = use_stmts->rename->next;
+	  free (use_stmts->rename);
+	}
+      next = use_stmts->next;
+      free (use_stmts);
+    }
+}
+
+
+void
+gfc_module_init_2 (void)
+{
+  last_atom = ATOM_LPAREN;
+  gfc_rename_list = NULL;
+  module_list = NULL;
+}
+
+
+void
+gfc_module_done_2 (void)
+{
+  free_rename (gfc_rename_list);
+  gfc_rename_list = NULL;
+}