/* Build expressions with type checking for CHILL compiler.
   Copyright (C) 1992, 93, 1994, 1998, 1999 Free Software Foundation, Inc.

This file is part of GNU CC.

GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


/* This file is part of the CHILL front end.
   It contains routines to build C expressions given their operands,
   including computing the modes of the result, C-specific error checks,
   and some optimization.

   There are also routines to build RETURN_STMT nodes and CASE_STMT nodes,
   and to process initializations in declarations (since they work
   like a strange sort of assignment).  */

#include "config.h"
#include "system.h"
#include "tree.h"
#include "ch-tree.h"
#include "flags.h"
#include "rtl.h"
#include "expr.h"
#include "lex.h"
#include "toplev.h"

extern tree intQI_type_node;
extern tree intHI_type_node;
extern tree intSI_type_node;
extern tree intDI_type_node;
#if HOST_BITS_PER_WIDE_INT >= 64
extern tree intTI_type_node;
#endif

extern tree unsigned_intQI_type_node;
extern tree unsigned_intHI_type_node;
extern tree unsigned_intSI_type_node;
extern tree unsigned_intDI_type_node;
#if HOST_BITS_PER_WIDE_INT >= 64
extern tree unsigned_intTI_type_node;
#endif

/* forward declarations */
static int chill_l_equivalent PROTO((tree, tree, struct mode_chain*));
static tree extract_constant_from_buffer PROTO((tree, unsigned char *, int));
static int expand_constant_to_buffer PROTO((tree, unsigned char *, int));

/*
 * This function checks an array access.
 * It calls error (ERROR_MESSAGE) if the condition (index <= domain max value
 *                                     index >= domain min value)
 *                   is not met at compile time,
 *         If a runtime test is required and permitted,
 *         check_expression is used to do so.
 * the global RANGE_CHECKING flags controls the
 * generation of runtime checking code.
 */
tree
valid_array_index_p (array, idx, error_message, is_varying_lhs)
     tree array, idx;
     char *error_message;
     int is_varying_lhs;
{
  tree cond, low_limit, high_cond, atype, domain;
  tree orig_index = idx;
  enum chill_tree_code condition;

  if (array == NULL_TREE || TREE_CODE (array) == ERROR_MARK
      || idx == NULL_TREE || TREE_CODE (idx) == ERROR_MARK)
    return error_mark_node;
  
  if (TREE_CODE (idx) == TYPE_DECL
      || TREE_CODE_CLASS (TREE_CODE (idx)) == 't')
    {
      error ("array or string index is a mode (instead of a value)");
      return error_mark_node;
    }

  atype = TREE_TYPE (array);

  if (chill_varying_type_p (atype))
    {
      domain = TYPE_DOMAIN (CH_VARYING_ARRAY_TYPE (atype));
      high_cond = build_component_ref (array, var_length_id);
      if (chill_varying_string_type_p (atype))
	{
	  if (is_varying_lhs)
	    condition = GT_EXPR;
	  else
	    condition = GE_EXPR;
	}
      else
	condition = GT_EXPR;
    }
  else
    {
      domain = TYPE_DOMAIN (atype);
      high_cond = TYPE_MAX_VALUE (domain);
      condition = GT_EXPR;
    }

  if (CH_STRING_TYPE_P (atype))
    {
      if (! CH_SIMILAR (TREE_TYPE (orig_index), integer_type_node))
	{
	  error ("index is not an integer expression");
	  return error_mark_node;
	}
    }
  else
    {
      if (! CH_COMPATIBLE (orig_index, domain))
	{
	  error ("index not compatible with index mode");
	  return error_mark_node;
	}
    }

  /* Convert BOOLS(1) to BOOL and CHARS(1) to CHAR. */
  if (flag_old_strings)
    {
      idx = convert_to_discrete (idx);
      if (idx == NULL) /* should never happen */
	error ("index is not discrete");
    }

  /* we know we'll refer to this value twice */
  if (range_checking)
    idx = save_expr (idx);

  low_limit = TYPE_MIN_VALUE (domain);
  high_cond = build_compare_discrete_expr (condition, idx, high_cond);

  /* an invalid index expression meets this condition */
  cond = fold (build (TRUTH_ORIF_EXPR, boolean_type_node,
	   build_compare_discrete_expr (LT_EXPR, idx, low_limit),
	     high_cond));

  /* strip a redundant NOP_EXPR */
  if (TREE_CODE (cond) == NOP_EXPR
      && TREE_TYPE (cond) == boolean_type_node
      && TREE_CODE (TREE_OPERAND (cond, 0)) == INTEGER_CST)
    cond = TREE_OPERAND (cond, 0);
      
  idx = convert (CH_STRING_TYPE_P (atype) ? integer_type_node : domain,
		 idx);

  if (TREE_CODE (cond) == INTEGER_CST)
    {
      if (tree_int_cst_equal (cond, boolean_false_node))
	return idx;       /* condition met at compile time */
      error (error_message); /* condition failed at compile time */
      return error_mark_node;
    }
  else if (range_checking)
    {
      /* FIXME: often, several of these conditions will
	 be generated for the same source file and line number.
	 A great optimization would be to share the
	 cause_exception function call among them rather
	 than generating a cause_exception call for each. */
      return check_expression (idx, cond,
			       ridpointers[(int) RID_RANGEFAIL]);
    }
  else
    return idx;           /* don't know at compile time */
}

/*
 * Extract a slice from an array, which could look like a
 * SET_TYPE if it's a bitstring.  The array could also be VARYING
 * if the element type is CHAR.  The min_value and length values 
 * must have already been checked with valid_array_index_p.  No 
 * checking is done here.
 */
tree
build_chill_slice (array, min_value, length)
     tree array, min_value, length;
{
  tree result;
  tree array_type = TREE_TYPE (array);

  if (!CH_REFERABLE (array) && TREE_CODE (array) != SAVE_EXPR
      && (TREE_CODE (array) != COMPONENT_REF
	   || TREE_CODE (TREE_OPERAND (array, 0)) != SAVE_EXPR))
    {
      if (!TREE_CONSTANT (array))
	warning ("possible internal error - slice argument is neither referable nor constant");
      else
	{
	  /* Force to storage.
	     NOTE:  This could mean multiple identical copies of
	     the same constant.  FIXME. */
	  tree mydecl = decl_temp1 (get_unique_identifier("SLICEE"),
				    array_type, 1, array, 0, 0);
	  TREE_READONLY (mydecl) = 1;
	  /* mark_addressable (mydecl); FIXME: necessary? */
	  array = mydecl;
	}
    }

  /*
     The code-generation which uses a slice tree needs not only to
     know the dynamic upper and lower limits of that slice, but the
     original static allocation, to use to build temps where one or both
     of the dynamic limits must be calculated at runtime..  We pass the
     dynamic size by building a new array_type whose limits are the
     min_value and min_value + length values passed to us.  
     
     The static allocation info is passed by using the parent array's
     limits to compute a temp_size, which is passed in the lang_specific
     field of the slice_type.
   */
     
  if (TREE_CODE (array_type) == ARRAY_TYPE)
    {
      tree domain_type = TYPE_DOMAIN (array_type);
      tree domain_min = TYPE_MIN_VALUE (domain_type);
      tree domain_max = fold (build (PLUS_EXPR, domain_type,
				     domain_min,
				     size_binop (MINUS_EXPR,
						 length, integer_one_node)));
      tree index_type = build_chill_range_type (TYPE_DOMAIN (array_type),
						domain_min,
						domain_max);

      tree element_type = TREE_TYPE (array_type);
      tree slice_type = build_simple_array_type (element_type, index_type, NULL_TREE);
      tree slice_pointer_type;
      tree max_size;

      if (CH_CHARS_TYPE_P (array_type))
	MARK_AS_STRING_TYPE (slice_type);
      else
	TYPE_PACKED (slice_type) = TYPE_PACKED (array_type);

      SET_CH_NOVELTY (slice_type, CH_NOVELTY (array_type));

      if (TREE_CONSTANT (array) && TREE_CODE (min_value) == INTEGER_CST
	  && TREE_CODE (length) == INTEGER_CST)
	{
	  int type_size = int_size_in_bytes (array_type);
	  unsigned char *buffer = (unsigned char*) alloca (type_size);
	  int delta = int_size_in_bytes (element_type)
	    * (TREE_INT_CST_LOW (min_value) - TREE_INT_CST_LOW (domain_min));
	  bzero (buffer, type_size);
	  if (expand_constant_to_buffer (array, buffer, type_size))
	    {
	      result = extract_constant_from_buffer (slice_type,
						     buffer + delta,
						     type_size - delta);
	      if (result)
		return result;
	    }
	}

      /* Kludge used by case CONCAT_EXPR in chill_expand_expr.
	 Set TYPE_ARRAY_MAX_SIZE to a constant upper bound on the
	 bytes needed. */
      max_size = size_in_bytes (slice_type);
      if (TREE_CODE (max_size) != INTEGER_CST)
	{
	  max_size = TYPE_ARRAY_MAX_SIZE (array_type);
	  if (max_size == NULL_TREE)
	    max_size = size_in_bytes (array_type);
	}
      TYPE_ARRAY_MAX_SIZE (slice_type) = max_size;

      mark_addressable (array);
      /* Contruct a SLICE_EXPR to represent a slice of a packed array of bits. */
      if (TYPE_PACKED (array_type))
	{
	  if (pass == 2 && TREE_CODE (length) != INTEGER_CST)
	    {
	      sorry ("bit array slice with non-constant length");
	      return error_mark_node;
	    }
	  if (domain_min && ! integer_zerop (domain_min))
	    min_value = size_binop (MINUS_EXPR, min_value,
				    convert (sizetype, domain_min));
	  result = build (SLICE_EXPR, slice_type, array, min_value, length);
	  TREE_READONLY (result)
	    = TREE_READONLY (array) | TYPE_READONLY (TREE_TYPE (array_type));
	  return result;
	}

      slice_pointer_type = build_chill_pointer_type (slice_type);
      if (TREE_CODE (min_value) == INTEGER_CST
	  && domain_min && TREE_CODE (domain_min) == INTEGER_CST
	  && compare_int_csts (EQ_EXPR, min_value, domain_min))
	result = fold (build1 (ADDR_EXPR, slice_pointer_type, array));
      else
	{
	  min_value = convert (sizetype, min_value);
	  if (domain_min && ! integer_zerop (domain_min))
	    min_value = size_binop (MINUS_EXPR, min_value,
				    convert (sizetype, domain_min));
	  min_value = size_binop (MULT_EXPR, min_value,
				  size_in_bytes (element_type));
	  result = fold (build (PLUS_EXPR, slice_pointer_type,
				build1 (ADDR_EXPR, slice_pointer_type,
					array),
				convert (slice_pointer_type, min_value)));
	}
      /* Return the final array value. */
      result = fold (build1 (INDIRECT_REF, slice_type, result));
      TREE_READONLY (result)
	= TREE_READONLY (array) | TYPE_READONLY (element_type);
      return result;
    }
  else if (TREE_CODE (array_type) == SET_TYPE)  /* actually a bitstring */
    {
      if (pass == 2 && TREE_CODE (length) != INTEGER_CST)
	{
	  sorry ("bitstring slice with non-constant length");
	  return error_mark_node;
	}
      result = build (SLICE_EXPR, build_bitstring_type (length),
		      array, min_value, length);
      TREE_READONLY (result)
	= TREE_READONLY (array) | TYPE_READONLY (TREE_TYPE (array_type));
      return result;
    }
  else if (chill_varying_type_p (array_type))
      return build_chill_slice (varying_to_slice (array), min_value, length);
  else
    {
      error ("slice operation on non-array, non-bitstring value not supported");
      return error_mark_node;
    }
}

static tree
build_empty_string (type)
     tree type;
{
  int orig_pass = pass;
  tree range, result;

  range = build_chill_range_type (type, integer_zero_node,
				  integer_minus_one_node);
  result = build_chill_array_type (type,
	     tree_cons (NULL_TREE, range, NULL_TREE), 0, NULL_TREE);
  pass = 2;
  range = build_chill_range_type (type, integer_zero_node,
				  integer_minus_one_node);
  result = build_chill_array_type (type,
	     tree_cons (NULL_TREE, range, NULL_TREE), 0, NULL_TREE);
  pass = orig_pass;

  return decl_temp1 (get_unique_identifier ("EMPTY_STRING"),
		     result, 0, NULL_TREE, 0, 0);
}

/* We build the runtime range-checking as a separate list
 * rather than making a compound_expr with min_value
 * (for example), to control when that comparison gets 
 * generated.  We cannot allow it in a TYPE_MAX_VALUE or
 * TYPE_MIN_VALUE expression, for instance, because that code 
 * will get generated when the slice is laid out, which would 
 * put it outside the scope of an exception handler for the 
 * statement we're generating.  I.e. we would be generating
 * cause_exception calls which might execute before the
 * necessary ch_link_handler call.
 */
tree
build_chill_slice_with_range (array, min_value, max_value)
     tree array, min_value, max_value;
{
  if (array == NULL_TREE || TREE_CODE (array) == ERROR_MARK
      || min_value == NULL_TREE || TREE_CODE(min_value) == ERROR_MARK
      || max_value == NULL_TREE || TREE_CODE(max_value) == ERROR_MARK)
    return error_mark_node;

  if (TREE_TYPE (array) == NULL_TREE
      || (TREE_CODE (TREE_TYPE (array)) != ARRAY_TYPE
	  && TREE_CODE (TREE_TYPE (array)) != SET_TYPE
	  && !chill_varying_type_p (TREE_TYPE (array))))
    {
      error ("can only take slice of array or string");
      return error_mark_node;
    }

  array = save_if_needed (array);

  /* FIXME: test here for max_value >= min_value, except
     for max_value == -1, min_value == 0 (empty string) */
  min_value = valid_array_index_p (array, min_value,
				   "slice lower limit out-of-range", 0);
  if (TREE_CODE (min_value) == ERROR_MARK)
    return min_value;

  /* FIXME: suppress this test if max_value is the LENGTH of a 
     varying array, which has presumably already been checked. */
  max_value = valid_array_index_p (array, max_value,
				   "slice upper limit out-of-range", 0);
  if (TREE_CODE (max_value) == ERROR_MARK)
    return error_mark_node;

  if (TREE_CODE (min_value) == INTEGER_CST
      && TREE_CODE (max_value) == INTEGER_CST
      && tree_int_cst_lt (max_value, min_value))
    return build_empty_string (TREE_TYPE (TREE_TYPE (array)));

  return build_chill_slice (array, min_value,
	     save_expr (size_binop (PLUS_EXPR,
	       size_binop (MINUS_EXPR, max_value, min_value),
				    integer_one_node)));
}


tree
build_chill_slice_with_length (array, min_value, length)
     tree array, min_value, length;
{
  tree max_index;
  tree cond, high_cond, atype;

  if (array == NULL_TREE || TREE_CODE (array) == ERROR_MARK
      || min_value == NULL_TREE || TREE_CODE(min_value) == ERROR_MARK
      || length == NULL_TREE || TREE_CODE(length) == ERROR_MARK)
    return error_mark_node;

  if (TREE_TYPE (array) == NULL_TREE
      || (TREE_CODE (TREE_TYPE (array)) != ARRAY_TYPE
	  && TREE_CODE (TREE_TYPE (array)) != SET_TYPE
	  && !chill_varying_type_p (TREE_TYPE (array))))
    {
      error ("can only take slice of array or string");
      return error_mark_node;
    }

  if (TREE_CONSTANT (length) 
      && tree_int_cst_lt (length, integer_zero_node))
    return build_empty_string (TREE_TYPE (TREE_TYPE (array)));

  array = save_if_needed (array);
  min_value = save_expr (min_value);
  length = save_expr (length);

  if (! CH_SIMILAR (TREE_TYPE (length), integer_type_node))
    {
      error ("slice length is not an integer");
      length = integer_one_node;
    }

  max_index = size_binop (MINUS_EXPR, 
	        size_binop (PLUS_EXPR, length, min_value),
			  integer_one_node);
  max_index = convert_to_class (chill_expr_class (min_value), max_index);

  min_value = valid_array_index_p (array, min_value,
				   "slice start index out-of-range", 0);
  if (TREE_CODE (min_value) == ERROR_MARK)
    return error_mark_node;

  atype = TREE_TYPE (array);

  if (chill_varying_type_p (atype))
    high_cond = build_component_ref (array, var_length_id);
  else
    high_cond = TYPE_MAX_VALUE (TYPE_DOMAIN (atype));

  /* an invalid index expression meets this condition */
  cond = fold (build (TRUTH_ORIF_EXPR, boolean_type_node,
		      build_compare_discrete_expr (LT_EXPR,
						   length, integer_zero_node),
		      build_compare_discrete_expr (GT_EXPR,
						   max_index, high_cond)));

  if (TREE_CODE (cond) == INTEGER_CST)
    {
      if (! tree_int_cst_equal (cond, boolean_false_node))
	{
	  error ("slice length out-of-range");
	  return error_mark_node;
	}
	  
    }
  else if (range_checking)
    {
      min_value = check_expression (min_value, cond,
				    ridpointers[(int) RID_RANGEFAIL]);
    }

  return build_chill_slice (array, min_value, length);
}

tree
build_chill_array_ref (array, indexlist)
     tree array, indexlist;
{
  tree idx;

  if (array == NULL_TREE || TREE_CODE (array) == ERROR_MARK)
    return error_mark_node;
  if (indexlist == NULL_TREE || TREE_CODE (indexlist) == ERROR_MARK)
    return error_mark_node;

  idx = TREE_VALUE (indexlist);   /* handle first index */

  idx = valid_array_index_p (array, idx,
			     "array index out-of-range", 0);
  if (TREE_CODE (idx) == ERROR_MARK)
    return error_mark_node;

  array = build_chill_array_ref_1 (array, idx);

  if (array && TREE_CODE (array) != ERROR_MARK 
      && TREE_CHAIN (indexlist))
    {
      /* Z.200 (1988) section 4.2.8 says that:
	 <array> '(' <expression {',' <expression> }* ')'
	 is derived syntax (i.e. syntactic sugar) for:
	 <array> '(' <expression ')' { '(' <expression> ')' }*
	 The intent is clear if <array> has mode: ARRAY (...) ARRAY (...) XXX.
	 But what if <array> has mode: ARRAY (...) CHARS (N)
	 or: ARRAY (...) BOOLS (N).
	 Z.200 doesn't explicitly prohibit it, but the intent is unclear.
	 We'll allow it, since it seems reasonable and useful.
	 However, we won't allow it if <array> is:
	 ARRAY (...) PROC (...).
	 (The latter would make sense if we allowed general
	 Currying, which Chill doesn't.)  */
      if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE
	  || chill_varying_type_p (TREE_TYPE (array))
	  || CH_BOOLS_TYPE_P (TREE_TYPE (array)))
	array = build_generalized_call (array, TREE_CHAIN (indexlist));
      else
	error ("too many index expressions");
    }
  return array;
}

/*
 * Don't error check the index in here.  It's supposed to be 
 * checked by the caller.
 */
tree
build_chill_array_ref_1 (array, idx)
     tree array, idx;
{
  tree type;
  tree domain;
  tree rval;

  if (array == NULL_TREE || TREE_CODE (array) == ERROR_MARK
      || idx == NULL_TREE || TREE_CODE (idx) == ERROR_MARK)
    return error_mark_node;

  if (chill_varying_type_p (TREE_TYPE (array)))
    array = varying_to_slice (array);

  domain = TYPE_DOMAIN (TREE_TYPE (array));

#if 0
  if (! integer_zerop (TYPE_MIN_VALUE (domain)))
    {
      /* The C part of the compiler doesn't understand how to do
	 arithmetic with dissimilar enum types.  So we check compatability
	 here, and perform the math in INTEGER_TYPE.  */
      if (TREE_CODE (TREE_TYPE (idx)) == ENUMERAL_TYPE
	  && chill_comptypes (TREE_TYPE (idx), domain, 0))
	idx = convert (TREE_TYPE (TYPE_MIN_VALUE (domain)), idx);
      idx = build_binary_op (MINUS_EXPR, idx, TYPE_MIN_VALUE (domain), 0);
    }
#endif

  if (CH_STRING_TYPE_P (TREE_TYPE (array)))
    {
      /* Could be bitstring or char string.  */
      if (TREE_TYPE (TREE_TYPE (array)) == boolean_type_node)
	{
	  rval = build (SET_IN_EXPR, boolean_type_node, idx, array);
	  TREE_READONLY (rval) = TREE_READONLY (array);
	  return rval;
	}
    }

  if (!discrete_type_p (TREE_TYPE (idx)))
    {
      error ("array index is not discrete");
      return error_mark_node;
    }

  /* An array that is indexed by a non-constant
     cannot be stored in a register; we must be able to do
     address arithmetic on its address.
     Likewise an array of elements of variable size.  */
  if (TREE_CODE (idx) != INTEGER_CST
      || (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0
	  && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) != INTEGER_CST))
    {
      if (mark_addressable (array) == 0)
	return error_mark_node;
    }

  type = TREE_TYPE (TREE_TYPE (array));

  /* Do constant folding */
  if (TREE_CODE (idx) == INTEGER_CST && TREE_CONSTANT (array))
    {
      struct ch_class class;
      class.kind = CH_VALUE_CLASS;
      class.mode = type;

      if (TREE_CODE (array) == CONSTRUCTOR)
	{
	  tree list = CONSTRUCTOR_ELTS (array);
	  for ( ; list != NULL_TREE; list = TREE_CHAIN (list))
	    {
	      if (tree_int_cst_equal (TREE_PURPOSE (list), idx))
		return convert_to_class (class, TREE_VALUE (list));
	    }
	}
      else if (TREE_CODE (array) == STRING_CST
	       && CH_CHARS_TYPE_P (TREE_TYPE (array)))
	{
	  HOST_WIDE_INT i = TREE_INT_CST_LOW (idx);
	  if (i >= 0 && i < TREE_STRING_LENGTH (array))
	    {
	      char ch = TREE_STRING_POINTER (array) [i];
	      return convert_to_class (class,
				       build_int_2 ((unsigned char)ch, 0));
	    }
	}
    }

  if (TYPE_PACKED (TREE_TYPE (array)))
    rval = build (PACKED_ARRAY_REF, type, array, idx);
  else
    rval = build (ARRAY_REF, type, array, idx);

  /* Array ref is const/volatile if the array elements are
     or if the array is.  */
  TREE_READONLY (rval) = TREE_READONLY (array) | TYPE_READONLY (type);
  TREE_SIDE_EFFECTS (rval)
    |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
	| TREE_SIDE_EFFECTS (array));
  TREE_THIS_VOLATILE (rval)
    |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
	/* This was added by rms on 16 Nov 91.
	   It fixes  vol struct foo *a;  a->elts[1] 
	   in an inline function.
	   Hope it doesn't break something else.  */
	| TREE_THIS_VOLATILE (array));
  return fold (rval);
}

tree
build_chill_bitref (bitstring, indexlist)
     tree bitstring, indexlist;
{
  if (TREE_CODE (bitstring) == ERROR_MARK)
    return bitstring;
  if (TREE_CODE (indexlist) == ERROR_MARK)
    return indexlist;

  if (TREE_CHAIN (indexlist) != NULL_TREE)
    {
      error ("invalid compound index for bitstring mode");
      return error_mark_node;
    }

  if (TREE_CODE (indexlist) == TREE_LIST)
    {
      tree result = build (SET_IN_EXPR, boolean_type_node,
			   TREE_VALUE (indexlist), bitstring);
      TREE_READONLY (result) = TREE_READONLY (bitstring);
      return result;
    }
  else abort ();
}


int
discrete_type_p (type)
     tree type;
{
  return INTEGRAL_TYPE_P (type);
}

/* Checks that EXP has discrete type, or can be converted to discrete.
   Otherwise, returns NULL_TREE.
   Normally returns the (possibly-converted) EXP. */

tree
convert_to_discrete (exp)
     tree exp;
{
  if (! discrete_type_p (TREE_TYPE (exp)))
    {
      if (flag_old_strings)
	{
	  if (CH_CHARS_ONE_P (TREE_TYPE (exp)))
	    return convert (char_type_node, exp);
	  if (CH_BOOLS_ONE_P (TREE_TYPE (exp)))
	    return convert (boolean_type_node, exp);
	}
      return NULL_TREE;
    }
  return exp;
}

/* Write into BUFFER the target-machine representation of VALUE.
   Returns 1 on success, or 0 on failure. (Either the VALUE was
   not constant, or we don't know how to do the conversion.) */

static int
expand_constant_to_buffer (value, buffer, buf_size)
     tree value;
     unsigned char *buffer; 
     int buf_size;
{
  tree type = TREE_TYPE (value);
  int size = int_size_in_bytes (type);
  int i;
  if (size < 0 || size > buf_size)
    return 0;
  switch (TREE_CODE (value))
    {
    case INTEGER_CST:
      {
	HOST_WIDE_INT lo = TREE_INT_CST_LOW (value);
	HOST_WIDE_INT hi = TREE_INT_CST_HIGH (value);
	for (i = 0; i < size; i++)
	  {
	    /* Doesn't work if host and target BITS_PER_UNIT differ. */
	    unsigned char byte = lo & ((1 << BITS_PER_UNIT) - 1);
	    if (BYTES_BIG_ENDIAN)
	      buffer[size - i - 1] = byte;
	    else
	      buffer[i] = byte;
	    rshift_double (lo, hi, BITS_PER_UNIT, BITS_PER_UNIT * size,
			   &lo, &hi, 0);
	  }
      }
      break;
    case STRING_CST:
      {
	size = TREE_STRING_LENGTH (value);
	if (size > buf_size)
	  return 0;
	bcopy (TREE_STRING_POINTER (value), buffer, size);
	break;
      }
    case CONSTRUCTOR:
      if (TREE_CODE (type) == ARRAY_TYPE)
	{
	  tree element_type = TREE_TYPE (type);
	  int element_size = int_size_in_bytes (element_type);
	  tree list = CONSTRUCTOR_ELTS (value);
	  HOST_WIDE_INT next_index;
	  HOST_WIDE_INT min_index = 0;
	  if (element_size < 0)
	    return 0;

	  if (TYPE_DOMAIN (type) != 0)
	    {
	      tree min_val = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
	      if (min_val)
		{
		  if (TREE_CODE (min_val) != INTEGER_CST)
		    return 0;
		  else
		    min_index = TREE_INT_CST_LOW (min_val);
		}
	    }

	  next_index = min_index;

	  for (; list != NULL_TREE; list = TREE_CHAIN (list))
	    {
	      HOST_WIDE_INT offset;
	      HOST_WIDE_INT last_index;
	      tree purpose = TREE_PURPOSE (list);
	      if (purpose)
		{
		  if (TREE_CODE (purpose) == INTEGER_CST)
		    last_index = next_index = TREE_INT_CST_LOW (purpose);
		  else if (TREE_CODE (purpose) == RANGE_EXPR)
		    {
		      next_index = TREE_INT_CST_LOW (TREE_OPERAND(purpose, 0));
		      last_index = TREE_INT_CST_LOW (TREE_OPERAND(purpose, 1));
		    }
		  else
		    return 0;
		}
	      else
		last_index = next_index;
	      for ( ; next_index <= last_index; next_index++)
		{
		  offset = (next_index - min_index) * element_size;
		  if (!expand_constant_to_buffer (TREE_VALUE (list),
						  buffer + offset,
						  buf_size - offset))
		    return 0;
		}
	    }
	  break;
	}
      else if (TREE_CODE (type) == RECORD_TYPE)
	{
	  tree list = CONSTRUCTOR_ELTS (value);
	  for (; list != NULL_TREE; list = TREE_CHAIN (list))
	    {
	      tree field = TREE_PURPOSE (list);
	      HOST_WIDE_INT offset;
	      if (field == NULL_TREE || TREE_CODE (field) != FIELD_DECL)
		return 0;
	      if (DECL_BIT_FIELD (field))
		return 0;
	      offset = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
		/ BITS_PER_UNIT;
	      if (!expand_constant_to_buffer (TREE_VALUE (list),
					      buffer + offset,
					      buf_size - offset))
		return 0;
	    }
	  break;
	}
      else if (TREE_CODE (type) == SET_TYPE)
	{
	  if (get_set_constructor_bytes (value, buffer, buf_size)
	      != NULL_TREE)
	    return 0;
	}
      break;
    default:
      return 0;
    }
  return 1;
}

/* Given that BUFFER contains a target-machine representation of
   a value of type TYPE, return that value as a tree.
   Returns NULL_TREE on failure. (E.g. the TYPE might be variable size,
   or perhaps we don't know how to do the conversion.) */

static tree
extract_constant_from_buffer (type, buffer, buf_size)
     tree type;
     unsigned char *buffer;
     int buf_size;
{
  tree value;
  int size = int_size_in_bytes (type);
  int i;
  if (size < 0 || size > buf_size)
    return 0;
  switch (TREE_CODE (type))
    {
    case INTEGER_TYPE:
    case CHAR_TYPE:
    case BOOLEAN_TYPE:
    case ENUMERAL_TYPE:
    case POINTER_TYPE:
      {
	HOST_WIDE_INT lo = 0, hi = 0;
	/* Accumulate (into (lo,hi) the bytes (from buffer). */
	for (i = size; --i >= 0; )
	  {
	    unsigned char byte;
	    /* Get next byte (in big-endian order). */
	    if (BYTES_BIG_ENDIAN)
	      byte = buffer[size - i - 1];
	    else
	      byte = buffer[i];
	    lshift_double (lo, hi, BITS_PER_UNIT, TYPE_PRECISION (type),
			   &lo, &hi, 0);
	    add_double (lo, hi, byte, 0, &lo, &hi);
	  }
	value = build_int_2 (lo, hi);
	TREE_TYPE (value) = type;
	return value;
      }
    case ARRAY_TYPE:
      {
	tree element_type = TREE_TYPE (type);
	int element_size = int_size_in_bytes (element_type);
	tree list = NULL_TREE;
	HOST_WIDE_INT min_index = 0, max_index, cur_index;
	if (element_size == 1 && CH_CHARS_TYPE_P (type))
	  {
	    value = build_string (size, buffer);
	    CH_DERIVED_FLAG (value) = 1;
	    TREE_TYPE (value) = type;
	    return value;
	  }
	if (TYPE_DOMAIN (type) == 0)
	  return 0;
	value = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
	if (value)
	  {
	    if (TREE_CODE (value) != INTEGER_CST)
	      return 0;
	    else
	      min_index = TREE_INT_CST_LOW (value);
	  }
	value = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
	if (value == NULL_TREE || TREE_CODE (value) != INTEGER_CST)
	  return 0;
	else
	  max_index = TREE_INT_CST_LOW (value);
	for (cur_index = max_index; cur_index >= min_index; cur_index--)
	  {
	    HOST_WIDE_INT offset = (cur_index - min_index) * element_size;
	    value = extract_constant_from_buffer (element_type,
						  buffer + offset,
						  buf_size - offset);
	    if (value == NULL_TREE)
	      return NULL_TREE;
	    list = tree_cons (build_int_2 (cur_index, 0), value, list);
	  }
	value = build (CONSTRUCTOR, type, NULL_TREE, list);
	TREE_CONSTANT (value) = 1;
	TREE_STATIC (value) = 1;
	return value;
      }
    case RECORD_TYPE:
      {
	tree list = NULL_TREE;
	tree field = TYPE_FIELDS (type);
	for (; field != NULL_TREE; field = TREE_CHAIN (field))
	  {
	    HOST_WIDE_INT offset
	      = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) / BITS_PER_UNIT;
	    if (DECL_BIT_FIELD (field))
	      return 0;
	    value = extract_constant_from_buffer (TREE_TYPE (field),
						  buffer + offset,
						  buf_size - offset);
	    if (value == NULL_TREE)
	      return NULL_TREE;
	    list = tree_cons (field, value, list);
	  }
	value = build (CONSTRUCTOR, type, NULL_TREE, nreverse (list));
	TREE_CONSTANT (value) = 1;
	TREE_STATIC (value) = 1;
	return value;
      }

    case UNION_TYPE:
      {
	tree longest_variant = NULL_TREE;
	int longest_size = 0;
	tree field = TYPE_FIELDS (type);
	
	/* This is a kludge.  We assume that converting the data to te
	   longest variant will provide valid data for the "correct"
	   variant.  This is usually the case, but is not guaranteed.
	   For example, the longest variant may include holes.
	   Also incorrect interpreting the given value as the longest
	   variant may confuse the compiler if that should happen
	   to yield invalid values.  ??? */

	for (; field != NULL_TREE; field = TREE_CHAIN (field))
	  {
	    int size = TREE_INT_CST_LOW (size_in_bytes (TREE_TYPE (field)));
	    
	    if (size > longest_size)
	      {
		longest_size = size;
		longest_variant = field;
	      }
	  }
	if (longest_variant == NULL_TREE)
	  return NULL_TREE;
	return extract_constant_from_buffer (TREE_TYPE (longest_variant), buffer, buf_size);
      }

    case SET_TYPE:
      {
	tree list = NULL_TREE;
	int i;
	HOST_WIDE_INT min_index, max_index;
	if (TYPE_DOMAIN (type) == 0)
	  return 0;
	value = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
	if (value == NULL_TREE)
	  min_index = 0;
	else if (TREE_CODE (value) != INTEGER_CST)
	  return 0;
	else
	  min_index = TREE_INT_CST_LOW (value);
	value = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
	if (value == NULL_TREE)
	  max_index = 0;
	else if (TREE_CODE (value) != INTEGER_CST)
	  return 0;
	else
	  max_index = TREE_INT_CST_LOW (value);
	for (i = max_index + 1 - min_index; --i >= 0; )
	  {
	    unsigned char byte = (unsigned char)buffer[i / BITS_PER_UNIT];
	    unsigned bit_pos = (unsigned)i % (unsigned)BITS_PER_UNIT;
	    if (BYTES_BIG_ENDIAN
		? (byte & (1 << (BITS_PER_UNIT - 1 - bit_pos)))
		: (byte & (1 << bit_pos)))
	      list = tree_cons (NULL_TREE,
				build_int_2 (i + min_index, 0), list);
	  }
	value = build (CONSTRUCTOR, type, NULL_TREE, list);
	TREE_CONSTANT (value) = 1;
	TREE_STATIC (value) = 1;
	return value;
      }

    default:
      return NULL_TREE;
    }
}

tree
build_chill_cast (type, expr)
     tree type, expr;
{
  tree expr_type;
  int  expr_type_size;
  int  type_size;
  int  type_is_discrete;
  int  expr_type_is_discrete;

  if (type == NULL_TREE || TREE_CODE (type) == ERROR_MARK)
    return error_mark_node;
  if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
    return error_mark_node;

  /* if expression was untyped because of its context (an
     if_expr or case_expr in a tuple, perhaps) just apply
     the type */
  expr_type = TREE_TYPE (expr);
  if (expr_type == NULL_TREE
      || TREE_CODE (expr_type) == ERROR_MARK)
    return convert (type, expr);

  if (expr_type == type)
    return expr;

  expr_type_size = int_size_in_bytes (expr_type);
  type_size      = int_size_in_bytes (type);

  if (expr_type_size == -1)
    {
      error ("conversions from variable_size value");
      return error_mark_node;
    }
  if (type_size == -1)
    {
      error ("conversions to variable_size mode");
      return error_mark_node;
    }

  /* FIXME: process REAL ==> INT && INT ==> REAL && REAL ==> REAL. I hope this is correct. */
  if ((TREE_CODE (expr_type) == INTEGER_TYPE && TREE_CODE (type) == REAL_TYPE) ||
      (TREE_CODE (expr_type) == REAL_TYPE && TREE_CODE (type) == INTEGER_TYPE) ||
      (TREE_CODE (expr_type) == REAL_TYPE && TREE_CODE (type) == REAL_TYPE))
    return convert (type, expr);

  /* FIXME: Don't know if this is correct */
  /* Don't allow conversions to or from REAL with others then integer */
  if (TREE_CODE (type) == REAL_TYPE)
    {
      error ("cannot convert to float");
      return error_mark_node;
    }
  else if (TREE_CODE (expr_type) == REAL_TYPE)
    {
      error ("cannot convert float to this mode");
      return error_mark_node;
    }

  if (expr_type_size == type_size && CH_REFERABLE (expr))
    goto do_location_conversion;

  type_is_discrete
    = discrete_type_p (type) || TREE_CODE (type) == POINTER_TYPE;
  expr_type_is_discrete
    = discrete_type_p (expr_type) || TREE_CODE (expr_type) == POINTER_TYPE;
  if (expr_type_is_discrete && type_is_discrete)
    {
      /* do an overflow check
	 FIXME: is this always neccessary ??? */
      /* FIXME: don't do range chacking when target type is PTR.
	 PTR doesn't have MIN and MAXVALUE. result is sigsegv. */
      if (range_checking && type != ptr_type_node)
	{
	  tree tmp = expr;

	  STRIP_NOPS (tmp);
	  if (TREE_CONSTANT (tmp) && TREE_CODE (tmp) != ADDR_EXPR)
	    {
	      if (compare_int_csts (LT_EXPR, tmp, TYPE_MIN_VALUE (type)) ||
		  compare_int_csts (GT_EXPR, tmp, TYPE_MAX_VALUE (type)))
		{
		  error ("OVERFLOW in expression conversion");
		  return error_mark_node;
		}
	    }
	  else
	    {
	      int cond1 = tree_int_cst_lt (TYPE_SIZE (type),
					   TYPE_SIZE (expr_type));
	      int cond2 = TREE_UNSIGNED (type) && (! TREE_UNSIGNED (expr_type));
	      int cond3 = (! TREE_UNSIGNED (type))
		&& TREE_UNSIGNED (expr_type)
		&& tree_int_cst_equal (TYPE_SIZE (type),
				       TYPE_SIZE (expr_type));
	      int cond4 = TREE_TYPE (type) && type_is_discrete;

	      if (cond1 || cond2 || cond3 || cond4)
		{
		  tree type_min = TYPE_MIN_VALUE (type);
		  tree type_max = TYPE_MAX_VALUE (type);
  
		  expr = save_if_needed (expr);
		  if (expr && type_min && type_max)
		    {
		      tree check = test_range (expr, type_min, type_max);
		      if (!integer_zerop (check))
			{
			  if (current_function_decl == NULL_TREE)
			    {
			      if (TREE_CODE (check) == INTEGER_CST)
				error ("overflow (not inside function)");
			      else
				warning ("possible overflow (not inside function)");
			    }
			  else
			    {
			      if (TREE_CODE (check) == INTEGER_CST)
				warning ("expression will always cause OVERFLOW");
			      expr = check_expression (expr, check,
						       ridpointers[(int) RID_OVERFLOW]);
			    }
			}
		    }
		}
	    }
	}
      return convert (type, expr);
    }

  if (TREE_CODE (expr) == INTEGER_CST && expr_type_size != type_size)
    {
      /* There should probably be a pedwarn here ... */
      tree itype = type_for_size (type_size * BITS_PER_UNIT, 1);
      if (itype)
	{
	  expr = convert (itype, expr);
	  expr_type = TREE_TYPE (expr);
	  expr_type_size= type_size;
	}
    }

  /* If expr is a constant of the right size, use it to to
     initialize a static variable. */
  if (expr_type_size == type_size && TREE_CONSTANT (expr) && !pedantic)
    {
      unsigned char *buffer = (unsigned char*) alloca (type_size);
      tree value;
      bzero (buffer, type_size);
      if (!expand_constant_to_buffer (expr, buffer, type_size))
	{
	  error ("not implemented: constant conversion from that kind of expression");
	  return error_mark_node;
	}
      value = extract_constant_from_buffer (type, buffer, type_size);
      if (value == NULL_TREE)
	{
	  error ("not implemented: constant conversion to that kind of mode");
	  return error_mark_node;
	}
      return value;
    }

  if (!CH_REFERABLE (expr) && expr_type_size == type_size)
    {
      tree temp = decl_temp1 (get_unique_identifier ("CAST"),
			      TREE_TYPE (expr), 0, 0, 0, 0);
      tree convert1 = build_chill_modify_expr (temp, expr);
      pedwarn ("non-standard, non-portable value conversion");
      return build (COMPOUND_EXPR, type, convert1,
		    build_chill_cast (type, temp));
    }

  if (CH_REFERABLE (expr) && expr_type_size != type_size)
    error ("location conversion between differently-sized modes");
  else
    error ("unsupported value conversion");
  return error_mark_node;

 do_location_conversion:
  /* To avoid confusing other parts of gcc,
     represent this as the C expression: *(TYPE*)EXPR. */
  mark_addressable (expr);
  expr = build1 (INDIRECT_REF, type,
		 build1 (NOP_EXPR, build_pointer_type (type),
			 build1 (ADDR_EXPR, build_pointer_type (expr_type),
				 expr)));
  TREE_READONLY (expr) = TYPE_READONLY (type);
  return expr;
}

/*
 * given a set_type, build an integer array from it that C will grok.
 */
tree
build_array_from_set (type)
     tree type;
{
  tree bytespint, bit_array_size, int_array_count;
 
  if (type == NULL_TREE || type == error_mark_node || TREE_CODE (type) != SET_TYPE)
    return error_mark_node;

  bytespint = build_int_2 (HOST_BITS_PER_INT / HOST_BITS_PER_CHAR, 0);
  bit_array_size = size_in_bytes (type);
  int_array_count = fold (size_binop (TRUNC_DIV_EXPR, bit_array_size,
						 bytespint));
  if (integer_zerop (int_array_count))
    int_array_count = size_one_node;
  type = build_array_type (integer_type_node, 
			   build_index_type (int_array_count));
  return type;
}


tree
build_chill_bin_type (size)
     tree size;
{
#if 0
  int isize;

  if (TREE_CODE (size) != INTEGER_CST
      || (isize = TREE_INT_CST_LOW (size), isize <= 0))
    {
      error ("operand to bin must be a non-negative integer literal");
      return error_mark_node;
    }
  if (isize <= TYPE_PRECISION (unsigned_char_type_node))
    return unsigned_char_type_node;
  if (isize <= TYPE_PRECISION (short_unsigned_type_node))
    return short_unsigned_type_node;
  if (isize <= TYPE_PRECISION (unsigned_type_node))
    return unsigned_type_node;
  if (isize <= TYPE_PRECISION (long_unsigned_type_node))
    return long_unsigned_type_node;
  if (isize <= TYPE_PRECISION (long_long_unsigned_type_node))
    return long_long_unsigned_type_node;
  error ("size %d of BIN too big - no such integer mode", isize);
  return error_mark_node;
#endif
  tree bintype;
 
  if (pass == 1)
    {
      bintype = make_node (INTEGER_TYPE);
      TREE_TYPE (bintype) = ridpointers[(int) RID_BIN];
      TYPE_MIN_VALUE (bintype) = size;
      TYPE_MAX_VALUE (bintype) = size;
    }
  else
    {
      error ("BIN in pass 2");
      return error_mark_node;
    }
  return bintype;
}

tree
chill_expand_tuple (type, constructor)
     tree type, constructor;
{
  char *name;
  tree nonreft = type;

  if (TYPE_NAME (type) != NULL_TREE)
    {
      if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
	name = IDENTIFIER_POINTER (TYPE_NAME (type));
      else
	name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
    }
  else
    name = "";

  /* get to actual underlying type for digest_init */
  while (nonreft && TREE_CODE (nonreft) == REFERENCE_TYPE)
    nonreft = TREE_TYPE (nonreft);

  if (TREE_CODE (nonreft) == ARRAY_TYPE
      || TREE_CODE (nonreft) == RECORD_TYPE
      || TREE_CODE (nonreft) == SET_TYPE)
    return convert (nonreft, constructor);
  else
    {
      error ("mode of tuple is neither ARRAY, STRUCT, nor POWERSET");
      return error_mark_node;
    }
}

/* This function classifies an expr into the Null class,
   the All class, the M-Value, the M-derived, or the M-reference class.
   It probably has some inaccuracies. */

struct ch_class
chill_expr_class (expr)
     tree expr;
{
  struct ch_class class;
  /* The Null class contains the NULL pointer constant (only). */
  if (expr == null_pointer_node)
    {
      class.kind = CH_NULL_CLASS;
      class.mode = NULL_TREE;
      return class;
    }

  /* The All class contains the <undefined value> "*". */
  if (TREE_CODE (expr) == UNDEFINED_EXPR)
    {
      class.kind = CH_ALL_CLASS;
      class.mode = NULL_TREE;
      return class;
    }

  if (CH_DERIVED_FLAG (expr))
    {
      class.kind = CH_DERIVED_CLASS;
      class.mode = TREE_TYPE (expr);
      return class;
    }

  /* The M-Reference contains <references location> (address-of) expressions.
     Note that something that's been converted to a reference doesn't count. */
  if (TREE_CODE (expr) == ADDR_EXPR
      && TREE_CODE (TREE_TYPE (expr)) != REFERENCE_TYPE)
    {
      class.kind = CH_REFERENCE_CLASS;
      class.mode = TREE_TYPE (TREE_TYPE (expr));
      return class;
    }

  /* The M-Value class contains expressions with a known, specific mode M. */
  class.kind = CH_VALUE_CLASS;
  class.mode = TREE_TYPE (expr);
  return class;
}

/* Returns >= 1 iff REF is a location. Return 2 if it is referable. */

int chill_location (ref)
     tree ref;
{
  register enum tree_code code = TREE_CODE (ref);

  switch (code)
    {
    case REALPART_EXPR:
    case IMAGPART_EXPR:
    case ARRAY_REF:
    case PACKED_ARRAY_REF:
    case COMPONENT_REF:
    case NOP_EXPR: /* RETYPE_EXPR */
      return chill_location (TREE_OPERAND (ref, 0));
    case COMPOUND_EXPR:
      return chill_location (TREE_OPERAND (ref, 1));

    case BIT_FIELD_REF:
    case SLICE_EXPR:
      /* A bit-string slice is nor referable. */
      return chill_location (TREE_OPERAND (ref, 0)) == 0 ? 0 : 1;

    case CONSTRUCTOR:
    case STRING_CST:
      return 0;

    case INDIRECT_REF:
    case VAR_DECL:
    case PARM_DECL:
    case RESULT_DECL:
    case ERROR_MARK:
      if (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
	  && TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE)
	return 2;
      break;

    default:
      break;
    }
  return 0;
}

int
chill_referable (val)
     tree val;
{
  return chill_location (val) > 1;
}

/* Make a copy of MODE, but with the given NOVELTY. */

tree
copy_novelty (novelty, mode)
     tree novelty, mode;
{
  if (CH_NOVELTY (mode) != novelty)
    {
      mode = copy_node (mode);
      TYPE_MAIN_VARIANT (mode) = mode;
      TYPE_NEXT_VARIANT (mode) = 0;
      TYPE_POINTER_TO (mode) = 0;
      TYPE_REFERENCE_TO (mode) = 0;
      SET_CH_NOVELTY (mode, novelty);
    }
  return mode;
}


struct mode_chain
{
  struct mode_chain *prev;
  tree mode1, mode2;
};

/* Tests if MODE1 and MODE2 are SIMILAR.
   This is more or less as defined in the Blue Book, though
   see FIXME for parts that are unfinished.
   CHAIN is used to catch infinite recursion:  It is a list of pairs
   of mode arguments to calls to chill_similar "outer" to this call. */   

int
chill_similar (mode1, mode2, chain)
     tree mode1, mode2;
     struct mode_chain *chain;
{
  int varying1, varying2;
  tree t1, t2;
  struct mode_chain *link, node;
  if (mode1 == NULL_TREE || mode2 == NULL_TREE)
    return 0;

  while (TREE_CODE (mode1) == REFERENCE_TYPE)
    mode1 = TREE_TYPE (mode1);
  while (TREE_CODE (mode2) == REFERENCE_TYPE)
    mode2 = TREE_TYPE (mode2);

  /* Range modes are similar to their parent types. */
  while (TREE_CODE (mode1) == INTEGER_TYPE && TREE_TYPE (mode1) != NULL_TREE)
    mode1 = TREE_TYPE (mode1);
  while (TREE_CODE (mode2) == INTEGER_TYPE && TREE_TYPE (mode2) != NULL_TREE)
    mode2 = TREE_TYPE (mode2);

   
  /* see Z.200 sections 12.1.2.2 and 13.2 - all integer precisions 
     are similar to INT and to each other */
  if (mode1 == mode2 ||
      (TREE_CODE (mode1) == INTEGER_TYPE && TREE_CODE (mode2) == INTEGER_TYPE))
    return 1;

  /* This guards against certain kinds of recursion.
     For example:
     SYNMODE a = STRUCT ( next REF a );
     SYNMODE b = STRUCT ( next REF b );
     These moes are similar, but will get an infite recursion trying
     to prove that.  So, if we are recursing, assume the moes are similar.
     If they are not, we'll find some other discrepancy.  */
  for (link = chain; link != NULL; link = link->prev)
    {
      if (link->mode1 == mode1 && link->mode2 == mode2)
	return 1;
    }

  node.mode1 = mode1;
  node.mode2 = mode2;
  node.prev = chain;

  varying1 = chill_varying_type_p (mode1);
  varying2 = chill_varying_type_p (mode2);
  /* FIXME:  This isn't quite strict enough. */
  if ((varying1 && varying2)
      || (varying1 && TREE_CODE (mode2) == ARRAY_TYPE)
      || (varying2 && TREE_CODE (mode1) == ARRAY_TYPE))
    return 1;

  if (TREE_CODE(mode1) != TREE_CODE(mode2))
    {
      if (flag_old_strings)
	{
	  /* The recursion is to handle varying strings. */
	  if ((TREE_CODE (mode1) == CHAR_TYPE
	       && CH_SIMILAR (mode2, string_one_type_node))
	      || (TREE_CODE (mode2) == CHAR_TYPE
	       && CH_SIMILAR (mode1, string_one_type_node)))
	    return 1;
	  if ((TREE_CODE (mode1) == BOOLEAN_TYPE
	       && CH_SIMILAR (mode2, bitstring_one_type_node))
	      || (TREE_CODE (mode2) == BOOLEAN_TYPE
	       && CH_SIMILAR (mode1, bitstring_one_type_node)))
	    return 1;
	}
      if (TREE_CODE (mode1) == FUNCTION_TYPE
	  && TREE_CODE (mode2) == POINTER_TYPE
	  && TREE_CODE (TREE_TYPE (mode2)) == FUNCTION_TYPE)
	mode2 = TREE_TYPE (mode2);
      else if (TREE_CODE (mode2) == FUNCTION_TYPE
	  && TREE_CODE (mode1) == POINTER_TYPE
	  && TREE_CODE (TREE_TYPE (mode1)) == FUNCTION_TYPE)
	mode1 = TREE_TYPE (mode1);
      else
	return 0;
    }

  if (CH_IS_BUFFER_MODE (mode1) && CH_IS_BUFFER_MODE (mode2))
    {
      tree len1 = max_queue_size (mode1);
      tree len2 = max_queue_size (mode2);
      return tree_int_cst_equal (len1, len2);
    }
  else if (CH_IS_EVENT_MODE (mode1) && CH_IS_EVENT_MODE (mode2))
    {
      tree len1 = max_queue_size (mode1);
      tree len2 = max_queue_size (mode2);
      return tree_int_cst_equal (len1, len2);
    }
  else if (CH_IS_ACCESS_MODE (mode1) && CH_IS_ACCESS_MODE (mode2))
    {
      tree index1 = access_indexmode (mode1);
      tree index2 = access_indexmode (mode2);
      tree record1 = access_recordmode (mode1);
      tree record2 = access_recordmode (mode2);
      if (! chill_read_compatible (index1, index2))
	return 0;
      return chill_read_compatible (record1, record2);
    }
  switch ((enum chill_tree_code)TREE_CODE (mode1))
    {
    case INTEGER_TYPE:
    case BOOLEAN_TYPE:
    case CHAR_TYPE:
      return 1;
    case ENUMERAL_TYPE:
      if (TYPE_VALUES (mode1) == TYPE_VALUES (mode2))
	return 1;
      else
	{
	  /* FIXME: This is more strict than z.200, which seems to
	     allow the elements to be reordered, as long as they
	     have the same values. */

	  tree field1 = TYPE_VALUES (mode1);
	  tree field2 = TYPE_VALUES (mode2);

	  while (field1 != NULL_TREE && field2 != NULL_TREE)
	    {
	      tree value1, value2;
	      /* Check that the names are equal.  */
	      if (TREE_PURPOSE (field1) != TREE_PURPOSE (field2))
		break;

	      value1 = TREE_VALUE (field1);
	      value2 = TREE_VALUE (field2);
	      /* This isn't quite sufficient in general, but will do ... */
	      /* Note that proclaim_decl can cause the SET modes to be
		 compared BEFORE they are satisfied, but otherwise
		 chill_similar is mostly called after satisfaction. */
	      if (TREE_CODE (value1) == CONST_DECL)
		value1 = DECL_INITIAL (value1);
	      if (TREE_CODE (value2) == CONST_DECL)
		value2 = DECL_INITIAL (value2);
	      /* Check that the values are equal or both NULL.  */
	      if (!(value1 == NULL_TREE && value2 == NULL_TREE)
		  && (value1 == NULL_TREE || value2 == NULL_TREE
		      || ! tree_int_cst_equal (value1, value2)))
		break;
	      field1 = TREE_CHAIN (field1);
	      field2 = TREE_CHAIN (field2);
	    }
	  return field1 == NULL_TREE && field2 == NULL_TREE;
	}
    case SET_TYPE:
      /* check for bit strings */
      if (CH_BOOLS_TYPE_P (mode1))
	return CH_BOOLS_TYPE_P (mode2);
      if (CH_BOOLS_TYPE_P (mode2))
	return CH_BOOLS_TYPE_P (mode1);
      /* both are powerset modes */
      return CH_EQUIVALENT (TYPE_DOMAIN (mode1), TYPE_DOMAIN (mode2));

    case POINTER_TYPE:
      /* Are the referenced modes equivalent? */
      return !integer_zerop (chill_equivalent (TREE_TYPE (mode1),
					       TREE_TYPE (mode2),
					       &node));

    case ARRAY_TYPE:
      /* char for char strings */
      if (CH_CHARS_TYPE_P (mode1))
	return CH_CHARS_TYPE_P (mode2);
      if (CH_CHARS_TYPE_P (mode2))
	return CH_CHARS_TYPE_P (mode1);
      /* array modes */
      if (CH_V_EQUIVALENT (TYPE_DOMAIN (mode1), TYPE_DOMAIN (mode2))
	  /* Are the elements modes equivalent? */
	  && !integer_zerop (chill_equivalent (TREE_TYPE (mode1),
					       TREE_TYPE (mode2),
					       &node)))
	{
	  /* FIXME:  Check that element layouts are equivalent */

	  tree count1 = fold (build (MINUS_EXPR, sizetype,
				     TYPE_MAX_VALUE (TYPE_DOMAIN (mode1)),
				     TYPE_MIN_VALUE (TYPE_DOMAIN (mode1))));
	  tree count2 = fold (build (MINUS_EXPR, sizetype,
				     TYPE_MAX_VALUE (TYPE_DOMAIN (mode2)),
				     TYPE_MIN_VALUE (TYPE_DOMAIN (mode2))));
	  tree cond = build_compare_discrete_expr (EQ_EXPR, count1, count2);
	  if (TREE_CODE (cond) == INTEGER_CST)
	    return !integer_zerop (cond);
	  else
	    {
#if 0
	      extern int ignoring;
	      if (!ignoring 
		  && range_checking
		  && current_function_decl)
		return cond;
#endif
	      return 1;
	    }
	}
      return 0;

    case RECORD_TYPE:
    case UNION_TYPE:
      for (t1 = TYPE_FIELDS (mode1), t2 = TYPE_FIELDS (mode2);
	   t1 && t2;  t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
	   {
	     if (TREE_CODE (t1) != TREE_CODE (t2))
	       return 0;
	     /* Are the field modes equivalent? */
	     if (integer_zerop (chill_equivalent (TREE_TYPE (t1),
						   TREE_TYPE (t2),
						   &node)))
	       return 0;
	   }
      return t1 == t2;

    case FUNCTION_TYPE:
      if (!chill_l_equivalent (TREE_TYPE (mode1), TREE_TYPE (mode2), &node))
	return 0;
      for (t1 = TYPE_ARG_TYPES (mode1), t2 = TYPE_ARG_TYPES (mode2);
	   t1 != NULL_TREE && t2 != NULL_TREE;
	   t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
	{
	  tree attr1 = TREE_PURPOSE (t1)
	    ? TREE_PURPOSE (t1) : ridpointers[(int) RID_IN];
	  tree attr2 = TREE_PURPOSE (t2)
	    ? TREE_PURPOSE (t2) : ridpointers[(int) RID_IN];
	  if (attr1 != attr2)
	    return 0;
	  if (!chill_l_equivalent (TREE_VALUE (t1), TREE_VALUE (t2), &node))
	    return 0;
	}
      if (t1 != t2) /* Both NULL_TREE */
	return 0;
      /* check list of exception names */
      t1 = TYPE_RAISES_EXCEPTIONS (mode1);
      t2 = TYPE_RAISES_EXCEPTIONS (mode2);
      if (t1 == NULL_TREE && t2 != NULL_TREE)
	return 0;
      if (t1 != NULL_TREE && t2 == NULL_TREE)
	return 0;
      if (list_length (t1) != list_length (t2))
	return 0;
      while (t1 != NULL_TREE)
        {
	  if (value_member (TREE_VALUE (t1), t2) == NULL_TREE)
	    return 0;
	  t1 = TREE_CHAIN (t1);
        }
      /* FIXME:  Should also check they have the same RECURSIVITY */
      return 1;

    default:
      ;
#if 0
      /* Need to handle row modes, instance modes,
	 association modes, access modes, text modes,
	 duration modes, absolute time modes, structure modes,
	 parameterized structure modes */
#endif
    }
  return 1;
}

/* Return a node that is true iff MODE1 and MODE2 are equivalent.
   This is normally boolean_true_node or boolean_false_node,
   but can be dynamic for dynamic types.
   CHAIN is as for chill_similar.  */

tree
chill_equivalent (mode1, mode2, chain)
     tree mode1, mode2;
     struct mode_chain *chain;
{
  int varying1, varying2;
  int is_string1, is_string2;
  tree base_mode1, base_mode2;

  /* Are the modes v-equivalent? */
#if 0
  if (!chill_similar (mode1, mode2, chain)
      || CH_NOVELTY(mode1) != CH_NOVELTY(mode2))
    return boolean_false_node;
#endif
  if (!chill_similar (mode1, mode2, chain))
    return boolean_false_node;
  else if (TREE_CODE (mode2) == FUNCTION_TYPE
	   && TREE_CODE (mode1) == POINTER_TYPE
	   && TREE_CODE (TREE_TYPE (mode1)) == FUNCTION_TYPE)
    /* don't check novelty in this case to avoid error in case of
       NEWMODE'd proceduremode gets assigned a function */
    return boolean_true_node;
  else if (CH_NOVELTY(mode1) != CH_NOVELTY(mode2))
    return boolean_false_node;

  varying1 = chill_varying_type_p (mode1);
  varying2 = chill_varying_type_p (mode2);

  if (varying1 != varying2)
    return boolean_false_node;
  base_mode1 = varying1 ? CH_VARYING_ARRAY_TYPE (mode1) : mode1;
  base_mode2 = varying2 ? CH_VARYING_ARRAY_TYPE (mode2) : mode2;
  is_string1 = CH_STRING_TYPE_P (base_mode1);
  is_string2 = CH_STRING_TYPE_P (base_mode2);
  if (is_string1 || is_string2)
    {
      if (is_string1 != is_string2)
	return boolean_false_node;
      return fold (build (EQ_EXPR, boolean_type_node,
			  TYPE_SIZE (base_mode1),
			  TYPE_SIZE (base_mode2)));
    }

  /* && some more stuff FIXME! */
  if (TREE_CODE(mode1) == INTEGER_TYPE || TREE_CODE(mode2) == INTEGER_TYPE)
    {
      if (TREE_CODE(mode1) != INTEGER_TYPE || TREE_CODE(mode2) != INTEGER_TYPE)
	return boolean_false_node;
      /* If one is a range, the other has to be a range. */
      if ((TREE_TYPE (mode1) != NULL_TREE) != (TREE_TYPE (mode2) != NULL_TREE))
	return boolean_false_node;
      if (TYPE_PRECISION (mode1) != TYPE_PRECISION (mode2))
	return boolean_false_node;
      if (!tree_int_cst_equal (TYPE_MIN_VALUE (mode1), TYPE_MIN_VALUE (mode2)))
	return boolean_false_node;
      if (!tree_int_cst_equal (TYPE_MAX_VALUE (mode1), TYPE_MAX_VALUE (mode2)))
	return boolean_false_node;
    }
  return boolean_true_node;
}

static int
chill_l_equivalent (mode1, mode2, chain)
     tree mode1, mode2;
     struct mode_chain *chain;
{
  /* Are the modes equivalent? */
  if (integer_zerop (chill_equivalent (mode1, mode2, chain)))
    return 0;
  if (TYPE_READONLY (mode1) != TYPE_READONLY (mode2))
    return 0;
#if 0
  ... other conditions ...;
#endif
  return 1;
}

/* See Z200 12.1.2.12 */

int
chill_read_compatible (modeM, modeN)
     tree modeM, modeN;
{
  while (TREE_CODE (modeM) == REFERENCE_TYPE)
    modeM = TREE_TYPE (modeM);
  while (TREE_CODE (modeN) == REFERENCE_TYPE)
    modeN = TREE_TYPE (modeN);

  if (!CH_EQUIVALENT (modeM, modeN))
    return 0;
  if (TYPE_READONLY (modeN))
    {
      if (!TYPE_READONLY (modeM))
	return 0;
      if (CH_IS_BOUND_REFERENCE_MODE (modeM)
	  && CH_IS_BOUND_REFERENCE_MODE (modeN))
	{
	  return chill_l_equivalent (TREE_TYPE (modeM), TREE_TYPE (modeN), 0);
	}
#if 0
      ...;
#endif
    }
  return 1;
}

/* Tests if MODE is compatible with the class of EXPR.
   Cfr. Chill Blue Book 12.1.2.15. */

int
chill_compatible (expr, mode)
     tree expr, mode;
{
  struct ch_class class;

  if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
    return 0;
  if (mode == NULL_TREE || TREE_CODE (mode) == ERROR_MARK)
    return 0;

  while (TREE_CODE (mode) == REFERENCE_TYPE)
    mode = TREE_TYPE (mode);

  if (TREE_TYPE (expr) == NULL_TREE)
    {
      if (TREE_CODE (expr) == CONSTRUCTOR)
	return TREE_CODE (mode) == RECORD_TYPE
	  || ((TREE_CODE (mode) == SET_TYPE || TREE_CODE (mode) == ARRAY_TYPE)
	      && ! TYPE_STRING_FLAG (mode));
      else
	return TREE_CODE (expr) == CASE_EXPR || TREE_CODE (expr) == COND_EXPR;
    }

  class = chill_expr_class (expr);
  switch (class.kind)
    {
    case CH_ALL_CLASS:
      return 1;
    case CH_NULL_CLASS:
      return CH_IS_REFERENCE_MODE (mode) || CH_IS_PROCEDURE_MODE (mode)
	|| CH_IS_INSTANCE_MODE (mode);
    case CH_VALUE_CLASS:
      if (CH_HAS_REFERENCING_PROPERTY (mode))
	return CH_RESTRICTABLE_TO(mode, class.mode);
      else
	return CH_V_EQUIVALENT(mode, class.mode);
    case CH_DERIVED_CLASS:
      return CH_SIMILAR (class.mode, mode);
    case CH_REFERENCE_CLASS:
      if (!CH_IS_REFERENCE_MODE (mode))
	return 0;
#if 0
      /* FIXME! */
      if (class.mode is a row mode)
	...;
      else if (class.mode is not a static mode)
	return 0; /* is this possible? FIXME */
#endif
      return !CH_IS_BOUND_REFERENCE_MODE(mode)
	|| CH_READ_COMPATIBLE (TREE_TYPE (mode), class.mode);
    }
  return 0; /* ERROR! */
}

/* Tests if the class of of EXPR1 and EXPR2 are compatible.
   Cfr. Chill Blue Book 12.1.2.16. */

int
chill_compatible_classes (expr1, expr2)
     tree expr1, expr2;
{
  struct ch_class temp;
  struct ch_class class1, class2;
  class1 = chill_expr_class (expr1);
  class2 = chill_expr_class (expr2);

  switch (class1.kind)
    {
    case CH_ALL_CLASS:
      return 1;
    case CH_NULL_CLASS:
      switch (class2.kind)
	{
	case CH_ALL_CLASS:
	case CH_NULL_CLASS:
	case CH_REFERENCE_CLASS:
	  return 1;
	case CH_VALUE_CLASS:
	case CH_DERIVED_CLASS:
	  goto rule4;
	}
    case CH_REFERENCE_CLASS:
      switch (class2.kind)
	{
	case CH_ALL_CLASS:
	case CH_NULL_CLASS:
	  return 1;
	case CH_REFERENCE_CLASS:
	  return CH_EQUIVALENT (class1.mode, class2.mode);
	case CH_VALUE_CLASS:
	  goto rule6;
	case CH_DERIVED_CLASS:
	  return 0;
	}
    case CH_DERIVED_CLASS:
      switch (class2.kind)
	{
	case CH_ALL_CLASS:
	  return 1;
	case CH_VALUE_CLASS:
	case CH_DERIVED_CLASS:
	  return CH_SIMILAR (class1.mode, class2.mode);
	case CH_NULL_CLASS:
	  class2 = class1;
	  goto rule4;
	case CH_REFERENCE_CLASS:
	  return 0;
	}
    case CH_VALUE_CLASS:
      switch (class2.kind)
	{
	case CH_ALL_CLASS:
	  return 1;
	case CH_DERIVED_CLASS:
	  return CH_SIMILAR (class1.mode, class2.mode);
	case CH_VALUE_CLASS:
	  return CH_V_EQUIVALENT (class1.mode, class2.mode);
	case CH_NULL_CLASS:
	  class2 = class1;
	  goto rule4;
	case CH_REFERENCE_CLASS:
	  temp = class1;  class1 = class2;  class2 = temp;
	  goto rule6;
	}
    }
 rule4:
  /* The Null class is Compatible with the M-derived class or M-value class
     if and only if M is a reference mdoe, procedure mode or instance mode.*/
  return CH_IS_REFERENCE_MODE (class2.mode)
    || CH_IS_PROCEDURE_MODE (class2.mode)
    || CH_IS_INSTANCE_MODE (class2.mode);

 rule6:
  /* The M-reference class is compatible with the N-value class if and
     only if N is a reference mode and ... */
  if (!CH_IS_REFERENCE_MODE (class2.mode))
    return 0;
  if (1) /* If M is a static mode - FIXME */
    {
      if (!CH_IS_BOUND_REFERENCE_MODE (class2.mode))
	return 1;
      if (CH_EQUIVALENT (TREE_TYPE (class2.mode), class1.mode))
	return 1;
    }
  /* If N is a row mode whose .... FIXME */
  return 0;
}

/* Cfr.  Blue Book 12.1.1.6, with some "extensions." */

tree
chill_root_mode (mode)
     tree mode;
{
  /* Reference types are not user-visible types.
     This seems like a good place to get rid of them. */
  if (TREE_CODE (mode) == REFERENCE_TYPE)
    mode = TREE_TYPE (mode);

  while (TREE_CODE (mode) == INTEGER_TYPE && TREE_TYPE (mode) != NULL_TREE)
    mode = TREE_TYPE (mode);  /* a sub-range */

  /* This extension in not in the Blue Book - which only has a
     single Integer type.
     We should probably use chill_integer_type_node rather
     than integer_type_node, but that is likely to bomb.
     At some point, these will become the same, I hope. FIXME */
  if (TREE_CODE (mode) == INTEGER_TYPE
      && TYPE_PRECISION (mode) < TYPE_PRECISION (integer_type_node)
      && CH_NOVELTY (mode) == NULL_TREE)
    mode = integer_type_node;
 
  if (TREE_CODE (mode) == FUNCTION_TYPE)
    return build_pointer_type (mode);

  return mode;
}

/* Cfr.  Blue Book 12.1.1.7. */

tree
chill_resulting_mode (mode1, mode2)
     tree mode1, mode2;
{
  mode1 = CH_ROOT_MODE (mode1);
  mode2 = CH_ROOT_MODE (mode2);
  if (chill_varying_type_p (mode1))
    return mode1;
  if (chill_varying_type_p (mode2))
    return mode2;
  return mode1;
}

/* Cfr.  Blue Book (z200, 1988) 12.1.1.7 Resulting class. */

struct ch_class
chill_resulting_class (class1, class2)
     struct ch_class class1, class2;
{
  struct ch_class class;
  switch (class1.kind)
    {
    case CH_VALUE_CLASS:
      switch (class2.kind)
	{
	case CH_DERIVED_CLASS:
	case CH_ALL_CLASS:
	  class.kind = CH_VALUE_CLASS;
	  class.mode = CH_ROOT_MODE (class1.mode);
	  return class;
	case CH_VALUE_CLASS:
	  class.kind = CH_VALUE_CLASS;
	  class.mode
	    = CH_ROOT_MODE (CH_RESULTING_MODE (class1.mode, class2.mode));
	  return class;
	default:
	  break;
	}
      break;
    case CH_DERIVED_CLASS:
      switch (class2.kind)
	{
	case CH_VALUE_CLASS:
	  class.kind = CH_VALUE_CLASS;
	  class.mode = CH_ROOT_MODE (class2.mode);
	  return class;
	case CH_DERIVED_CLASS:
	  class.kind = CH_DERIVED_CLASS;
	  class.mode = CH_RESULTING_MODE (class1.mode, class2.mode);
	  return class;
	case CH_ALL_CLASS:
	  class.kind = CH_DERIVED_CLASS;
	  class.mode = CH_ROOT_MODE (class1.mode);
	  return class;
	default:
	  break;
	}
      break;
    case CH_ALL_CLASS:
      switch (class2.kind)
	{
	case CH_VALUE_CLASS:
	  class.kind = CH_VALUE_CLASS;
	  class.mode = CH_ROOT_MODE (class2.mode);
	  return class;
	case CH_ALL_CLASS:
	  class.kind = CH_ALL_CLASS;
	  class.mode = NULL_TREE;
	  return class;
	case CH_DERIVED_CLASS:
	  class.kind = CH_DERIVED_CLASS;
	  class.mode = CH_ROOT_MODE (class2.mode);
	  return class;
	default:
	  break;
	}
      break;
    default:
      break;
    }
  error ("internal error in chill_root_resulting_mode");
  class.kind = CH_VALUE_CLASS;
  class.mode = CH_ROOT_MODE (class1.mode);
  return class;
}


/*
 * See Z.200, section 6.3, static conditions. This function
 * returns bool_false_node if the condition is not met at compile time,
 *         bool_true_node if the condition is detectably met at compile time
 *         an expression if a runtime check would be required or was generated.
 * It should only be called with string modes and values.
 */
tree
string_assignment_condition (lhs_mode, rhs_value)
     tree lhs_mode, rhs_value;
{
  tree lhs_size, rhs_size, cond;
  tree rhs_mode = TREE_TYPE (rhs_value);
  int lhs_varying = chill_varying_type_p (lhs_mode);

  if (lhs_varying)
    lhs_size = size_in_bytes (CH_VARYING_ARRAY_TYPE (lhs_mode));
  else if (CH_BOOLS_TYPE_P (lhs_mode))
    lhs_size = TYPE_MAX_VALUE (TYPE_DOMAIN (lhs_mode));
  else
    lhs_size = size_in_bytes (lhs_mode);
  lhs_size = convert (chill_unsigned_type_node, lhs_size);

  if (rhs_mode && TREE_CODE (rhs_mode) == REFERENCE_TYPE)
    rhs_mode = TREE_TYPE (rhs_mode);
  if (rhs_mode == NULL_TREE)
    {
      /* actually, count constructor's length */
      abort ();
    }
  else if (chill_varying_type_p (rhs_mode))
    rhs_size = build_component_ref (rhs_value, var_length_id);
  else if (CH_BOOLS_TYPE_P (rhs_mode))
    rhs_size = TYPE_MAX_VALUE (TYPE_DOMAIN (rhs_mode));
  else
    rhs_size = size_in_bytes (rhs_mode);
  rhs_size = convert (chill_unsigned_type_node, rhs_size);

  /* validity condition */
  cond = fold (build (lhs_varying ? GE_EXPR : EQ_EXPR, 
	   boolean_type_node, lhs_size, rhs_size));
  return cond;
}

/*
 * take a basic CHILL type and wrap it in a VARYING structure.
 * Be sure the length field is initialized.  Return the wrapper.
 */
tree
build_varying_struct (type)
     tree type;
{  
  tree decl1, decl2, result;

  if (type == NULL_TREE || TREE_CODE (type) == ERROR_MARK)
    return error_mark_node;

  decl1 = build_decl (FIELD_DECL, var_length_id, chill_integer_type_node);
  decl2 = build_decl (FIELD_DECL, var_data_id, type);
  TREE_CHAIN (decl1) = decl2;      
  TREE_CHAIN (decl2) = NULL_TREE;
  result = build_chill_struct_type (decl1);

  /* mark this so we don't complain about missing initializers.
     It's fine for a VARYING array to be partially initialized.. */
  C_TYPE_VARIABLE_SIZE(type) = 1;
  return result;
}


/*
 * This is the struct type that forms the runtime initializer
 * list.  There's at least one of these generated per module.
 * It's attached to the global initializer list by the module's
 * 'constructor' code.  Should only be called in pass 2.
 */
tree
build_init_struct ()
{
  tree decl1, decl2, result;
  /* We temporarily reset the maximum_field_alignment to zero so the
     compiler's init data structures can be compatible with the
     run-time system, even when we're compiling with -fpack. */
  extern int maximum_field_alignment;
  int save_maximum_field_alignment = maximum_field_alignment;
  maximum_field_alignment = 0;

  decl1 = build_decl (FIELD_DECL, get_identifier ("__INIT_ENTRY"),
	    build_chill_pointer_type (
              build_function_type (void_type_node, NULL_TREE)));

  decl2 = build_decl (FIELD_DECL, get_identifier ("__INIT_NEXT"),
		      build_chill_pointer_type (void_type_node));

  TREE_CHAIN (decl1) = decl2;      
  TREE_CHAIN (decl2) = NULL_TREE;
  result = build_chill_struct_type (decl1);
  maximum_field_alignment = save_maximum_field_alignment;
  return result;
}


/*
 * Return 1 if the given type is a single-bit boolean set,
 *          in which the domain's min and max values 
 *          are both zero,
 *        0 if not.  This can become a macro later..
 */
int
ch_singleton_set (type)
     tree type;
{
  if (type == NULL_TREE || TREE_CODE (type) == ERROR_MARK)
    return 0;
  if (TREE_CODE (type) != SET_TYPE)
    return 0;
  if (TREE_TYPE (type) == NULL_TREE 
      || TREE_CODE (TREE_TYPE (type)) != BOOLEAN_TYPE)
    return 0;
  if (TYPE_DOMAIN (type) == NULL_TREE)
    return 0;
  if (! tree_int_cst_equal (TYPE_MIN_VALUE (TYPE_DOMAIN (type)),
			    integer_zero_node))
    return 0;
  if (! tree_int_cst_equal (TYPE_MAX_VALUE (TYPE_DOMAIN (type)),
			    integer_zero_node))
    return 0;
  return 1;
}

/* return non-zero if TYPE is a compiler-generated VARYING
   array of some base type */
int
chill_varying_type_p (type)
     tree type;
{
  if (type == NULL_TREE)
    return 0;
  if (TREE_CODE (type) != RECORD_TYPE)
    return 0;
  if (TYPE_FIELDS (type) == NULL_TREE 
      || TREE_CHAIN (TYPE_FIELDS (type)) == NULL_TREE)
    return 0;
  if (DECL_NAME (TYPE_FIELDS (type)) != var_length_id)
    return 0;
  if (DECL_NAME (TREE_CHAIN (TYPE_FIELDS (type))) != var_data_id)
    return 0;
  if (TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (type))) != NULL_TREE)
    return 0;
  return 1;
}

/* return non-zero if TYPE is a compiler-generated VARYING
   string record */
int
chill_varying_string_type_p (type)
     tree type;
{
  tree var_data_type;
    
  if (!chill_varying_type_p (type))
      return 0;
  
  var_data_type = CH_VARYING_ARRAY_TYPE (type);
  return CH_CHARS_TYPE_P (var_data_type);
}

/* swiped from c-typeck.c */
/* Build an assignment expression of lvalue LHS from value RHS. */

tree
build_chill_modify_expr (lhs, rhs)
     tree lhs, rhs;
{
  register tree result;


  tree lhstype = TREE_TYPE (lhs);

  /* Avoid duplicate error messages from operands that had errors.  */
  if (lhs == NULL_TREE || TREE_CODE (lhs) == ERROR_MARK || rhs == NULL_TREE || TREE_CODE (rhs) == ERROR_MARK)
    return error_mark_node;

  /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue.  */
  /* Do not use STRIP_NOPS here.  We do not want an enumerator
     whose value is 0 to count as a null pointer constant.  */
  if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
    rhs = TREE_OPERAND (rhs, 0);

#if 0
  /* Handle a cast used as an "lvalue".
     We have already performed any binary operator using the value as cast.
     Now convert the result to the cast type of the lhs,
     and then true type of the lhs and store it there;
     then convert result back to the cast type to be the value
     of the assignment.  */

  switch (TREE_CODE (lhs))
    {
    case NOP_EXPR:
    case CONVERT_EXPR:
    case FLOAT_EXPR:
    case FIX_TRUNC_EXPR:
    case FIX_FLOOR_EXPR:
    case FIX_ROUND_EXPR:
    case FIX_CEIL_EXPR:
      {
	tree inner_lhs = TREE_OPERAND (lhs, 0);
	tree result;
	result = build_chill_modify_expr (inner_lhs,
		   convert (TREE_TYPE (inner_lhs),
		     convert (lhstype, rhs)));
	pedantic_lvalue_warning (CONVERT_EXPR);
	return convert (TREE_TYPE (lhs), result);
      }
    }

  /* Now we have handled acceptable kinds of LHS that are not truly lvalues.
     Reject anything strange now.  */

  if (!lvalue_or_else (lhs, "assignment"))
    return error_mark_node;
#endif
  /* FIXME: need to generate a RANGEFAIL if the RHS won't
     fit into the LHS. */

  if (TREE_CODE (lhs) != VAR_DECL
      && ((TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE &&
	   (TREE_TYPE (rhs) && TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE)) ||
	  chill_varying_type_p (TREE_TYPE (lhs)) || 
	  chill_varying_type_p (TREE_TYPE (rhs))))
    {
      int lhs_varying = chill_varying_type_p (TREE_TYPE (lhs));
      int rhs_varying = chill_varying_type_p (TREE_TYPE (rhs));

      /* point at actual RHS data's type */
      tree rhs_data_type = rhs_varying ? 
	CH_VARYING_ARRAY_TYPE (TREE_TYPE (rhs)) :
	  TREE_TYPE (rhs);
      {
	/* point at actual LHS data's type */
	tree lhs_data_type = lhs_varying ? 
	  CH_VARYING_ARRAY_TYPE (TREE_TYPE (lhs)) :
	    TREE_TYPE (lhs);

	int lhs_bytes = int_size_in_bytes (lhs_data_type);
	int rhs_bytes = int_size_in_bytes (rhs_data_type);

	/* if both sides not varying, and sizes not dynamically 
	   computed, sizes must *match* */
	if (! lhs_varying && ! rhs_varying && lhs_bytes != rhs_bytes
	    && lhs_bytes > 0 && rhs_bytes > 0)
	  {
	    error ("string lengths not equal");
	    return error_mark_node;
	  }
	/* Must have enough space on LHS for static size of RHS */
    
	if (lhs_bytes > 0 && rhs_bytes > 0 
	    && lhs_bytes < rhs_bytes)
	  {
	    if (rhs_varying)
	      {
		/* FIXME: generate runtime test for room */
		;
	      }
	    else
	      {
		error ("can't do ARRAY assignment - too large");
		return error_mark_node;
	      }
	  }
      }

      /* now we know the RHS will fit in LHS, build trees for the
	 emit_block_move parameters */

      if (lhs_varying)
	rhs = convert (TREE_TYPE (lhs), rhs);
      else
	{
	  if (rhs_varying)
	    rhs = build_component_ref (rhs, var_data_id);

	  if (! mark_addressable (rhs))
	    {
	      error ("rhs of array assignment is not addressable");
	      return error_mark_node;
	    }

	  lhs = force_addr_of (lhs);
	  rhs = build1 (ADDR_EXPR, const_ptr_type_node, rhs);
	  return
	  build_chill_function_call (lookup_name (get_identifier ("memmove")),
	    tree_cons (NULL_TREE, lhs,
              tree_cons (NULL_TREE, rhs,
                tree_cons (NULL_TREE, size_in_bytes (rhs_data_type), 
		   NULL_TREE))));
	}
    }

  result = build (MODIFY_EXPR, lhstype, lhs, rhs);
  TREE_SIDE_EFFECTS (result) = 1;

  return result;
}

/* Constructors for pointer, array and function types.
   (RECORD_TYPE, UNION_TYPE and ENUMERAL_TYPE nodes are
   constructed by language-dependent code, not here.)  */

/* Construct, lay out and return the type of pointers to TO_TYPE.
   If such a type has already been constructed, reuse it.  */

tree
make_chill_pointer_type (to_type, code)
     tree to_type;
     enum tree_code code;  /* POINTER_TYPE or REFERENCE_TYPE */
{
  extern struct obstack *current_obstack;
  extern struct obstack *saveable_obstack;
  extern struct obstack  permanent_obstack;
  tree t;
  register struct obstack *ambient_obstack = current_obstack;
  register struct obstack *ambient_saveable_obstack = saveable_obstack;

  /* If TO_TYPE is permanent, make this permanent too.  */
  if (TREE_PERMANENT (to_type))
    {
      current_obstack = &permanent_obstack;
      saveable_obstack = &permanent_obstack;
    }

  t = make_node (code);
  TREE_TYPE (t) = to_type;

  current_obstack = ambient_obstack;
  saveable_obstack = ambient_saveable_obstack;
  return t;
}


tree
build_chill_pointer_type (to_type)
     tree to_type;
{
  int is_type_node = TREE_CODE_CLASS (TREE_CODE (to_type)) == 't';
  register tree t = is_type_node ? TYPE_POINTER_TO (to_type) : NULL_TREE;

  /* First, if we already have a type for pointers to TO_TYPE, use it.  */

  if (t)
    return t;

  /* We need a new one. */
  t = make_chill_pointer_type (to_type, POINTER_TYPE);

  /* Lay out the type.  This function has many callers that are concerned
     with expression-construction, and this simplifies them all.
     Also, it guarantees the TYPE_SIZE is permanent if the type is.  */
  if ((is_type_node && (TYPE_SIZE (to_type) != NULL_TREE))
      || pass == 2)
    {
      /* Record this type as the pointer to TO_TYPE.  */
      TYPE_POINTER_TO (to_type) = t;
      layout_type (t);
    }

  return t;
}

tree
build_chill_reference_type (to_type)
     tree to_type;
{
  int is_type_node = TREE_CODE_CLASS (TREE_CODE (to_type)) == 't';
  register tree t = is_type_node ? TYPE_REFERENCE_TO (to_type) : NULL_TREE;

  /* First, if we already have a type for references to TO_TYPE, use it.  */

  if (t)
    return t;

  /* We need a new one. */
  t = make_chill_pointer_type (to_type, REFERENCE_TYPE);

  /* Lay out the type.  This function has many callers that are concerned
     with expression-construction, and this simplifies them all.
     Also, it guarantees the TYPE_SIZE is permanent if the type is.  */
  if ((is_type_node && (TYPE_SIZE (to_type) != NULL_TREE))
      || pass == 2)
    {
      /* Record this type as the reference to TO_TYPE.  */
      TYPE_REFERENCE_TO (to_type) = t;
      layout_type (t);
      CH_NOVELTY (t) = CH_NOVELTY (to_type);
    }

  return t;
}

tree
make_chill_range_type (type, lowval, highval)
     tree type, lowval, highval;
{
  register tree itype = make_node (INTEGER_TYPE);
  TREE_TYPE (itype) = type;
  TYPE_MIN_VALUE (itype) = lowval;
  TYPE_MAX_VALUE (itype) = highval;
  return itype;
}

tree
layout_chill_range_type (rangetype, must_be_const)
     tree rangetype;
     int must_be_const;
{
  tree type = TREE_TYPE (rangetype);
  tree lowval = TYPE_MIN_VALUE (rangetype);
  tree highval = TYPE_MAX_VALUE (rangetype);
  int bad_limits = 0;

  if (TYPE_SIZE (rangetype) != NULL_TREE)
    return rangetype;

  /* process BIN */
  if (type == ridpointers[(int) RID_BIN])
    {
      int binsize;
      
      /* make a range out of it */
      if (TREE_CODE (highval) != INTEGER_CST)
	{
	  error ("non-constant expression for BIN");
	  return error_mark_node;
	}
      binsize = TREE_INT_CST_LOW (highval);
      if (binsize < 0)
	{
	  error ("expression for BIN must not be negative");
	  return error_mark_node;
	}
      if (binsize > 32)
	{
	  error ("cannot process BIN (>32)");
	  return error_mark_node;
	}
      type = ridpointers [(int) RID_RANGE];
      lowval = integer_zero_node;
      highval = build_int_2 ((1 << binsize) - 1, 0);
    }
 
  if (TREE_CODE (lowval) == ERROR_MARK ||
      TREE_CODE (highval) == ERROR_MARK)
    return error_mark_node;

  if (!CH_COMPATIBLE_CLASSES (lowval, highval))
    {
      error ("bounds of range are not compatible");
      return error_mark_node;
    }

  if (type == string_index_type_dummy)
    {
      if (TREE_CODE (highval) == INTEGER_CST
	  && compare_int_csts (LT_EXPR, highval, integer_minus_one_node))
	{
	  error ("negative string length");
	  highval = integer_minus_one_node;
	}
      if (compare_int_csts (EQ_EXPR, highval, integer_minus_one_node))
	type = integer_type_node;
      else
	type = sizetype;
      TREE_TYPE (rangetype) = type;
    }
  else if (type == ridpointers[(int) RID_RANGE])
    {
      /* This isn't 100% right, since the Blue Book definition
	 uses Resulting Class, rather than Resulting Mode,
	 but it's close enough. */
      type = CH_ROOT_RESULTING_CLASS (lowval, highval).mode;

      /* The default TYPE is the type of the constants -
	 except if the constants are integers, we choose an
	 integer type that fits. */
      if (TREE_CODE (type) == INTEGER_TYPE
	  && TREE_CODE (lowval) == INTEGER_CST
	  && TREE_CODE (highval) == INTEGER_CST)
	{
	  /* The logic of this code has been copied from finish_enum
	     in c-decl.c.  FIXME duplication! */
	  int precision = 0;
	  HOST_WIDE_INT maxvalue = TREE_INT_CST_LOW (highval);
	  HOST_WIDE_INT minvalue = TREE_INT_CST_LOW (lowval);
	  if (TREE_INT_CST_HIGH (lowval) >= 0
	      ? tree_int_cst_lt (TYPE_MAX_VALUE (unsigned_type_node), highval)
	      : (tree_int_cst_lt (lowval, TYPE_MIN_VALUE (integer_type_node))
		 || tree_int_cst_lt (TYPE_MAX_VALUE (integer_type_node), highval)))
	    precision = TYPE_PRECISION (long_long_integer_type_node);
	  else
	    {
	      if (maxvalue > 0)
		precision = floor_log2 (maxvalue) + 1;
	      if (minvalue < 0)
		{
		  /* Compute number of bits to represent magnitude of a
		     negative value.  Add one to MINVALUE since range of
		     negative numbers includes the power of two.  */
		  int negprecision = floor_log2 (-minvalue - 1) + 1;
		  if (negprecision > precision)
		    precision = negprecision;
		  precision += 1;	/* room for sign bit */
		}

	      if (!precision)
		precision = 1;
	    }
	  type = type_for_size (precision, minvalue >= 0);

	}
      TREE_TYPE (rangetype) = type;
    }
  else
    {
      if (!CH_COMPATIBLE (lowval, type))
	{
	  error ("range's lower bound and parent mode don't match");
	  return integer_type_node;    /* an innocuous fake */
	}
      if (!CH_COMPATIBLE (highval, type))
	{
	  error ("range's upper bound and parent mode don't match");
	  return integer_type_node;    /* an innocuous fake */
	}
    }

  if (TREE_CODE (type) == ERROR_MARK)
    return type;
  else if (TREE_CODE_CLASS (TREE_CODE (type)) != 't')
    {
      error ("making range from non-mode");
      return error_mark_node;
    }

  if (TREE_CODE (lowval) == REAL_CST || TREE_CODE (highval) == REAL_CST)
    {
      sorry ("floating point ranges");
      return integer_type_node; /* another fake */
    }

  if (TREE_CODE (lowval) != INTEGER_CST || TREE_CODE (highval) != INTEGER_CST)
    {
      if (must_be_const)
	{
	  error ("range mode has non-constant limits");
	  bad_limits = 1;
	}
    }
  else if (tree_int_cst_equal (lowval, integer_zero_node)
	   && tree_int_cst_equal (highval, integer_minus_one_node))
    ; /* do nothing - this is the index type for an empty string */
  else if (compare_int_csts (LT_EXPR, highval, TYPE_MIN_VALUE (type)))
    {
      error ("range's high bound < mode's low bound");
      bad_limits = 1;
    }
  else if (compare_int_csts (GT_EXPR, highval, TYPE_MAX_VALUE (type)))
    {
      error ("range's high bound > mode's high bound");
      bad_limits = 1;
    }
  else if (compare_int_csts (LT_EXPR, highval, lowval))
    {
      error ("range mode high bound < range mode low bound");
      bad_limits = 1;
    }
  else if (compare_int_csts (LT_EXPR, lowval, TYPE_MIN_VALUE (type)))
    {
      error ("range's low bound < mode's low bound");
      bad_limits = 1;
    }
  else if (compare_int_csts (GT_EXPR, lowval, TYPE_MAX_VALUE (type)))
    {
      error ("range's low bound > mode's high bound");
      bad_limits = 1;
    }

  if (bad_limits)
    {
      lowval = TYPE_MIN_VALUE (type);
      highval = lowval;
    }

  highval = convert (type, highval);
  lowval =  convert (type, lowval);
  TYPE_MIN_VALUE (rangetype) = lowval;
  TYPE_MAX_VALUE (rangetype) = highval;
  TYPE_PRECISION (rangetype) = TYPE_PRECISION (type);
  TYPE_MODE (rangetype) = TYPE_MODE (type);
  TYPE_SIZE (rangetype) = TYPE_SIZE (type);
  TYPE_SIZE_UNIT (rangetype) = TYPE_SIZE_UNIT (type);
  TYPE_ALIGN (rangetype) = TYPE_ALIGN (type);
  TREE_UNSIGNED (rangetype) = TREE_UNSIGNED (type);
  CH_NOVELTY (rangetype) = CH_NOVELTY (type);
  return rangetype;
}

/* Build a _TYPE node that has range bounds associated with its values.
   TYPE is the base type for the range type. */
tree
build_chill_range_type (type, lowval, highval)
     tree type, lowval, highval;
{
  tree rangetype;

  if (type == NULL_TREE)
    type = ridpointers[(int) RID_RANGE];
  else if (TREE_CODE (type) == ERROR_MARK)
    return error_mark_node;

  rangetype = make_chill_range_type (type, lowval, highval);
  if (pass != 1)
    rangetype = layout_chill_range_type (rangetype, 0);

  return rangetype;
}

/* Build a CHILL array type, but with minimal checking etc. */

tree
build_simple_array_type (type, idx, layout)
     tree type, idx, layout;
{
  tree array_type = make_node (ARRAY_TYPE);
  TREE_TYPE (array_type) = type;
  TYPE_DOMAIN (array_type) = idx;
  TYPE_ATTRIBUTES (array_type) = layout;
  if (pass != 1)
    array_type = layout_chill_array_type (array_type);
  return array_type;
}

static void
apply_chill_array_layout (array_type)
     tree array_type;
{
  tree layout, temp, what, element_type;
  int stepsize=0, word, start_bit=0, length, natural_length;
  int stepsize_specified;
  int start_bit_error = 0;
  int length_error = 0;

  layout = TYPE_ATTRIBUTES (array_type);
  if (layout == NULL_TREE)
    return;

  if (layout == integer_zero_node) /* NOPACK */
    {
      TYPE_PACKED (array_type) = 0;
      return;
    }

  /* Allow for the packing of 1 bit discrete modes at the bit level. */
  element_type = TREE_TYPE (array_type);
  if (discrete_type_p (element_type)
      && get_type_precision (TYPE_MIN_VALUE (element_type),
			     TYPE_MAX_VALUE (element_type)) == 1)
    natural_length = 1;
  else
    natural_length = TREE_INT_CST_LOW (TYPE_SIZE (element_type));

  if (layout == integer_one_node) /* PACK */
    {
      if (natural_length == 1)
	TYPE_PACKED (array_type) = 1;
      return;
    }

  /* The layout is a STEP (...).
     The current implementation restricts STEP specifications to be of the form
     STEP(POS(0,0,n),n) where n is the natural size of the element mode. */
  stepsize_specified = 0;
  temp = TREE_VALUE (layout);
  if (TREE_VALUE (temp) != NULL_TREE)
    {
      if (TREE_CODE (TREE_VALUE (temp)) != INTEGER_CST)
	error ("Stepsize in STEP must be an integer constant");
      else
	{
	  stepsize = TREE_INT_CST_LOW (TREE_VALUE (temp));
	  if (stepsize <= 0)
	    error ("Stepsize in STEP must be > 0");
	  else
	    stepsize_specified = 1;

	  if (stepsize != natural_length)
	    sorry ("Stepsize in STEP must be the natural width of "
		   "the array element mode");
	}
    }

  temp = TREE_PURPOSE (temp);
  if (TREE_CODE (TREE_PURPOSE (temp)) != INTEGER_CST)
    error ("Starting word in POS must be an integer constant");
  else
    {
      word = TREE_INT_CST_LOW (TREE_PURPOSE (temp));
      if (word < 0)
	error ("Starting word in POS must be >= 0");
      if (word != 0)
	sorry ("Starting word in POS within STEP must be 0");
    }

  length = natural_length;
  temp = TREE_VALUE (temp);
  if (temp != NULL_TREE)
    {
      int wordsize = TYPE_PRECISION (chill_integer_type_node);
      if (TREE_CODE (TREE_PURPOSE (temp)) != INTEGER_CST)
	{
	  error ("Starting bit in POS must be an integer constant");
	  start_bit_error = 1;
	}
      else
	{
	  start_bit = TREE_INT_CST_LOW (TREE_PURPOSE (temp));
	  if (start_bit != 0)
	    sorry ("Starting bit in POS within STEP must be 0");
	  if (start_bit < 0)
	    {
	      error ("Starting bit in POS must be >= 0");
	      start_bit = 0;
	      start_bit_error = 1;
	    }
	  else if (start_bit >= wordsize)
	    {
	      error ("Starting bit in POS must be < the width of a word");
	      start_bit = 0;
	      start_bit_error = 1;
	    }
	}

      temp = TREE_VALUE (temp);
      if (temp != NULL_TREE)
	{
	  what = TREE_PURPOSE (temp);
	  if (what == integer_zero_node)
	    {
	      if (TREE_CODE (TREE_VALUE (temp)) != INTEGER_CST)
		{
		  error ("Length in POS must be an integer constant");
		  length_error = 1;
		}
	      else
		{
		  length = TREE_INT_CST_LOW (TREE_VALUE (temp));
		  if (length <= 0)
		    error ("Length in POS must be > 0");
		}
	    }
	  else
	    {
	      if (TREE_CODE (TREE_VALUE (temp)) != INTEGER_CST)
		{
		  error ("End bit in POS must be an integer constant");
		  length_error = 1;
		}
	      else
		{
		  int end_bit = TREE_INT_CST_LOW (TREE_VALUE (temp));
		  if (end_bit < start_bit)
		    {
		      error ("End bit in POS must be >= the start bit");
		      end_bit = wordsize - 1;
		      length_error = 1;
		    }
		  else if (end_bit >= wordsize)
		    {
		      error ("End bit in POS must be < the width of a word");
		      end_bit = wordsize - 1;
		      length_error = 1;
		    }
		  else if (start_bit_error)
		    length_error = 1;
		  else
		    length = end_bit - start_bit + 1;
		}
	    }
	  if (! length_error && length != natural_length)
	    {
	      sorry ("The length specified on POS within STEP must be "
		     "the natural length of the array element type");
	    }
	}
    }

  if (! length_error && stepsize_specified && stepsize < length)
    error ("Step size in STEP must be >= the length in POS");

  if (length == 1)
    TYPE_PACKED (array_type) = 1;
}

tree
layout_chill_array_type (array_type)
     tree array_type;
{
  tree itype;
  tree element_type = TREE_TYPE (array_type);

  if (TREE_CODE (element_type) == ARRAY_TYPE
      && TYPE_SIZE (element_type) == 0)
    layout_chill_array_type (element_type);

  itype = TYPE_DOMAIN (array_type);

  if (TREE_CODE (itype) == ERROR_MARK
      || TREE_CODE (element_type) == ERROR_MARK)
    return error_mark_node;

  /* do a lower/upper bound check. */
  if (TREE_CODE (itype) == INTEGER_CST)
    {
      error ("array index must be a range, not a single integer");
      return error_mark_node;
    }
  if (TREE_CODE_CLASS (TREE_CODE (itype)) != 't'
      || !discrete_type_p (itype))
    {
      error ("array index is not a discrete mode");
      return error_mark_node;
    }

  /* apply the array layout, if specified. */
  apply_chill_array_layout (array_type);
  TYPE_ATTRIBUTES (array_type) = NULL_TREE;

  /* Make sure TYPE_POINTER_TO (element_type) is filled in.  */
  build_pointer_type (element_type);

  if (TYPE_SIZE (array_type) == 0)
    layout_type (array_type);

  if (TYPE_READONLY_PROPERTY (element_type))
    TYPE_FIELDS_READONLY (array_type) = 1;

  TYPE_ARRAY_MAX_SIZE (array_type) = size_in_bytes (array_type);
  return array_type;
}

/* Build a CHILL array type.

   TYPE is the element type of the array.
   IDXLIST is the list of dimensions of the array.
   VARYING_P is non-zero if the array is a varying array.
   LAYOUT is (NULL_TREE, integer_one_node, integer_zero_node, tree_list),
   meaning (default, pack, nopack, STEP (...) ).  */
tree
build_chill_array_type (type, idxlist, varying_p, layouts)
     tree type, idxlist;
     int varying_p;
     tree layouts;
{
  tree array_type = type;

  if (type == NULL_TREE || TREE_CODE (type) == ERROR_MARK)
    return error_mark_node;
  if (idxlist == NULL_TREE || TREE_CODE (idxlist) == ERROR_MARK)
    return error_mark_node;

  /* We have to walk down the list of index decls, building inner
     array types as we go. We need to reverse the list of layouts so that the
     first layout applies to the last index etc. */
  layouts = nreverse (layouts);
  for ( ; idxlist; idxlist = TREE_CHAIN (idxlist))
    {
      if (layouts != NULL_TREE)
	{
	  type = build_simple_array_type (
		   type, TREE_VALUE (idxlist), TREE_VALUE (layouts));
	  layouts = TREE_CHAIN (layouts);
	}
      else
        type = build_simple_array_type (type, TREE_VALUE (idxlist), NULL_TREE);
    }
  array_type = type;
  if (varying_p)
    array_type = build_varying_struct (array_type);
  return array_type;
}

/* Function to help qsort sort FIELD_DECLs by name order.  */

static int
field_decl_cmp (x, y)
     tree *x, *y;
{
  return (long)DECL_NAME (*x) - (long)DECL_NAME (*y);
}

tree
make_chill_struct_type (fieldlist)
     tree fieldlist;
{
  tree t, x;
  if (TREE_UNION_ELEM (fieldlist))
    t = make_node (UNION_TYPE);
  else
    t = make_node (RECORD_TYPE);
  /* Install struct as DECL_CONTEXT of each field decl. */
  for (x = fieldlist; x; x = TREE_CHAIN (x))
    {
      DECL_CONTEXT (x) = t;
      DECL_FIELD_SIZE (x) = 0;
    }

  /* Delete all duplicate fields from the fieldlist */
  for (x = fieldlist; x && TREE_CHAIN (x);)
    /* Anonymous fields aren't duplicates.  */
    if (DECL_NAME (TREE_CHAIN (x)) == 0)
      x = TREE_CHAIN (x);
    else
      {
	register tree y = fieldlist;
	  
	while (1)
	  {
	    if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x)))
	      break;
	    if (y == x)
	      break;
	    y = TREE_CHAIN (y);
	  }
	if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x)))
	  {
	    error_with_decl (TREE_CHAIN (x), "duplicate member `%s'");
	    TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x));
	  }
	else x = TREE_CHAIN (x);
      }

  TYPE_FIELDS (t) = fieldlist;

  return t;
}

/* decl is a FIELD_DECL.
   DECL_INIT (decl) is (NULL_TREE, integer_one_node, integer_zero_node, tree_list),
   meaning (default, pack, nopack, POS (...) ).
   The return value is a boolean: 1 if POS specified, 0 if not */
static int
apply_chill_field_layout (decl, next_struct_offset)
     tree decl;
     int* next_struct_offset;
{
  tree layout, type, temp, what;
  int word = 0, wordsize, start_bit, offset, length, natural_length;
  int pos_error = 0;
  int is_discrete;

  type = TREE_TYPE (decl);
  is_discrete = discrete_type_p (type);
  if (is_discrete)
    natural_length = get_type_precision (TYPE_MIN_VALUE (type), TYPE_MAX_VALUE (type));
  else
    natural_length = TREE_INT_CST_LOW (TYPE_SIZE (type));

  layout = DECL_INITIAL (decl);
  if (layout == integer_zero_node) /* NOPACK */
    {
      DECL_PACKED (decl) = 0;
      *next_struct_offset += natural_length;
      return 0; /* not POS */
    }

  if (layout == integer_one_node) /* PACK */
    {
      if (is_discrete)
	DECL_BIT_FIELD (decl) = 1;
      else
	{
	  DECL_BIT_FIELD (decl) = 0;
	  DECL_ALIGN (decl) = BITS_PER_UNIT;
	}
      DECL_PACKED (decl) = 1;
      DECL_FIELD_SIZE (decl) = natural_length;
      *next_struct_offset += natural_length;
      return 0; /* not POS */
    }

  /* The layout is a POS (...). The current implementation restricts the use
     of POS to monotonically increasing fields whose width must be the
     natural width of the underlying type. */
  temp = TREE_PURPOSE (layout);

  if (TREE_CODE (TREE_PURPOSE (temp)) != INTEGER_CST)
    {
      error ("Starting word in POS must be an integer constant");
      pos_error = 1;
    }
  else
    {
      word = TREE_INT_CST_LOW (TREE_PURPOSE (temp));
      if (word < 0)
	{
	  error ("Starting word in POS must be >= 0");
	  word = 0;
	  pos_error = 1;
	}
    }

  wordsize = TYPE_PRECISION (chill_integer_type_node);
  offset = word * wordsize;
  length = natural_length;

  temp = TREE_VALUE (temp);
  if (temp != NULL_TREE)
    {
      if (TREE_CODE (TREE_PURPOSE (temp)) != INTEGER_CST)
	{
	  error ("Starting bit in POS must be an integer constant");
	  start_bit = *next_struct_offset - offset;
	  pos_error = 1;
	}
      else
	{
	  start_bit = TREE_INT_CST_LOW (TREE_PURPOSE (temp));
	  if (start_bit < 0)
	    {
	      error ("Starting bit in POS must be >= 0");
	      start_bit = *next_struct_offset - offset;
	      pos_error = 1;
	    }
	  else if (start_bit >= wordsize)
	    {
	      error ("Starting bit in POS must be < the width of a word");
	      start_bit = *next_struct_offset - offset;
	      pos_error = 1;
	    }
	}

      temp = TREE_VALUE (temp);
      if (temp != NULL_TREE)
	{
	  what = TREE_PURPOSE (temp);
	  if (what == integer_zero_node)
	    {
	      if (TREE_CODE (TREE_VALUE (temp)) != INTEGER_CST)
		{
		  error ("Length in POS must be an integer constant");
		  pos_error = 1;
		}
	      else
		{
		  length = TREE_INT_CST_LOW (TREE_VALUE (temp));
		  if (length <= 0)
		    {
		      error ("Length in POS must be > 0");
		      length = natural_length;
		      pos_error = 1;
		    }
		}
	    }
	  else
	    {
	      if (TREE_CODE (TREE_VALUE (temp)) != INTEGER_CST)
		{
		  error ("End bit in POS must be an integer constant");
		  pos_error = 1;
		}
	      else
		{
		  int end_bit = TREE_INT_CST_LOW (TREE_VALUE (temp));
		  if (end_bit < start_bit)
		    {
		      error ("End bit in POS must be >= the start bit");
		      pos_error = 1;
		    }
		  else if (end_bit >= wordsize)
		    {
		      error ("End bit in POS must be < the width of a word");
		      pos_error = 1;
		    }
		  else
		    length = end_bit - start_bit + 1;
		}
	    }
	  if (length != natural_length && ! pos_error)
	    {
	      sorry ("The length specified on POS must be the natural length "
		     "of the field type");
	      length = natural_length;
	    }
	}

      offset += start_bit;
    }

  if (offset != *next_struct_offset && ! pos_error)
    sorry ("STRUCT fields must be layed out in monotonically increasing order");

  DECL_PACKED (decl) = 1;
  DECL_BIT_FIELD (decl) = is_discrete;
  DECL_FIELD_SIZE (decl) = length;
  *next_struct_offset += natural_length;

  return 1; /* was POS */
}

tree
layout_chill_struct_type (t)
     tree t;
{
  tree fieldlist = TYPE_FIELDS (t);
  tree x;
  int old_momentary;
  int was_pos;
  int pos_seen = 0;
  int pos_error = 0;
  int next_struct_offset;

  old_momentary = suspend_momentary ();

  /* Process specified field sizes.
     Set DECL_FIELD_SIZE to the specified size, or 0 if none specified.
     The specified size is found in the DECL_INITIAL.
     Store 0 there, except for ": 0" fields (so we can find them
     and delete them, below).  */

  next_struct_offset = 0;
  for (x = fieldlist; x; x = TREE_CHAIN (x))
    {
      /* An EVENT or BUFFER mode is implemented as a RECORD_TYPE
	 which may contain a CONST_DECL for the maximum queue size. */
      if (TREE_CODE (x) == CONST_DECL)
	continue;

      /* If any field is const, the structure type is pseudo-const.  */
      /* A field that is pseudo-const makes the structure likewise.  */
      if (TREE_READONLY (x) || TYPE_READONLY_PROPERTY (TREE_TYPE (x)))
	TYPE_FIELDS_READONLY (t) = 1;

      /* Any field that is volatile means variables of this type must be
	 treated in some ways as volatile.  */
      if (TREE_THIS_VOLATILE (x))
	C_TYPE_FIELDS_VOLATILE (t) = 1;

      if (DECL_INITIAL (x) != NULL_TREE)
	{
	  was_pos = apply_chill_field_layout (x, &next_struct_offset);
	  DECL_INITIAL (x) = NULL_TREE;
	}
      else
	{
	  unsigned int min_align = TYPE_ALIGN (TREE_TYPE (x));
	  DECL_ALIGN (x) = MAX (DECL_ALIGN (x), min_align);
	  was_pos = 0;
	}
      if ((! was_pos && pos_seen) || (was_pos && ! pos_seen && x != fieldlist))
	pos_error = 1;
      pos_seen |= was_pos;
    }

  if (pos_error)
    error ("If one field has a POS layout, then all fields must have a POS layout");

  /* Now DECL_INITIAL is null on all fields.  */

  layout_type (t);

  /*  Now we have the truly final field list.
      Store it in this type and in the variants.  */

  TYPE_FIELDS (t) = fieldlist;

  /* If there are lots of fields, sort so we can look through them fast.
     We arbitrarily consider 16 or more elts to be "a lot".  */
  {
    int len = 0;

    for (x = fieldlist; x; x = TREE_CHAIN (x))
      {
	if (len > 15)
	  break;
	len += 1;
      }
    if (len > 15)
      {
	tree *field_array;
	char *space;

	len += list_length (x);
	/* Use the same allocation policy here that make_node uses, to
	   ensure that this lives as long as the rest of the struct decl.
	   All decls in an inline function need to be saved.  */
	if (allocation_temporary_p ())
	  space = savealloc (sizeof (struct lang_type) + len * sizeof (tree));
	else
	  space = oballoc (sizeof (struct lang_type) + len * sizeof (tree));

	TYPE_LANG_SPECIFIC (t) = (struct lang_type *) space;
	TYPE_LANG_SPECIFIC (t)->foo.rec.len = len;

	field_array = &TYPE_LANG_SPECIFIC (t)->foo.rec.elts[0];
	len = 0;
	for (x = fieldlist; x; x = TREE_CHAIN (x))
	  field_array[len++] = x;

	qsort (field_array, len, sizeof (tree), field_decl_cmp);
      }
  }

  for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
    {
      TYPE_FIELDS (x) = TYPE_FIELDS (t);
      TYPE_LANG_SPECIFIC (x) = TYPE_LANG_SPECIFIC (t);
      TYPE_ALIGN (x) = TYPE_ALIGN (t);
    }

  resume_momentary (old_momentary);

  return t;
}

/* Given a list of fields, FIELDLIST, return a structure 
   type that contains these fields.  The returned type is 
   always a new type.  */
tree
build_chill_struct_type (fieldlist)
     tree fieldlist;
{
  register tree t;

  if (fieldlist == NULL_TREE || TREE_CODE (fieldlist) == ERROR_MARK)
    return error_mark_node;

  t = make_chill_struct_type (fieldlist);
  if (pass != 1)
    t = layout_chill_struct_type (t);

/*   pushtag (NULL_TREE, t); */

  return t;
}

/* Fix a LANG_TYPE.  These are used for three different uses:
   - representing a 'READ M' (in which case TYPE_READONLY is set);
   - for a  NEWMODE or SYNMODE (CH_NOVELTY is set for a NEWMODE); and
   - for a parameterised type (TREE_TYPE points to base type,
     while TYPE_DOMAIN is the parameter or parameter list).
   Called from satisfy. */
tree
smash_dummy_type (type)
     tree type;
{
  /* Save fields that we don't want to copy from ORIGIN. */ 
  tree origin = TREE_TYPE (type);
  tree main_tree = TYPE_MAIN_VARIANT (origin);
  int  save_uid = TYPE_UID (type);
  struct obstack *save_obstack = TYPE_OBSTACK (type);
  tree save_name = TYPE_NAME (type);
  int  save_permanent = TREE_PERMANENT (type);
  int  save_readonly = TYPE_READONLY (type);
  tree  save_novelty = CH_NOVELTY (type);
  tree save_domain = TYPE_DOMAIN (type);

  if (origin == NULL_TREE)
    abort ();

  if (save_domain)
    {
      if (TREE_CODE (save_domain) == ERROR_MARK)
	return error_mark_node;
      if (origin == char_type_node)
	{ /* Old-fashioned CHAR(N) declaration. */
	  origin = build_string_type (origin, save_domain);
	}
      else
	{ /* Handle parameterised modes. */
	  int is_varying = chill_varying_type_p (origin);
	  tree new_max = save_domain;
	  tree origin_novelty = CH_NOVELTY (origin);
	  if (is_varying)
	    origin = CH_VARYING_ARRAY_TYPE (origin);
	  if (CH_STRING_TYPE_P (origin))
	    {
	      tree oldindex = TYPE_DOMAIN (origin);
	      new_max = check_range (new_max, new_max, NULL_TREE,
				     size_binop (PLUS_EXPR,
						 TYPE_MAX_VALUE (oldindex),
						 integer_one_node));
	      origin = build_string_type (TREE_TYPE (origin), new_max);
	    }
	  else if (TREE_CODE (origin) == ARRAY_TYPE)
	    {
	      tree oldindex = TYPE_DOMAIN (origin);
	      tree upper = check_range (new_max, new_max, NULL_TREE,
					TYPE_MAX_VALUE (oldindex));
	      tree newindex
		= build_chill_range_type (TREE_TYPE (oldindex),
					  TYPE_MIN_VALUE (oldindex), upper);
	      origin = build_simple_array_type (TREE_TYPE (origin), newindex, NULL_TREE);
	    }
	  else if (TREE_CODE (origin) == RECORD_TYPE)
	    {
	      error ("parameterised structures not implemented");
	      return error_mark_node;
	    }
	  else
	    {
	      error ("invalid parameterised type");
	      return error_mark_node;
	    }
	    
	  SET_CH_NOVELTY (origin, origin_novelty);
	  if (is_varying)
	    {
	      origin = build_varying_struct (origin);
	      SET_CH_NOVELTY (origin, origin_novelty);
	    }
	}
      save_domain = NULL_TREE;
    }

  if (TREE_CODE (origin) == ERROR_MARK)
    return error_mark_node;

  *(struct tree_type*)type = *(struct tree_type*)origin;
  /* The following is so that the debug code for
     the copy is different from the original type.
     The two statements usually duplicate each other
     (because they clear fields of the same union),
     but the optimizer should catch that. */
  TYPE_SYMTAB_POINTER (type) = 0;
  TYPE_SYMTAB_ADDRESS (type) = 0;

  /* Restore fields that we didn't want copied from ORIGIN. */
  TYPE_UID (type) = save_uid;
  TYPE_OBSTACK (type) = save_obstack;
  TREE_PERMANENT (type) = save_permanent;
  TYPE_NAME (type) = save_name;

  TREE_CHAIN (type) = NULL_TREE;
  TYPE_VOLATILE (type) = 0;
  TYPE_POINTER_TO (type) = 0;
  TYPE_REFERENCE_TO (type) = 0;

  if (save_readonly)
    { /* TYPE is READ ORIGIN.
	 Add this type to the chain of variants of TYPE.  */
      TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (main_tree);
      TYPE_NEXT_VARIANT (main_tree) = type;
      TYPE_READONLY (type) = save_readonly;
    }
  else
    {
      /* TYPE is the copy of the RHS in a NEWMODE or SYNMODE.
       We also get here after old-fashioned CHAR(N) declaration (see above). */
      TYPE_MAIN_VARIANT (type) = type;
      TYPE_NEXT_VARIANT (type) = NULL_TREE;
      if (save_name)
	DECL_ORIGINAL_TYPE (save_name) = origin;

      if (save_novelty != NULL_TREE)  /* A NEWMODE declaration. */
	{
	  CH_NOVELTY (type) = save_novelty;

	  /* Z.200: "If the DEFINING mode of the NEWMODE name is a range mode,
	     then the virtual mode &name is introduced as the PARENT mode
	     of the NEWMODE name. The DEFINING mode of &name is the PARENT
	     mode of the range mode, and the NOVELTY of &name is that of
	     the NEWMODE name." */

	  if (TREE_CODE (type) == INTEGER_TYPE && TREE_TYPE (type))
	    {
	      tree parent;
	      /* PARENT is the virtual mode &name mentioned above. */
	      push_obstacks_nochange ();
	      end_temporary_allocation ();
	      parent = copy_novelty (save_novelty,TREE_TYPE (type));
	      pop_obstacks ();
	      
	      TREE_TYPE (type) = parent;
	      TYPE_MIN_VALUE (type) = convert (parent, TYPE_MIN_VALUE (type));
	      TYPE_MAX_VALUE (type) = convert (parent, TYPE_MAX_VALUE (type));
	    }
	}
    }
  return type;
}

/* This generates a LANG_TYPE node that represents 'READ TYPE'. */

tree
build_readonly_type (type)
     tree type;
{
  tree node = make_node (LANG_TYPE);
  TREE_TYPE (node) = type;
  TYPE_READONLY (node) = 1;
  if (pass != 1)
    node = smash_dummy_type (node);
  return node;
}


/* Return an unsigned type the same as TYPE in other respects.  */

tree
unsigned_type (type)
     tree type;
{
  tree type1 = TYPE_MAIN_VARIANT (type);
  if (type1 == signed_char_type_node || type1 == char_type_node)
    return unsigned_char_type_node;
  if (type1 == integer_type_node)
    return unsigned_type_node;
  if (type1 == short_integer_type_node)
    return short_unsigned_type_node;
  if (type1 == long_integer_type_node)
    return long_unsigned_type_node;
  if (type1 == long_long_integer_type_node)
    return long_long_unsigned_type_node;

  return signed_or_unsigned_type (1, type);
}

/* Return a signed type the same as TYPE in other respects.  */

tree
signed_type (type)
     tree type;
{
  tree type1 = TYPE_MAIN_VARIANT (type);
  while (TREE_CODE (type1) == INTEGER_TYPE && TREE_TYPE (type1) != NULL_TREE)
    type1 = TREE_TYPE (type1);
  if (type1 == unsigned_char_type_node || type1 == char_type_node)
    return signed_char_type_node;
  if (type1 == unsigned_type_node)
    return integer_type_node;
  if (type1 == short_unsigned_type_node)
    return short_integer_type_node;
  if (type1 == long_unsigned_type_node)
    return long_integer_type_node;
  if (type1 == long_long_unsigned_type_node)
    return long_long_integer_type_node;
  if (TYPE_PRECISION (type1) == 1)
    return signed_boolean_type_node;

  return signed_or_unsigned_type (0, type);
}

/* Return a type the same as TYPE except unsigned or
   signed according to UNSIGNEDP.  */

tree
signed_or_unsigned_type (unsignedp, type)
     int unsignedp;
     tree type;
{
  if (! INTEGRAL_TYPE_P (type)
      || TREE_UNSIGNED (type) == unsignedp)
    return type;

  if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node))
    return unsignedp ? unsigned_char_type_node : signed_char_type_node;
  if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)) 
    return unsignedp ? unsigned_type_node : integer_type_node;
  if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node)) 
    return unsignedp ? short_unsigned_type_node : short_integer_type_node;
  if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node)) 
    return unsignedp ? long_unsigned_type_node : long_integer_type_node;
  if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node)) 
    return (unsignedp ? long_long_unsigned_type_node
	    : long_long_integer_type_node);
  return type;
}

/* Mark EXP saying that we need to be able to take the
   address of it; it should not be allocated in a register.
   Value is 1 if successful.  */

int
mark_addressable (exp)
     tree exp;
{
  register tree x = exp;
  while (1)
    switch (TREE_CODE (x))
      {
      case ADDR_EXPR:
      case COMPONENT_REF:
      case ARRAY_REF:
      case REALPART_EXPR:
      case IMAGPART_EXPR:
	x = TREE_OPERAND (x, 0);
	break;

      case TRUTH_ANDIF_EXPR:
      case TRUTH_ORIF_EXPR:
      case COMPOUND_EXPR:
	x = TREE_OPERAND (x, 1);
	break;

      case COND_EXPR:
	return mark_addressable (TREE_OPERAND (x, 1))
	  & mark_addressable (TREE_OPERAND (x, 2));

      case CONSTRUCTOR:
	TREE_ADDRESSABLE (x) = 1;
	return 1;

      case INDIRECT_REF:
	/* We sometimes add a cast *(TYPE*)&FOO to handle type and mode
	   incompatibility problems.  Handle this case by marking FOO.  */
	if (TREE_CODE (TREE_OPERAND (x, 0)) == NOP_EXPR
	    && TREE_CODE (TREE_OPERAND (TREE_OPERAND (x, 0), 0)) == ADDR_EXPR)
	  {
	    x = TREE_OPERAND (TREE_OPERAND (x, 0), 0);
	    break;
	  }
	if (TREE_CODE (TREE_OPERAND (x, 0)) == ADDR_EXPR)
	  {
	    x = TREE_OPERAND (x, 0);
	    break;
	  }
	return 1;

      case VAR_DECL:
      case CONST_DECL:
      case PARM_DECL:
      case RESULT_DECL:
	if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x)
	    && DECL_NONLOCAL (x))
	  {
	    if (TREE_PUBLIC (x))
	      {
		error ("global register variable `%s' used in nested function",
		       IDENTIFIER_POINTER (DECL_NAME (x)));
		return 0;
	      }
	    pedwarn ("register variable `%s' used in nested function",
		     IDENTIFIER_POINTER (DECL_NAME (x)));
	  }
	else if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x))
	  {
	    if (TREE_PUBLIC (x))
	      {
		error ("address of global register variable `%s' requested",
		       IDENTIFIER_POINTER (DECL_NAME (x)));
		return 0;
	      }

	    /* If we are making this addressable due to its having
	       volatile components, give a different error message.  Also
	       handle the case of an unnamed parameter by not trying
	       to give the name.  */

	    else if (C_TYPE_FIELDS_VOLATILE (TREE_TYPE (x)))
	      {
		error ("cannot put object with volatile field into register");
		return 0;
	      }

	    pedwarn ("address of register variable `%s' requested",
		     IDENTIFIER_POINTER (DECL_NAME (x)));
	  }
	put_var_into_stack (x);

	/* drops through */
      case FUNCTION_DECL:
	TREE_ADDRESSABLE (x) = 1;
#if 0  /* poplevel deals with this now.  */
	if (DECL_CONTEXT (x) == 0)
	  TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (x)) = 1;
#endif
	/* drops through */
      default:
	return 1;
    }
}

/* Return nonzero if VALUE is a valid constant-valued expression
   for use in initializing a static variable; one that can be an
   element of a "constant" initializer.

   Return null_pointer_node if the value is absolute;
   if it is relocatable, return the variable that determines the relocation.
   We assume that VALUE has been folded as much as possible;
   therefore, we do not need to check for such things as
   arithmetic-combinations of integers.  */

tree
initializer_constant_valid_p (value, endtype)
     tree value;
     tree endtype;
{
  switch (TREE_CODE (value))
    {
    case CONSTRUCTOR:
      if (TREE_CODE (TREE_TYPE (value)) == UNION_TYPE
	  && TREE_CONSTANT (value))
	return
	  initializer_constant_valid_p (TREE_VALUE (CONSTRUCTOR_ELTS (value)),
					endtype);
	
      return TREE_STATIC (value) ? null_pointer_node : 0;

    case INTEGER_CST:
    case REAL_CST:
    case STRING_CST:
    case COMPLEX_CST:
      return null_pointer_node;

    case ADDR_EXPR:
      return TREE_OPERAND (value, 0);

    case NON_LVALUE_EXPR:
      return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);

    case CONVERT_EXPR:
    case NOP_EXPR:
      /* Allow conversions between pointer types.  */
      if (TREE_CODE (TREE_TYPE (value)) == POINTER_TYPE
	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE)
	return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);

      /* Allow conversions between real types.  */
      if (TREE_CODE (TREE_TYPE (value)) == REAL_TYPE
	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == REAL_TYPE)
	return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);

      /* Allow length-preserving conversions between integer types.  */
      if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE
	  && (TYPE_PRECISION (TREE_TYPE (value))
	      == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
	return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);

      /* Allow conversions between other integer types only if
	 explicit value.  */
      if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE)
	{
	  tree inner = initializer_constant_valid_p (TREE_OPERAND (value, 0),
						     endtype);
	  if (inner == null_pointer_node)
	    return null_pointer_node;
	  return 0;
	}

      /* Allow (int) &foo provided int is as wide as a pointer.  */
      if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE
	  && (TYPE_PRECISION (TREE_TYPE (value))
	      >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
	return initializer_constant_valid_p (TREE_OPERAND (value, 0),
					     endtype);

      /* Likewise conversions from int to pointers.  */
      if (TREE_CODE (TREE_TYPE (value)) == POINTER_TYPE
	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE
	  && (TYPE_PRECISION (TREE_TYPE (value))
	      <= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
	return initializer_constant_valid_p (TREE_OPERAND (value, 0),
					     endtype);

      /* Allow conversions to union types if the value inside is okay.  */
      if (TREE_CODE (TREE_TYPE (value)) == UNION_TYPE)
	return initializer_constant_valid_p (TREE_OPERAND (value, 0),
					     endtype);
      return 0;

    case PLUS_EXPR:
      if (TREE_CODE (endtype) == INTEGER_TYPE
	  && TYPE_PRECISION (endtype) < POINTER_SIZE)
	return 0;
      {
	tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
						    endtype);
	tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
						    endtype);
	/* If either term is absolute, use the other terms relocation.  */
	if (valid0 == null_pointer_node)
	  return valid1;
	if (valid1 == null_pointer_node)
	  return valid0;
	return 0;
      }

    case MINUS_EXPR:
      if (TREE_CODE (endtype) == INTEGER_TYPE
	  && TYPE_PRECISION (endtype) < POINTER_SIZE)
	return 0;
      {
	tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
						    endtype);
	tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
						    endtype);
	/* Win if second argument is absolute.  */
	if (valid1 == null_pointer_node)
	  return valid0;
	/* Win if both arguments have the same relocation.
	   Then the value is absolute.  */
	if (valid0 == valid1)
	  return null_pointer_node;
	return 0;
      }
    default:
      return 0;
    }
}

/* Return an integer type with BITS bits of precision,
   that is unsigned if UNSIGNEDP is nonzero, otherwise signed.  */

tree
type_for_size (bits, unsignedp)
     unsigned bits;
     int unsignedp;
{
  if (bits == TYPE_PRECISION (integer_type_node))
    return unsignedp ? unsigned_type_node : integer_type_node;

  if (bits == TYPE_PRECISION (signed_char_type_node))
    return unsignedp ? unsigned_char_type_node : signed_char_type_node;

  if (bits == TYPE_PRECISION (short_integer_type_node))
    return unsignedp ? short_unsigned_type_node : short_integer_type_node;

  if (bits == TYPE_PRECISION (long_integer_type_node))
    return unsignedp ? long_unsigned_type_node : long_integer_type_node;

  if (bits == TYPE_PRECISION (long_long_integer_type_node))
    return (unsignedp ? long_long_unsigned_type_node
	    : long_long_integer_type_node);

  if (bits <= TYPE_PRECISION (intQI_type_node))
    return unsignedp ? unsigned_intQI_type_node : intQI_type_node;

  if (bits <= TYPE_PRECISION (intHI_type_node))
    return unsignedp ? unsigned_intHI_type_node : intHI_type_node;

  if (bits <= TYPE_PRECISION (intSI_type_node))
    return unsignedp ? unsigned_intSI_type_node : intSI_type_node;

  if (bits <= TYPE_PRECISION (intDI_type_node))
    return unsignedp ? unsigned_intDI_type_node : intDI_type_node;

#if HOST_BITS_PER_WIDE_INT >= 64
  if (bits <= TYPE_PRECISION (intTI_type_node))
    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
#endif

  return 0;
}

/* Return a data type that has machine mode MODE.
   If the mode is an integer,
   then UNSIGNEDP selects between signed and unsigned types.  */

tree
type_for_mode (mode, unsignedp)
     enum machine_mode mode;
     int unsignedp;
{
  if ((int)mode == (int)TYPE_MODE (integer_type_node))
    return unsignedp ? unsigned_type_node : integer_type_node;

  if ((int)mode == (int)TYPE_MODE (signed_char_type_node))
    return unsignedp ? unsigned_char_type_node : signed_char_type_node;

  if ((int)mode == (int)TYPE_MODE (short_integer_type_node))
    return unsignedp ? short_unsigned_type_node : short_integer_type_node;

  if ((int)mode == (int)TYPE_MODE (long_integer_type_node))
    return unsignedp ? long_unsigned_type_node : long_integer_type_node;

  if ((int)mode == (int)TYPE_MODE (long_long_integer_type_node))
    return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node;

  if ((int)mode == (int)TYPE_MODE (intQI_type_node))
    return unsignedp ? unsigned_intQI_type_node : intQI_type_node;

  if ((int)mode == (int)TYPE_MODE (intHI_type_node))
    return unsignedp ? unsigned_intHI_type_node : intHI_type_node;

  if ((int)mode == (int)TYPE_MODE (intSI_type_node))
    return unsignedp ? unsigned_intSI_type_node : intSI_type_node;

  if ((int)mode == (int)TYPE_MODE (intDI_type_node))
    return unsignedp ? unsigned_intDI_type_node : intDI_type_node;

#if HOST_BITS_PER_WIDE_INT >= 64
  if ((int)mode == (int)TYPE_MODE (intTI_type_node))
    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
#endif

  if ((int)mode == (int)TYPE_MODE (float_type_node))
    return float_type_node;

  if ((int)mode == (int)TYPE_MODE (double_type_node))
    return double_type_node;

  if ((int)mode == (int)TYPE_MODE (long_double_type_node))
    return long_double_type_node;

  if ((int)mode == (int)TYPE_MODE (build_pointer_type (char_type_node)))
    return build_pointer_type (char_type_node);

  if ((int)mode == (int)TYPE_MODE (build_pointer_type (integer_type_node)))
    return build_pointer_type (integer_type_node);

  return 0;
}
