ftp.nice.ch/pub/next/developer/languages/ada/Adaed.1.11.s.tar.gz#/Adaed-1.11.0a/4a.c

This is 4a.c in view mode; [Download] [Up]

/*
 * Copyright (C) 1985-1992  New York University
 * 
 * This file is part of the Ada/Ed-C system.  See the Ada/Ed README file for
 * warranty (none) and distribution info and also the GNU General Public
 * License for more details.

 */
#include "4.h"
#include "attr.h"
#include "setprots.h"
#include "evalprots.h"
#include "errmsgprots.h"
#include "dclmapprots.h"
#include "sspansprots.h"
#include "nodesprots.h"
#include "miscprots.h"
#include "smiscprots.h"
#include "utilprots.h"
#include "chapprots.h"

static int constraint_kind(Symbol);
static void make_constrained_node(Node, Symbol, int);
static void dereference_node(Node, Symbol);
static Symbol resolve2_attr(Node, Symbol);
static int in_univ_attributes(int);
static void check_bounds_in_range(Node, Node, Symbol);
static void check_array_conversion(Node, Symbol, Symbol);
static int reads_prefix(int, Symbol);

int in_type_classes(Symbol sym)							 /*;in_type_classes*/
{
	/* return true if sym in type_classes, as defined in check_type*/
	/* New procedure aqdded for c version */
	return (
	     sym == symbol_boolean_type 
	  || sym == symbol_discrete_type   
	  || sym == symbol_integer_type
	  || sym == symbol_real_type
	  || sym == symbol_universal_type);
}

void check_type_i(Node expn)								 /*;check_type_i*/
{
	/* check_type('integer_type', expn) */
	check_type(symbol_integer_type, expn);
}

void check_type_r(Node expn)								 /*;check_type_r*/
{
	/* check_type('real_type', expn) */
	check_type(symbol_real_type, expn);
}

void check_type_d(Node expn)								 /*;check_type_d*/
{
	/* check_type('discrete_type', expn) */
	check_type(symbol_discrete_type, expn);
}

void check_type_u(Node expn)								/*;check_type_u*/
{
	/* check_type('universal_type', expn) */
	check_type(symbol_universal_type, expn);
}

void check_type(Symbol context_type, Node expn)				/*;check_type*/
{
	/* This procedure performs type checking and operator disambiguation.
	 * -expn- is an expression tree, which must have the type -context_type-.
	 * This procedure is called in all contexts where the type of
	 * an expression is known a priori : assignments, conditionals, etc.
	 * The procedure returns the annotated tree for -expn-, labelling each
	 * node with its unique type, and resolving overloaded constructs where
	 * needed.
	 * Some contexts require that a type belong to a class of types instead
	 * of one  specific type. For example, a condition must be of a boolean
	 * type, not just BOOLEAN.
	 */

	Set types, otypes;
	Symbol t, old_context;
	Forset	fs1;

	if (cdebug2 > 3) TO_ERRFILE("AT PROC :  check_type");

	N_TYPE(expn) = symbol_any;		/*By default.*/
	noop_error = FALSE;

	resolve1(expn);		/* Bottom-up pass.*/

	if (noop_error) {
		noop_error = FALSE;	/* error emitted already*/
		N_TYPE(expn) = symbol_any;
		return;
	}

	types = N_PTYPES(expn);
	old_context = context_type;
	if (in_type_classes(context_type)) {
		/* Keep only those that belong to this class.*/
		otypes = set_copy(types);
		types = set_new(0);
		FORSET(t = (Symbol), otypes, fs1);
			if (compatible_types(t, context_type))
				types = set_with(types, (char *) t);
		ENDFORSET(fs1);
		set_free(otypes);

		if (set_size(types) > 1) {
			/* May be overloaded operator: user_defined one hides predefined.*/
			/* types -:= univ_types */
			otypes = set_copy(types); 
			types = set_new(0);
			FORSET(t = (Symbol), otypes, fs1);
				if (t != symbol_universal_integer && t!= symbol_universal_real)
					types = set_with(types, (char *)t);
			ENDFORSET(fs1);
			set_free(otypes);
		}

		if (set_size(types) == 1) {
			context_type = (Symbol) set_arb (types);
			set_free(types);
		}
		else {
			type_error(set_new1((char *) symbol_any), context_type, 
			  set_size(types), expn);
			N_TYPE(expn) = symbol_any;
			set_free(types);
			return;
		}
	}

	resolve2(expn, context_type);

	if (noop_error) {
		noop_error = FALSE;	/* error emitted already*/
		return;
	}

	/* Now emit a constraint qualification if needed.*/
	if (! in_type_classes(old_context)) apply_constraint(expn, context_type);
	if (! in_univ_types(context_type)) eval_static(expn);
}

static int constraint_kind(Symbol typ)					 /*;constraint_kind*/
{
	Symbol	d;

	if (cdebug2 > 3) {
		TO_ERRFILE("AT PROC :  constraint_kind");
	}
	/* Note that the use of '' in SETL version is translated to zero in
	 * the c version. This use of '' is common only to this routine and
	 * the next following one.
	 */
	if (is_unconstrained(typ) || in_univ_types(typ))  return as_opt;
	if (is_scalar_type(typ)) {
		if (NATURE(typ) == na_enum) return as_opt;
		else return as_qual_range;
	}
	if (is_array(typ))  {
		if (full_others || NATURE(scope_name) == na_record)
			return as_qual_index;
		else return as_opt;
	}
	if (is_record(typ)) {
		if (has_discriminants(typ)) return  as_qual_discr;
		else return as_opt;
	}
	if (is_access(typ)) {
		d = (Symbol) designated_type(typ);
		if (is_scalar_type(d)) return as_opt;
		else if (is_unconstrained(d)) return as_opt;
		else if (is_array(d)) {
			return as_qual_aindex;
		}
		else if (is_record(d)) {
			if (has_discriminants(d)) return as_qual_adiscr;
			else return as_opt;
		}
	}
	return as_opt;
}

void apply_constraint(Node expn, Symbol typ)		  /*;apply_constraint*/
{
	int	k, constraint;

	if (cdebug2 > 3) {
		TO_ERRFILE("AT PROC :  apply constraint");
	}

	constraint = constraint_kind(typ);
	/* test of constraint != 0 corresponds to encoding assigned in previous
	 * procedure
	 */
	k = N_KIND(expn);

	/* If node is insert node, lone descendant is original expression.*/
	if (k == as_insert)  apply_constraint(N_AST1(expn), typ);

	if (k == as_subtype || k == as_parenthesis || constraint == as_opt)
		return;
	/* the two cases have to be distinguished : a'first..a'last and a'b 
	 * in an aggregate, where a qual_range doesn't make any sens.
	 */
	if (k == as_attribute
	  && ((int) attribute_kind(expn) == ATTR_T_RANGE
	  ||  (int) attribute_kind(expn) == ATTR_O_RANGE)
	  && constraint == as_qual_range)
		return;

	if (k == as_ivalue || (N_TYPE(expn) != typ)
	  || (k == as_array_aggregate)
	  || (k == as_new && N_AST2(expn) == OPT_NODE)) {

		/* The two following lines were in the Setl version : We don't have
		 * to keep them since qual_a* is tranformed in qual_* in the code
		 * generator
		 * if (is_access (typ)) {type_const = (Symbol) designated_type (typ); }
		 *    else { type_const = typ; }
		 */
		make_constrained_node(expn, typ, constraint);
	}
}

static void make_constrained_node(Node expn, Symbol typ, int constraint)
													/*;make_constrained_node*/
{
	Node e_node;

	e_node = copy_node(expn);
	N_KIND(expn) = constraint;
	N_AST1(expn) = e_node;
	if (N_AST2_DEFINED(constraint)) N_AST2(expn) = (Node)0;
	if (N_AST3_DEFINED(constraint)) N_AST3(expn) = (Node)0;
	if (N_AST4_DEFINED(constraint)) N_AST4(expn) = (Node)0;
	N_TYPE(expn) = typ;
}

int in_priv_types(Symbol s)									/*;in_priv_types*/
{
	return (s == symbol_private || s == symbol_limited_private);
}

void resolve1(Node expn)										/*;resolve1*/
{
	/* This procedure performs the first, bottom-up pass of the type checking
	 * and	overload resolution. It	 annotates the	expression tree	 with the
	 * attribute N_PTYPES(expn),  corresponding to the possible  types of the
	 * expression.
	 */

	Fortup ft1;
	Forset fs1, fs2;
	unsigned int    op_name;
	int	    exists, i, j, k, tmp1, nat;
	Symbol name, target_type;
	Set names, op_types, array_types;
	Tuple tmp;
	Set tset;
	Node arg, aggregate_node;
	Tuple arg_list;
	Symbol n_t;
	Node lit_name;
	Symbol n;
	Node op_node, args_node;
	Set possible_types;
	Node arg2;
	Symbol nam;
	Node constraint;
	Set ts;
	Symbol t;
	Node ac_expn, type_id;
	Symbol type_mark;
	Symbol desig_type;
	Node c_expr, arg1;
	Node t_node;
	Node e;
	Symbol to_type;
	Set types;
	Node type_node;
	Node low;
	Node high;
	Set t_low, t_high;
	Symbol t1, t2, it, typ;
	Node call_node, index_node;
	Span save_span;

	if (cdebug2 > 3) TO_ERRFILE("AT PROC : resolve1 ");

	/*if (noop_error ? false) then return; end if; */
	/* TODO: check why noop_error assumed possible non_boolean in above */
	if (noop_error) {
		N_PTYPES(expn) = set_new1((char *) symbol_any);
		return;
	}

	op_name = N_KIND(expn);

	if (cdebug2 > 3) {
#ifdef IBM_PC
		printf(" resolve1 %p %s\n", expn, kind_str(op_name));
#else
		printf(" resolve1 %ld %s\n", expn, kind_str(op_name));
#endif
	}

	switch (op_name) {
	case as_simple_name:
		name = N_OVERLOADED(expn) ? (Symbol) 0 : N_UNQ(expn);
		if (name != (Symbol)0) {

			n_t = TYPE_OF(name);
			nat = NATURE(name);
			if ( nat == na_obj
			  || nat == na_constant
			  || nat == na_in
			  || nat == na_inout
			  || nat == na_out
			  || nat == na_task_obj
			  || nat == na_task_obj_spec
			  || nat == na_task_type
			  || nat == na_task_type_spec) {
				N_PTYPES(expn) = set_new1((char *) n_t);
			}
			else if (nat == na_type || nat == na_subtype
			  || nat == na_enum || nat == na_record
			  || nat == na_array || nat == na_access) {
				N_PTYPES(expn) = set_new1((char *) symbol_any);
				pass1_error_id("Invalid use of type %", name, "4.4", expn);
			}
			else if (nat == na_discriminant) {
				/* A discriminant reference can only appear within a  */
				/* record definition. The rec.type in noted on the node. */
				save_span = get_left_span(expn);
				N_KIND(expn) = as_discr_ref;
				N_AST1(expn) = new_name_node(SCOPE_OF(name));
				N_AST2(expn) = N_AST4(expn) = (Node) 0;
				set_span(N_AST1(expn), save_span);
				N_PTYPES(expn) = set_new1((char *) n_t);
			}

			else if (nat == na_void) {
				N_PTYPES(expn) = set_new1((char *)symbol_any);
				pass1_error_id("premature use of %", name, "8.3", expn);
				return;
			}
			else {
				N_PTYPES(expn) = set_new1((char *) symbol_any);
				pass1_error_id("Invalid use of identifier %", name, 
				  "4.4", expn);
			}
		}
		else {
			/* The simple name is overloaded: case of a literal or para-*/
			/* meterless function. Reformat with null param. list.*/
			lit_name = copy_node(expn);
			args_node = node_new(as_list);
			N_LIST(args_node) = tup_new(0);
			N_KIND(expn) = as_call;
			N_AST1(expn) = lit_name;
			N_AST2(expn) = args_node;
			resolve1(expn);
		}
		break;
	case as_character_literal:
		N_PTYPES(expn) = set_new(set_size(N_NAMES(expn)));
		FORSET(n = (Symbol), N_NAMES(expn), fs1);
			N_PTYPES(expn) = set_with(N_PTYPES(expn), (char *) TYPE_OF(n));
		ENDFORSET(fs1);
		break;
	case as_op:
	case as_un_op:
	case as_call:
		/* Overloaded constructs. */

		op_node = N_AST1(expn);
		args_node = N_AST2(expn);

		FORTUP(arg = (Node), N_LIST(args_node), ft1);
			resolve1(arg);
			check_range_attribute(arg);   /* a no-no */
		ENDFORTUP(ft1);
		names = N_NAMES(op_node);
		result_types(expn);
		if (noop_error);	/* Previous error. */
		else if (set_size(N_PTYPES(expn)) == 0)
			type_error(names, (Symbol) 0, 0, expn);

		/* All other cases are basic operations on arrays, record, aggregates */
		/* attributes, subtypes, conversions and qualifications. */
		break;
	case as_name:
		find_old(expn);
		resolve1(expn);
		break;
	case as_int_literal:
		N_PTYPES(expn) = set_new1((char *)symbol_universal_integer);
		break;
	case as_real_literal:
		N_PTYPES(expn) = set_new1((char *) symbol_universal_real);
		break;
	case as_string_literal:
		N_PTYPES(expn) = set_new1((char *) symbol_string_type);
		break;
	case as_null:
		N_PTYPES(expn) = find_access_types();
		break;
	case as_aggregate:
		/* Verify that the list	of choices  is properly formatted, and
		 * collect  all possible aggregate  types. The types of the in-
		 * dividual choices are not used to resolve the aggregate type.
		 */
		arg_list = N_LIST(expn);
		exists = FALSE;
		FORTUPI(arg = (Node), arg_list, i, ft1);
			if (N_KIND(arg) == as_choice_list) {
				exists = TRUE;
				break;
			}
		ENDFORTUP(ft1);
		if (exists) {
			exists = FALSE;
			for (j = i + 1; j <= tup_size(arg_list); j++) {
				arg2 = (Node) arg_list[j];
				if (N_KIND(arg2) != as_choice_list) {
					exists = TRUE;
					break;
				}
			}
		}
		/*	if (exists arg = arg_list(i) | N_KIND(arg) = as_choice_list)
		 *    and(exists arg2 in arg_list(i+1..) |
		 *     N_KIND(arg2) /= as_choice_list)
		 */
		if (exists) {
			Tuple t, t1;
			pass1_error(
			  "positional associations must appear first in aggregate", "4.3",
			  arg2);
			t = tup_new(i);
			/* N_LIST(expn) = N_LIST(expn)(1..i); */
			t1 = N_LIST(expn);
			for (j = 1; j <= i; j++)
				t[i] = t1[i];
			N_LIST(expn) = t;
		}
		/* collect all possible aggregate types. */
		N_PTYPES(expn) = find_agg_types();
		break;
	case as_index:
		possible_types = set_new(0);
		{
			Symbol t;
			FORSET(t = (Symbol), valid_array_expn(expn), fs1);
				possible_types = set_with(possible_types, 
				  (char *) component_type(t));
			ENDFORSET(fs1);
		}
		if (set_size(possible_types) == 0)
			pass1_error("type mismatch in indexing", "4.1.1", expn);
		N_PTYPES(expn) = possible_types;
		break;
	case as_slice:
		/* Slicing operations are equivalent to indexing operations,
		 * for type checking purposes. We simply reformat the result
		 * of type checking, so that the result type of the slice is
		 * the base type of the array expression. If this type is an
		 * access type, we must of course dereference it.
		 */
		possible_types = valid_array_expn(expn);

		if (set_size(possible_types) == 0)
			pass1_error("type mismatch in slice", "4.1.2", expn);

		/* N_PTYPES(expn) := {base_type(t)  : t in possible_types}; */
		tset = set_new(0);
		{
			Symbol t;
			FORSET(t = (Symbol), possible_types, fs1);
				tset = set_with(tset, (char *) t);
			ENDFORSET(fs1);
		}
		set_free(possible_types);
		N_PTYPES(expn) = tset;
		break;
	case as_selector:
		valid_selected_expn(expn);
		break;
	case as_in:
	case as_notin:
		/* The second argument of membership operators is a type_mark or */
		/* a range. */
		op_node = N_AST1(expn);
		args_node = N_AST2(expn);
		tmp = N_LIST(args_node);
		arg1 = (Node) tmp[1];
		arg2 = (Node) tmp[2];

		resolve1(arg1);
		if (N_KIND(arg2) == as_range_expression) {
			find_old(arg2);
			k = N_KIND(arg2);
			if (k != as_simple_name && k != as_attribute) {
				pass1_error("invalid argument for membership operator",
				  "4.4", arg2);
				return;
			}
			nam = N_UNQ(arg2);
			t = base_type(nam);
			if (in_priv_types(t)) t = nam;
			N_PTYPES(arg2) = set_new1((char *) t);
			/* Missing: range attribute. */
		}
		else {
			if (N_KIND(arg2) != as_attribute) {
				/* Argument is a range: reformat as subtype of some type. */
				constraint = copy_node(arg2);
				N_KIND(arg2) = as_subtype;
				N_AST1(arg2) = OPT_NODE;
				N_AST2(arg2) = constraint;
			}
			resolve1(arg2);

			/* ts := {t in N_PTYPES(arg2) | is_scalar_type(t)}; */
			ts = set_new(0);
			{
				Symbol t;
				FORSET(t = (Symbol), N_PTYPES(arg2), fs1);
					if (is_scalar_type(t))
						ts = set_with(ts, (char *) t);
				ENDFORSET(fs1);
			}
			if (set_size(ts) == 0) {
#ifdef ERRNUM
				errmsgn(234, 235, arg2);
#else
				errmsg("bounds of range for membership op must be scalar",
				  "4.4", arg2);
#endif
			}
			else N_PTYPES(arg2) = ts;
		}
		/* Now resolve the expression as for any other operator. */

		{
			Set  op_name_set;
			N_KIND(expn) = as_op;
			op_name_set = op_name == as_in ? set_new1((char *)symbol_in)
			  : set_new1((char *)symbol_notin);
			N_NAMES(op_node) = op_name_set;
			result_types(expn);
			if (noop_error);
			else if (set_size(N_PTYPES(expn)) == 0)
				type_error(op_name_set, (Symbol)0, 0, expn);
		}
		break;
	case as_all:
		/* dereference operations must apply  to objects	 of access type.
		 * The type yielded is obtained by dereferencing the type descrip
		 * tor of the access object.
		 */
		ac_expn = N_AST1(expn);

		resolve1(ac_expn);
		/* ??possible_types := {designated_type(t): t in N_PTYPES(ac_expn)
		 *      | is_access(t)};
		 */
		possible_types = set_new(0);
		{
			Symbol t;
			FORSET(t = (Symbol), N_PTYPES(ac_expn), fs1);
				if (is_access(t))
					possible_types = set_with(possible_types,
				      (char *) designated_type(t));
			ENDFORSET(fs1);
		}
		if (set_size(possible_types) == 0) {
			pass1_error("Expect access type for dereference",
			  "3.8", ac_expn);
		}
		N_PTYPES(expn) = possible_types;
		break;
	case as_new:
		/* the elaboration of the subtypes may produce additional
		 * anonymous types. These are emitted later on (see resolve2)
		 * and here are just collected and discarded.
		 */
		newtypes = tup_with(newtypes, (char *)tup_new(0));
		desig_type = make_subtype(expn);
		{
			Tuple junk = (Tuple)tup_frome(newtypes);
			tup_free(junk);
		}
		type_id = N_AST1(expn);
		constraint = N_AST2(expn);

		type_mark = N_UNQ(type_id);
		if ((constraint == OPT_NODE) &&(is_unconstrained(type_mark))) {
			pass1_error_l("Constraint required in allocator when",
			  "initialization is absent", "4.8", expn);
			return;
		}
		else  /* use name of generated subtype to label allocator */
			N_UNQ(type_id) = desig_type;

		check_fully_declared(desig_type);

		/* Rebuild node as having a designated type and no aggregate. */
		if (constraint != OPT_NODE) {
			type_node = copy_node(expn);
			N_UNQ(type_node) = desig_type;
			N_KIND(type_node) = as_subtype_decl;
		}
		else type_node = type_id;
		N_AST1(expn) = type_node;
		N_AST2(expn) = OPT_NODE;

		/* N_PTYPES(expn) := {a in find_access_types() |
		 *    compatible_types(desig_type, designated_type(a))};
		 */
		{
			Set s;
			Symbol a;
			s = set_new(0);
			FORSET(a = (Symbol), find_access_types(), fs1);
				if (compatible_types(desig_type, designated_type(a)))
					s = set_with(s, (char *) a);
			ENDFORSET(fs1);
			N_PTYPES(expn) = s;
		}
		break;
	case as_new_init:
		/* Allocator given by a type mark and an explicit aggregate. */

		type_id = N_AST1(expn);
		aggregate_node = N_AST2(expn);
		find_type(type_id);
		desig_type = N_UNQ(type_id);
		if (!is_type(desig_type)) {
			pass1_error("invalid type mark in allocator", "4.8", type_id);
			return;
		}
		else
			if (is_limited_type(desig_type)) {
				pass1_error_l("initial value not allowed on an ",
				  "allocator for a limited type", "7.4.4", type_id);
				return;
			}
		if (N_KIND(aggregate_node) == as_parenthesis) {
			/*Remove parenthesis which is an artifact of parsing.*/
			aggregate_node = N_AST1(aggregate_node);
			N_AST2(expn) = aggregate_node;
		}
		resolve1(aggregate_node);
		/* ??N_PTYPES(expn) = {a in find_access_types() |
		 *     compatible_types(desig_type, designated_type(a))}; $$ES151
		 */
		{
			Symbol a;
			Set s;
			s = set_new(0);
			FORSET(a = (Symbol), find_access_types(), fs1);
				if (compatible_types(desig_type, designated_type(a)))
					s = set_with(s, (char *) a);
			ENDFORSET(fs1);
			N_PTYPES(expn) = s;
		}
		N_KIND(expn) = as_new; /* for common processing. */
		break;
	case as_choice_list:
		/* This is used only for the arguments to calls and not for */
		/* aggregates which are handled in complete_r_aggregate. */

		c_expr = N_AST2(expn);
		resolve1(c_expr);
#ifdef TBSL
		-- is copy of N_TYPES needed below	ds 8-jan-85
#endif
	    N_PTYPES(expn) = N_PTYPES(c_expr);
		break;
	case as_attribute:
		resolv_attr(expn);
		break;
	case as_qual_range:
		/* When qual_range appears in an expression, the bounds have */
		/* been type-checked. Simple extract the known result type. */
		N_PTYPES(expn) = set_new1((char *) N_TYPE(expn));
		break;
	case as_convert:
		/* The result type is the type mark of the conversion. */

		t_node = N_AST1(expn);
		arg = N_AST2(expn);
		tmp1 = N_KIND(arg);
		target_type = N_UNQ(t_node);
		if (tmp1 == as_null || tmp1 == as_new || tmp1 == as_new_init
		  || tmp1 == as_aggregate || tmp1 == as_string_literal) {
			pass1_error("invalid expression for conversion", 
			  "4.6(3)", arg);
			return;
		}
		else if (is_incomplete_type(target_type)) {
			pass1_error("premature use of private type in expression",
			  "7.4.1(4)", t_node);
		}
		else {
			resolve1(arg);
			N_PTYPES(expn) = set_new1((char *)target_type);
		}
		break;
	case as_qualify:

		t_node = N_AST1(expn);
		arg = N_AST2(expn);
		to_type = N_UNQ(t_node);
		if (!is_type(to_type)) {
			pass1_error("Expect type mark in qualified expression", 
			  "4.7", t_node);
			return;
		}
		else if (in_open_scopes(to_type) && is_task_type(to_type)) {
			pass1_error_id("invalid use of type % within its own body",
			  to_type, "9.1", t_node);
			return;
		}
		else if (is_incomplete_type(to_type)) {
			pass1_error("premature use of private type in expression",
			  "7.4.1(4)", t_node);
			return;
		}
		else N_PTYPES(expn) = set_new1((char *) to_type);

		resolve1(arg);

		if (noop_error) return;
		else types = N_PTYPES(arg);

		exists = FALSE;
		{
			Symbol t;
			FORSET(t = (Symbol), types, fs1);
				if (compatible_types(to_type, t)) {
					exists = TRUE;
					break;
				}
			ENDFORSET(fs1);
		}
		if (!exists) {
			pass1_error("Expression has wrong type for qualification",
			  "4.7", arg);
		}
		break;
	case as_subtype:
		/* For a subtype expression, the bounds expressions  must  be
		 * checked against the specified type, if any, or against the
		 * type required by context.
		 */
		type_node = N_AST1(expn);
		constraint = N_AST2(expn);
		if (N_KIND(constraint) == as_attribute)
			t_low = t_high = N_PTYPES(constraint);
		else {
			low = N_AST1(constraint);
			high = N_AST2(constraint);
			resolve1(low);
			resolve1(high);
			t_low = N_PTYPES(low);
			t_high = N_PTYPES(high);
		}
		if (type_node == OPT_NODE) {
			/* Case of a range expression with no named type. Validate
			 * the bounds against each other, and return the possible types.
			 */
			possible_types = set_new(0);
			FORSET(t1 = (Symbol), t_low, fs1);
				FORSET(t2 = (Symbol), t_high, fs2);
					it = intersect_types(t1, t2);
					if (it != (Symbol)0) 
						possible_types = set_with(possible_types, (char *)it);
				ENDFORSET(fs2);
			ENDFORSET(fs1);
		}
		else {
			int exists1, exists2;
			/* Subtype of a specified type. Validate the bounds against */
			/* it. */
			typ = N_UNQ(type_node);
			possible_types = set_new1((char *) typ);
			/* if (not exists t1 in t_low |compatible_types(typ, t1))
			 *  or (not exists t2 in t_high|compatible_types(typ, t2)) then
			 */
			exists1 = exists2 = FALSE;
			FORSET(t1 = (Symbol), t_low, fs1);
				if (compatible_types(typ, t1)) {
					exists1 = TRUE;
					break;
				}
			ENDFORSET(fs1);
			if (exists1 == TRUE) {
				FORSET(t2 = (Symbol), t_high, fs1);
					if (compatible_types(typ, t2)) {
						exists2 = TRUE;
						break;
					}
				ENDFORSET(fs1);
			}
			if (!exists1 || !exists2) {
				pass1_error("Invalid types in bounds for range",
				  "3.5, 4.1.2", expn);
			}
		}
		N_PTYPES(expn) = possible_types;
		break;
	case as_parenthesis:
		/* A parenthesised  expression carries  a special operator, in
		 * order to distinguish it from a variable.(Thus(X) is not a
		 * valid OUT parameter for a procedure, and(D) is not a valid
		 * use of a discriminant name).
		 */
		e = N_AST1(expn);
		resolve1(e);
		N_PTYPES(expn) = N_PTYPES(e);
		break;
	case as_call_or_index:
		/* A call to a parameterless function that returns an array can
		 * overload a call  to a function  call with arguments. Resolve
		 * each of the trees independently.
		 */
		call_node = N_AST1(expn);
		index_node = N_AST2(expn);
		op_node = N_AST1(call_node);
		args_node = N_AST2(call_node);
		FORTUP(arg = (Node), N_LIST(args_node), ft1);
			resolve1(arg);
		ENDFORTUP(ft1);
		result_types(call_node);
		op_types = N_PTYPES(call_node);
#ifdef TBSN
		if (cdebug2 > 3) TO_ERRFILE('op_types ' + str op_types);
#endif
		array_types = set_new(0);
		FORSET(t = (Symbol), valid_array_expn(index_node), fs1);
			t = (Symbol)component_type(t);
			array_types = set_with(array_types, (char *)t);
		ENDFORSET(fs1);
		N_PTYPES(index_node) = array_types;
#ifdef TBSN
		if (cdebug2 > 3) TO_ERRFILE('array_types ' + str array_types);
#endif
		N_PTYPES(expn) = set_union(op_types, array_types);
		break;
	case as_range:	/* A frequent error. */
		pass1_error("Invalid use of discrete range  in expression",
		  "4.4", expn);
		N_PTYPES(expn) = set_new1((char *) symbol_any);
		break;
	default:
		/* TBSL: in SETL have op_name = om: use 0 for now */
		if (op_name == 0) {
			/* usually a previous error; often an invalid selected */
			/*  component name. */
			noop_error = TRUE;
		}
		else 
			pass1_error("Invalid operator in expression: ", "4.4, 4.5", expn);
		break;
	}
}

void resolv_attr(Node expn)								  /*;resolv_attr*/
{
	Fortup ft1;
	int	    exists, i, j, notexists, nat, attrkind;
	Symbol s1, s;
	Node entry_node;
	Symbol range_typ;
	Node arg2;
	Node  a_node, arg1;
	Symbol type1;
	Node task_node;
	Symbol task, entry_name;
	Set task_types;
	Node index_node;
	static int is_attribute_prefix = FALSE;

	a_node = N_AST1(expn);
	arg1 = N_AST2(expn);
	arg2 = N_AST3(expn);
	if (N_KIND(a_node) == as_simple_name)  /* no attribute if simple name here*/
		attrkind = ATTR_any;
	else
		attrkind = (int) attribute_kind(expn); /* numeric code for attribute */

	/* verify that BASE appears only as the prefix of another attribute */
	if (attrkind == ATTR_BASE && !is_attribute_prefix)
#ifdef ERRNUM
		errmsgn(236, 233, expn);
#else
		errmsg("Invalid use of attribute BASE", "Annex A", expn);
#endif
	is_attribute_prefix = TRUE;

	/* First - for attributes applying to objects or types, change
	 * attrkind to reflect the type of entity to which the attribute
	 * is being applied.
	 */
	if ( attrkind == ATTR_FIRST || attrkind == ATTR_LAST
	  || attrkind == ATTR_RANGE || attrkind == ATTR_LENGTH
	  || attrkind == ATTR_SIZE || attrkind == ATTR_CONSTRAINED) 
			attrkind = (int)(attribute_kind(expn) +=(is_type_node(arg1) ? 2:1)); 

	/* We find the type of the left argument of the attribute. */
	/* It may be a type name, in which case there is nothing to be */
	/* done. */

	if (is_type_node(arg1)) {
		type1 = N_UNQ(arg1);
		if (is_incomplete_type(type1)) {
			premature_access(type1, arg1);
			N_PTYPES(expn) = set_new1((char *) symbol_any);
			return;
		}
		if (is_task_type(type1)
		  &&(attrkind != ATTR_BASE
		  && attrkind != ATTR_O_SIZE && attrkind != ATTR_T_SIZE
		  && attrkind != ATTR_STORAGE_SIZE)) {
			/* may refer to current task */
			if (in_open_scopes(type1))
				N_UNQ(arg1) = dcl_get(DECLARED(type1), "current_task");
			else
				/* use of the task type otherwise is invalid.*/
				pass1_error("invalid use of task type outside of it own body", 
				  "9.1", arg1);
		}
		N_PTYPES(arg1) = set_new1((char *) type1);
	}
	else if (attrkind == ATTR_COUNT) {
		find_entry_name(arg1);
		task_node = N_AST1(arg1);
		entry_node = N_AST2(arg1);
		task_types = N_PTYPES(task_node);

		if (entry_node == OPT_NODE || set_size(task_types) == 0) {
			/* previous error*/
			noop_error = TRUE; 
			return;
		}

		if (N_KIND(arg1) == as_entry_family_name) {
			entry_name = N_UNQ(entry_node);
			index_node = N_AST3(arg1);
			range_typ = (Symbol) index_type(TYPE_OF(entry_name));
			check_type(range_typ, index_node);
			N_KIND(arg1) = as_entry_name; /* for common processing */
		}
		else {   /* single entry, possibly overloaded */
			if (set_size(N_NAMES(arg1)) > 1) {
#ifdef ERRNUM
				errmsgn(237, 238, entry_node);
#else
				errmsg("ambiguous entry name for attribute", "9.9", entry_node);
#endif
				return;
			}
			else {
				entry_name = (Symbol) set_arb(N_NAMES(arg1));
				N_UNQ(entry_node) = entry_name;
				N_AST3(arg1) = OPT_NODE; /* discard N_NAMES */
			}
		}
		complete_task_name(task_node, TYPE_OF(SCOPE_OF(entry_name)));
		task= N_UNQ(task_node);

		/* The COUNT attribute can only be used immediately within*/
		/* the object executing the task body. */
		exists = FALSE;
		if (N_KIND(task_node) != as_simple_name) exists = TRUE;
		if (!exists) {
			/* check that the task is one of the open scopes */
			notexists = TRUE;
			FORTUPI(s = (Symbol), open_scopes, i, ft1);
				s = (Symbol) open_scopes[i];
				if (task == s 
			      || strcmp(original_name(task), "current_task") == 0
			      && SCOPE_OF(task) == s) {
					notexists = FALSE;
					break;
				}
			ENDFORTUP(ft1);
			if (notexists) exists = TRUE; /* not in open scopes */
		}
		if (!exists) {
			/* intervening scopes cannot be subprograms, etc */
			for (j = 1; j <= i-1; j++) {
				s1 = (Symbol) open_scopes[j];
				nat = NATURE(s1);
				if (nat != na_block && nat != na_entry 
				  && nat != na_entry_family) {
					exists = TRUE;
					break;
				}
			}
		}
		if (exists) {
			pass1_error_l( "E\'COUNT can only be used within the body ",
			  "of the task containing E", "9.9", expn);
			return;
		}

		type1 = symbol_none;
		N_PTYPES(arg1) = set_new1((char *) symbol_none);
	}
	else {
		resolve1(arg1);
		if (set_size(N_PTYPES(arg1)) != 1) {
			pass1_error_str("Invalid argument for attribute %",
			  attribute_str(attrkind), "Annex A, 4.1.4", expn);
			return;
		}
		else
			type1 = (Symbol) set_arb(N_PTYPES(arg1));
	}

	is_attribute_prefix = FALSE;   /* clear flag */

	/* Verify that the type has received a full declaration. */
	if (is_incomplete_type(type1)) {
		/* 'SIZE and 'ADDRESS can be applied to a deffered constant,
		 * in the default expression for record components and non-
		 * generic formal parameters. The nature of the current scope
		 * is either na_record or na_void(formal part or discr. part).
		 */
		if (!is_type_node(arg1) &&
		  (attrkind == ATTR_O_SIZE || attrkind == ATTR_T_SIZE
		  || attrkind == ATTR_ADDRESS) &&(NATURE(scope_name) == na_void
		  || NATURE(scope_name) == na_record)) {
			;
		}
		else {
			premature_access(type1, arg1);
			N_PTYPES(expn) = set_new1((char *) symbol_any);
			return;
		}
	}
	/* Verify that attributes have the proper number of arguments. */

	if (is_scalar_type(type1) &&
	  (  attrkind == ATTR_O_FIRST || attrkind == ATTR_T_FIRST
	  || attrkind == ATTR_O_LAST || attrkind == ATTR_T_LAST)) {
		if (arg2 != OPT_NODE) {
			pass1_error_str("Invalid second argument for attribute %",
			  attribute_str(attrkind), "Annex A, 4.1.4", arg2);
		}
		else if ((N_KIND(arg1) == as_simple_name &&(!is_type(N_UNQ(arg1))))
		  || (N_KIND(arg1) == as_attribute
		  && (int) attribute_kind(arg1) != ATTR_BASE)) {
			pass1_error("attribute cannot be applied to scalar object",
			  "Annex A", a_node);
		}
	}
	else if (attrkind == ATTR_POS 
	  || attrkind == ATTR_VAL 
	  || attrkind == ATTR_PRED 
	  || attrkind == ATTR_SUCC 
	  || attrkind == ATTR_VALUE 
	  || attrkind == ATTR_IMAGE) {
		if (arg2 == OPT_NODE) {
			pass1_error("Missing second argument for attribute ",
			  "Annex A", a_node);
			return;
		}
		else
			if (!is_type_node(arg1) || (N_KIND(arg1) == as_attribute
			  && (int) attribute_kind(arg1) == ATTR_BASE)) {
				pass1_error_l("First argument of attribute must ",
				  "be a type mark", "Annex A", a_node);
				return;
			}
	}

	/* In the case of array attributes, the argument may be an access */
	/*    object. Dereference it now. */
	if ((attrkind == ATTR_O_FIRST || attrkind == ATTR_T_FIRST 
	  || attrkind == ATTR_O_LAST || attrkind == ATTR_T_LAST 
	  || attrkind == ATTR_O_RANGE || attrkind == ATTR_T_RANGE 
	  || attrkind == ATTR_O_LENGTH || attrkind == ATTR_T_LENGTH)
	  && is_access(type1)
	  && is_array((Symbol)(designated_type(type1)))) {
		if (is_fully_private(type1)) {
			premature_access(type1, arg1);
			N_PTYPES(expn) = set_new1((char *)symbol_any);
			return;
		}
		dereference_node(arg1, type1);
		type1 = (Symbol) designated_type(type1);
	}
	else if ((attrkind == ATTR_CALLABLE || attrkind == ATTR_TERMINATED)
	  && is_access(type1)) {
		dereference_node(arg1, type1);
		type1 = (Symbol) designated_type(type1);
	}

	if (arg2 == OPT_NODE) {
		/* For array attributes, a missing second argument is */
		/* equivalent to a reference to the first dimension. */
		arg2 = node_new(as_int_literal);
		set_span(arg2, get_right_span(N_AST2(expn)));
		N_VAL(arg2) = strjoin("1", "");
		N_AST3(expn) = arg2;
	}

	/* The  procedure  attribute-type  will  resolve  fully arg2 */
	/* in the case of array attributes, to obtain a dimension no. */

	N_PTYPES(expn) =
	  set_new1((char *)attribute_type(attrkind, type1, arg1, arg2));
}

/* Made case as_attribute in resolve2 into separate procedure 
 * resolve2_attr.  Having resolve2_attr return (Symbol)0 in case of pass1_error.
 */

static void dereference_node(Node arg1, Symbol type1)	/*;dereference_node*/
{
	/* the prefix of several attributes must be appropriate for the type,
	 * i.e.  it can be an access to an entity  of the proper kind. This
	 * routine is called to emit an explicit dereference (.all) in such cases.
	 */

	Node acc_arg1;

	if (is_type_node(arg1)) {
		;	/* no op */
	}
	else {	/* Dereference object */
		acc_arg1 = copy_node(arg1);
		N_AST2(arg1) = (Node)0;
		N_AST3(arg1) = (Node)0;
		N_AST4(arg1) = (Node)0;
		N_PTYPES(acc_arg1) = set_new1((char *)type1);
		N_KIND(arg1) = as_all;
		N_AST1(arg1) = acc_arg1;
	}
	N_PTYPES(arg1) = set_new1((char *)designated_type(type1));
}

void resolve2(Node expn, Symbol context_typ)				  /*;resolve2*/
{
	/* This procedure performs the second, top-down pass of the
	 * type validation and overloading resolution.
	 * second argument is the type which the expression must yield.
	 * If the expression is overloaded, only one of its instances must
	 * yield  -context_typ-. Once this is ascertained, the known types of the
	 *formals for the top level operator in expression, are propagated
	 * downwards to the actuals.
	 */

	Fortup ft1;
	Forset fs1;
	int	    exists, nat, nk;
	Set types, a_types, ntypes;
	Set oa_types = (Set) 0;
	Symbol name, type2, c, rtype, target_type, ntype_sym;
	Node op_node, args_node, node;
	Set valid_ops;
	Symbol op_name, atysym, t2;
	Set op_names;
	Tuple tup, indices;
	Symbol target_typ;
	Node array1;
	Symbol array_type;
	int	    out_c;
	Tuple	index_list;
	Node index;
	int	    i, may_others;
	Node discr1, e, ac_expn;
	Symbol access_type;
	Node type_node, expn1, entry_node;
	Symbol alloc_type;
	Symbol accessed_type;
	/*char   *chk;*/
	char	*strvstr;
	Tuple	strvtup;
	int		strvlen, strvi;
	Symbol t;
	Symbol c1, c2;
	Set tu;
	Node t_node, constraint, low, high;
	Symbol b_type;
	int	    kind;
	Node call_node, index_node;
	Const	lv; /*TBSL: check type of lv */
	char	*orignam;
	Tuple	litmaptup;
	int		litmapi;
	Span	save_span;

	if (cdebug2 > 0) {
		TO_ERRFILE("resolve2 ");
#ifdef IBM_PC
		printf(" %p %s context %p %s\n"
		  , expn, kind_str(N_KIND(expn)), context_typ,
		  ((context_typ != (Symbol)0)? ORIG_NAME(context_typ):""));
#else
		printf(" %ld %s context %ld %s\n"
		  , expn, kind_str(N_KIND(expn)), context_typ,
		  ((context_typ != (Symbol)0)? ORIG_NAME(context_typ):""));
#endif
	}
	if (context_typ == (Symbol)0)
		printf("??:resolve2 context_typ null\n");
	if (noop_error) return;

	types = N_PTYPES(expn);

	if (expn == OPT_NODE) return;

	switch (nk = N_KIND(expn)) {
	case as_simple_name:
		name = N_UNQ(expn);
		/* If constant, get its value, and if universal constant,
		 * convert when necessary.
		 */
		type2 = TYPE_OF(name);
		if (!compatible_types(context_typ, type2)) {
#ifdef ERRNUM
			id_type_errmsgn(239, name, context_typ, 10, expn);
#else
			errmsg_id_type("% has incorrect type. Expect %", name, context_typ,
			  "none", expn);
#endif
			noop_error = TRUE;
			return;
		}
		else
			if ((NATURE(name) == na_out) &&(!out_context)) {
#ifdef ERRNUM
				id_errmsgn(240, name, 241, expn);
#else
				errmsg_id("invalid reading of out parameter %", name, "6.2",
				  expn);
#endif
			}
		if (NATURE(name) == na_constant) {
			if (in_univ_types(type2)) {
				copy_attributes((Node) SIGNATURE(name), expn);
				specialize(expn, context_typ);
				type2 = base_type(context_typ);
			}
			else if ((Node) SIGNATURE(name) == OPT_NODE
			  && (NATURE(scope_name) != na_void 
			  && NATURE(scope_name) != na_record)) {
				/* Only permissible contexts for a defered constant are
				 * formal parts and component declarations.
				 */
#ifdef ERRNUM
				l_errmsgn(242, 243, 43, expn);
#else
				errmsg_l("premature use of deferred constant before its",
				  "full declaration", "7.4.3", expn);
#endif
			}
		}
		else eval_static(expn);
		break;
	case as_character_literal:
		exists = FALSE;
		FORSET(c = (Symbol), N_NAMES(expn), fs1);
			if (compatible_types(context_typ, TYPE_OF(c))) {
				exists = TRUE;
				break;
			}
		ENDFORSET(fs1);
		if (exists) {
			type2 = TYPE_OF(c);
			/*N_VAL(expn) = literal_map(type2)(original_name(c));*/
			/* In the C version, create a Const with this value */
			orignam = ORIG_NAME(c);
			if (orignam == (char *)0) chaos("resolve2 null literal");
			litmaptup = (Tuple) literal_map(type2);
			for (litmapi = 1; litmapi <= tup_size(litmaptup); litmapi += 2) {
				if (streq(orignam, litmaptup[litmapi])) {
					N_VAL(expn) = (char *) int_const((int)litmaptup[litmapi+1]);
					break;
				}
			}
		}
		else {
			char *tmp_msg;

			tmp_msg = strjoin(N_VAL(expn), " has incorrect type. Expect %");
#ifdef ERRNUM
			type1_errmsgn(tmp_msg, context_typ, 10, expn);
#else
			errmsg_type(tmp_msg, context_typ, "none", expn);
#endif
			type2 = symbol_any;
			N_VAL(expn) = (char *) int_const(0);
		}
		N_KIND(expn) = as_ivalue;
		N_OVERLOADED(expn) = FALSE;
		N_PTYPES(expn) = (Set) 0;
		N_NAMES(expn) = (Set) 0;
		break;
	case as_op:
	case as_un_op:
	case as_call:
		op_node = N_AST1(expn);
		args_node = N_AST2(expn);
		op_names = N_NAMES(op_node);

		/* Find instance of operator that yields type imposed by context. */
		valid_ops = set_new(0);
		FORSET(name = (Symbol), op_names, fs1);
			if (compatible_types(context_typ, TYPE_OF(name)))
				valid_ops = set_with(valid_ops, (char *) name);
		ENDFORSET(fs1);

		N_NAMES(op_node) = valid_ops;

		if (set_size(valid_ops) > 1)
			disambiguate(expn, context_typ);

		if (set_size(N_NAMES(op_node)) > 1)
			/* try removing implicit conversions of universal quantities. */
			remove_conversions(expn);

		/* Now there should be only one possiblity left. */
		valid_ops = N_NAMES(op_node);
		if (set_size(valid_ops) != 1) {
			if (cdebug2 > 2) {
#ifdef TBSN
				??(for nam in valid_ops)
					TO_ERRFILE('OVERLOADS ', nam, SYMBTAB(nam));
				end for;
#endif
			}
			type_error(op_names, context_typ, set_size(valid_ops), op_node);
			return;
		}
		else {
			op_name = (Symbol) set_arb(valid_ops);
			type2   = TYPE_OF(op_name);
		}

		N_OVERLOADED(expn) = FALSE; /* DS -check this */
		N_NAMES(expn) = N_PTYPES(expn) = (Set)0;
		/* For a predefined operator, the type imposed by context fixes
		 * the types of the arguments. The signature of a predefined op.
		 * contains only classes of types, and it ignored in this pass.
		 * The resulting type must be that of the context.
		 */
		switch (nat = NATURE(op_name)) {
		case na_op:
			type2 = base_type(context_typ);
			N_UNQ(op_node) = op_name;
			complete_op_expr(expn, type2);
			/* The expression "+"(1, 2) is syntactically a function call. At
			 * this point it recognized as an operator node.
			 */
			if (N_KIND(expn) == as_call)
				N_KIND(expn) = (tup_size(N_LIST(args_node)) == 1) ? as_un_op
				  : as_op;

			/* For a procedure or function, the signature imposes a type on
			 * each actual parameter present, and specifies a default value
			 * for the ones that are absent. If the function is aliased(ie
			 * a renaming or derivation) the parent subprogram is called.
			 */
			break;
		case na_procedure:
		case na_procedure_spec:
		case na_function:
		case na_function_spec:
			complete_arg_list(SIGNATURE(op_name), args_node);
			N_KIND(expn) = as_call;
			N_UNQ(op_node) = op_name;
			TO_XREF(op_name);
			break;
		case na_entry:
		case na_entry_family:
			complete_arg_list(SIGNATURE(op_name), args_node);
			N_KIND(expn) = as_ecall;
			if (N_KIND(op_node) == as_entry_name
			  || N_KIND(op_node) == as_entry_family_name) {
				entry_node = N_AST2(op_node);
				/* Note  the unique name on the entry name node. */
				N_UNQ(entry_node) = op_name;
			}
			else {   /* called from proc_or_entry, no entry name yet */
				N_UNQ(op_node) = op_name;
			}
			TO_XREF(op_name);

			/* Resolved enumeration literals are returned as themselves. */
			break;
		case na_literal:
			save_span = get_left_span(expn);
			N_KIND(expn) = as_simple_name;
			N_UNQ(expn)  = op_name;
			set_span(expn, save_span);
			N_AST2(expn) = (Node)0; /* clear ast */
			N_VAL(expn)  = ORIG_NAME(op_name);
			TO_XREF(op_name);
			break;
		}
		/* Remaining cases are basic operations. */
		break;
	case as_int_literal:
		/* If the context  type is not universal, the literal must be trans-
		 * formed to its short SETL form.
		 */
		target_typ = ((context_typ == symbol_universal_integer)
		  ? symbol_universal_integer : symbol_integer);

		lv = adaval(target_typ, N_VAL(expn));
		if (adaval_overflow)
			create_raise(expn, symbol_numeric_error);
		else {
			ast_clear(expn);
			N_KIND(expn) = as_ivalue;
			N_VAL(expn) = (char *) lv;
		}
		type2 = base_type(context_typ); /* inherited from context */
		if (root_type(type2) != symbol_integer
		  && root_type(type2) != symbol_universal_integer) {
#ifdef ERRNUM
			errmsgn(244, 245, expn);
#else
			errmsg("invalid context for integer literal", "4.6(15)", expn);
#endif
		}
		break;
	case as_real_literal:
		/* If the context is not universal, or is not a fixed type, then
		 * convert the literal to a SETL floating number.
		 */
		target_typ = (context_typ == symbol_universal_real
		  || is_fixed_type(root_type(context_typ)))
		  ? symbol_universal_real: symbol_float;
		lv = adaval(target_typ, N_VAL(expn));
		if (adaval_overflow)
			create_raise(expn, symbol_constraint_error);
		else {
			ast_clear(expn);
			N_KIND(expn) = as_ivalue;
			N_VAL(expn) = (char *) lv;
		}
		type2 = base_type(context_typ); /* inherited from context */
		if (root_type(type2) != symbol_float 
		  && !is_fixed_type(root_type(type2))
		  && root_type(type2) != symbol_universal_real) {
#ifdef ERRNUM
			errmsgn(246, 245, expn);
#else
			errmsg("invalid context for real literal", "4.6(15)", expn);
#endif
		}
		break;
	case as_string_literal:
		if (is_array(context_typ)) {
			if (context_typ == symbol_string_type) {
				/* verify that only one string type is visible. */
				context_typ = symbol_string;
			}
			else if (is_fully_private(context_typ))
				premature_access(context_typ, expn);

			if (root_type(context_typ) == symbol_string) {
				/*N_VAL(expn) := [abs c: c in N_VAL(expn)];*/
				strvstr = N_VAL(expn);
				strvlen = strlen(strvstr);
				strvtup = tup_new(strvlen);
				for (strvi = 1; strvi <= strvlen; strvi++)
					strvtup[strvi] = (char *) strvstr[strvi-1];
				ast_clear(expn);
				N_VAL(expn) = (char *) strvtup;
				N_KIND(expn) = as_string_ivalue;
				N_NAMES(expn) = (Set) 0;
			}
			else {
				/* Context is user-defined array of a character type. */
				complete_string_literal(expn, component_type(context_typ));
			}
		}
		else {
#ifdef ERRNUM
			type_errmsgn(247, context_typ, 10, expn);
#else
			errmsg_type("Incorrect type for string literal. Expect %",
			  context_typ, "none", expn);
#endif
		}
		type2 = context_typ;
		break;
	case as_null:
		if (is_access(context_typ)) type2 = context_typ;
		else {
#ifdef ERRNUM
			errmsgn(248, 249, expn);
#else
			errmsg("Invalid context for NULL", "3.8.2", expn);
#endif
			return;
		}
		break;
	case as_aggregate:
		/* Resolve it using the  context type, and apply constraint if any.
		 * The possible types include all visible composite types, and there
		 * should be one of them compatible with the context.
		 */
		exists = FALSE;
		FORSET(t = (Symbol), types, fs1);
			if (compatible_types(t, context_typ)) {
				exists = TRUE;
				break;
			}
		ENDFORSET(fs1);
		if (!exists) {
#ifdef ERRNUM
			id_errmsgn(250, context_typ, 251, expn);
#else
			errmsg_id("No aggregate available for type %", context_typ, "4.2",
			  expn);
#endif
			return;
		}
		else complete_aggregate(context_typ, expn);

		type2 = context_typ;

		/* in the absence of more precise checks, the type of the
		 * aggregate can only be set to the base type (see end of resolve2
		 */
		context_typ = base_type (context_typ);

		/* For arrays, obtain required index type from type of array
		 * expression, and complete the determination of both.
		 */
		break;
	case as_index:
		array1 = N_AST1(expn);
		index_node = N_AST2(expn);

		array_type = complete_array_expn(expn, context_typ);

		/* Previous error*/
		if (array_type == symbol_any) return;
		/* Complete resolution of each index.
		 * The  index expression is	a context  in which  out parameters
		 * cannot  be  read. This  has to  be  special-cased	 because an
  		 * indexed  expression on  the lhs  of an assignment is a  valid
		 * context for an out parameter, and the global flag out_context
		 * is set accordingly in  processing assignments.
		 */
		out_c = out_context;
		out_context = FALSE;
		index_list = N_LIST(index_node);

		FORTUPI(index = (Node), index_list, i, ft1);
			resolve2(index, (Symbol) (index_types(array_type))[i]);
		ENDFORTUP(ft1);
		out_context = out_c;

		type2 = (Symbol) component_type(array_type);
		break;
	/* For slices, obtain array type, and apply its index type to the
	 * subtype expression for the discrete range.
	 */
	case as_slice:
		array1 = N_AST1(expn);
		index_node = N_AST2(expn);
		array_type = complete_array_expn(expn, context_typ);
		/* Previous error*/
		if (array_type == symbol_any)
			return;
		tup = N_LIST(index_node);
		discr1 = (Node) (tup[1]);
		resolve2(discr1, (Symbol) index_type(array_type));
		/*	Replace index list with its sole element. */
		N_AST2(expn) = discr1;
		N_AST3(expn) = N_AST4(expn) = (Node) 0;
		type2 = base_type(array_type);
		break;
	case as_selector:
		type2 = complete_selected_expn(expn, context_typ);

		/* For a parenthesised expression, resolve the expression, and keep
		 * the parenthesis, to distinguish them from variables. The possible
		 * constraint of the context is not propagated to the expression.
		 * If the context is universal, discard the parenthesis, to enable
		 * full evaluation of universal expressions.
		 */
		break;
	case as_parenthesis:
		e = N_AST1(expn);
		resolve2(e, base_type(context_typ));
		if (in_univ_types(context_typ))
			copy_attributes(e, expn);
		apply_constraint(e, context_typ);
		type2 = context_typ;
		break;
	/* For a dereference operation, we must verify that the access
	 * object points to the right type.
	 */
	case as_all:
		ac_expn = N_AST1(expn);
		{
			Symbol t;

			a_types = set_new(0);
			FORSET(t = (Symbol), N_PTYPES(ac_expn), fs1);
				if (is_access(t)
				  && compatible_types(context_typ, designated_type(t)))
					a_types = set_with(a_types, (char *) t);
			ENDFORSET(fs1);
		}
		/* TBSL: check that t is defined in type_error call dsd 18 aug */
		if (set_size(a_types) != 1) {
			remove_conversions(ac_expn);	/* last chance */
			oa_types = a_types;
			a_types = set_new(0);
			FORSET(atysym = (Symbol), N_PTYPES(ac_expn), fs1);
				if (set_mem((char *)atysym, oa_types))
					a_types = set_with(a_types, (char *) atysym);
			ENDFORSET(fs1);
			if (set_size(a_types) != 1) {
#ifdef TBSL
				]	    type_error(set_new1('@'), t, set_size(a_types), expn);
#endif
				set_free(oa_types); 
				set_free(a_types);
				return;
			}
		}
		access_type = (Symbol) set_arb(a_types);	/* Only one type left. */
		set_free(a_types);
		if (oa_types != (Set)0)
			set_free(oa_types);

		/* We know already that the nature of access type is na_access. */
		type2 = (Symbol) designated_type(access_type);
		/* It is always illegal to dereference an out parameter.*/
		out_c = out_context;
		out_context = FALSE;
		resolve2(ac_expn, access_type);
		out_context = out_c;
		break;
	/* For an allocator, we obtain the type of the access object
	 * by dereferencing the access type. The final expression however
	 * gives the access type, together with the validated access object.
	 */
	case as_new:
		type_node = N_AST1(expn);
		expn1 = N_AST2(expn);
		alloc_type = N_UNQ(type_node);

		if (!is_access(context_typ)) {
#ifdef ERRNUM
			errmsgn(252, 253, expn);
#else
			errmsg("Context of allocator must be an access type", "4.8, 3.8",
			  expn);
#endif
			return;
		}

		accessed_type = (Symbol) designated_type(context_typ);
		/* Verify that the allocator matches the context.
		 * The(possibly unconstrained)	access type is	the one given by the
		 * context(eg.	declaration). If the allocator provides a constraint
		 * rather than an aggregate, then a subtype has been created, and the
		 * access type is an access to this constrained type. The  constraint
		 * must	 then be  emitted so that it is evaluated at the proper time.
		 *(The subtype is  not an anonymous type, and is introduced  only to
		 * simplify type checking).
		 * The converse may also occur: the  context is	 constrained, but the
		 * allocator  type is  unconstrained. In that  case, use  the context
		 * context type as the type of the expression.
		 * Finally,  the context  may be  an unconstrained  array type, whose
		 * index  type	is   nevertheless   bounded.  When   the allocator is
		 * initialized with an aggregate, the bounds of the aggregate must be
		 * compatible with that index type.
		 */
		if (!compatible_types(accessed_type, alloc_type)) {
#ifdef ERRNUM
			type_errmsgn(254, accessed_type, 255, type_node);
#else
			errmsg_type("Invalid type for allocator. Expect %", accessed_type,
			  "3.8, 4.8", type_node);
#endif
			return;
		}

		if (expn1 != OPT_NODE) {
			res2_check(expn1, alloc_type);
			if (is_array(accessed_type) && can_constrain(accessed_type)) {
				/* bounds of the aggregate will have to be shown to be 
				 * compatible with the (unconstrained) designated type.
				 */
				make_constrained_node(expn1, accessed_type, as_qual_sub);
			}
			else if (!can_constrain(accessed_type)
			  && accessed_type != alloc_type) {
				/*A further qualification is necessary.*/
				may_others = full_others;
				full_others = TRUE;
				apply_constraint(expn1, accessed_type);
				full_others = may_others;
			}
		}
		else if (is_array(alloc_type) && N_KIND(type_node) == as_subtype_decl) {
			/* the index subtypes of the type will have to be elaborated. */
			indices = tup_new(0);
			{ 
				Symbol i;
				FORTUP(i = (Symbol), index_types(alloc_type), ft1);
					indices =
					  tup_with(indices, (char *) new_subtype_decl_node(i));
				ENDFORTUP(ft1);
			}
			N_TYPE(expn) = context_typ;
			make_insert_node(expn, indices, copy_node(expn));
		}

		else if (is_access(alloc_type) && N_KIND(type_node) == as_subtype_decl){
			/* the designated type is anonymous, and will also be elaborated. */
			indices = tup_new(0);
			{ 
				Symbol i, d;
				d = (Symbol) designated_type(alloc_type);
				if is_array(d) {       /* elaborate indices as well */
					FORTUP(i = (Symbol), index_types(d), ft1);
						indices =
						  tup_with(indices, (char *) new_subtype_decl_node(i));
					ENDFORTUP(ft1);
				}
				indices = tup_with(indices, (char *) new_subtype_decl_node(d));
			}
			N_TYPE(expn) = context_typ;
			make_insert_node(expn, indices, copy_node(expn));
		}
		type2 = context_typ;/* No further constraints */
		break;
	/* For an attribute, we complete the type checking of the right
	 * argument, and if it must be a static expression, we perform
	 * the appropriate check and extract the attribute.
	 */
	case as_attribute:
	case as_range_attribute:
		type2 = resolve2_attr(expn, context_typ);
		/* return immediately if resolve_attr failed due to pass1_error */
		if (type2== (Symbol)0) return;
		break;
	/* A conversion may imply a run-time action, or may be used
	 * between types of the same structure to achieve type consistency.
	 * In the later case, do not emit any conversion.
	 * In both cases however, a range check may be needed.
	 */

	case as_convert:
		t_node = N_AST1(expn);
		expn1 = N_AST2(expn);
		target_type = N_UNQ(t_node);
		type2 = target_type;
		types = N_PTYPES(expn1);
		/* Apply the preference rule to choose a universal meaning for
		 * the expression in case of overloading of operators.
		 */
		/*tu = set_inter(types, univ_types);*/
		tu = set_new(0);
		if (set_mem((char *) symbol_universal_integer, types))
			tu = set_with(tu, (char *) symbol_universal_integer);
		if (set_mem((char *) symbol_universal_real, types))
			tu = set_with(tu, (char *) symbol_universal_real);
		if (set_size(types) > 1 && set_size(tu) == 1)
			types = tu;
		else set_free(tu);

		/* Verify that original expression is unambiguous. */
		if (set_size(types) != 1) {
#ifdef ERRNUM
			errmsgn(256, 257, expn1);
#else
			errmsg("ambiguous expression for conversion", "4.6", expn1);
#endif
			return;
		}
		else {
			t = (Symbol) set_arb(types);
			/*	resolve2(expn1, t);  */
			if (is_numeric(t) && is_numeric(target_type)) {
				/* conversions between any two numeric types are allowed. */
				/* all done */

				resolve2 (expn1, t);
				N_AST2 (expn) = expn1;
				/*        N_AST1 (expn) = new_name_node (t); */
				N_TYPE (expn) = target_type;
			}
			/* conversion of records with discriminant will be valid if
			 *   the discriminants have the same values
			 */
			else if (is_record (target_type) && has_discriminants (target_type)
			  && (root_type (target_type) == root_type (t))) {
				resolve2 (expn1, t);
				N_KIND (expn) = as_qual_discr;
				N_AST1 (expn) = expn1;
				N_AST2 (expn) = (Node) 0;
				N_TYPE (expn) = target_type;
			}
			/* conversion of access values pointing to arrays will be valid
			 * if the indexes of the designated type have the same values
			 */

			else if (is_access (target_type)
			  && is_array (designated_type(target_type))
			  && (root_type (target_type) == root_type (t))) {
				resolve2 (expn1, t);
				N_KIND (expn) = as_qual_aindex;
				N_AST1 (expn) = expn1;
				N_AST2 (expn) = (Node) 0;
				N_TYPE (expn) = target_type;
			}
			/* conversion of access values pointing to records with discriminant
			 * will be valid if the discriminants of the designated type have
			 * the same values
			 */

			else if (is_access (target_type)
			  && is_record (designated_type(target_type))
			  && has_discriminants (designated_type(target_type))
			  && (root_type (target_type) == root_type (t))) {
				resolve2 (expn1, t);
				N_KIND (expn) = as_qual_adiscr;
				N_AST1 (expn) = expn1;
				N_AST2 (expn) = (Node) 0;
				N_TYPE (expn) = target_type;
			}

			else if (root_type(target_type) == root_type(t)) {
				/* conversions among types derived from a common root. In
				 * the absence of representation specifications, this is a
				 * noop, indicated here by having the same type on both sides
				 */
				resolve2 (expn1, t);
				N_AST2 (expn) = expn1;
				/* N_AST1 (expn) = new_name_node (t); */
				N_TYPE (expn) = target_type;
			}
			else if (is_array(target_type)) {
				/* conversion between array types are allowed, if types of
				 * indices are convertible and component types are the same.
				 */
				exists = FALSE;
				if ( is_array(t)
				  && no_dimensions(t) == no_dimensions(target_type))
					exists = TRUE;
				if (exists) {
					for (i = 1; i <= no_dimensions(t); i++) {
						if (root_type((Symbol)index_types(target_type)[i])
						   != root_type((Symbol)index_types(t)[i])) {
							exists = FALSE; 
							break;
						}
					}
				}
				if (exists) {
					if ( base_type((Symbol)component_type(target_type))
					  != base_type((Symbol) component_type(t)))
						exists = FALSE;
				}
				if (exists) { 		 /* convertible */
					/* the following lines have been translated from the Setl
					 * version
					 */

					if (is_access (component_type (t))) {
						c1 = designated_type (component_type (t));
						c2 = designated_type (component_type (target_type));
					}
					else {
						c1 = component_type (t);
						c2 = component_type (target_type);
					}
					if ((can_constrain (c1)) != (can_constrain (c2))) {
#ifdef ERRNUM
						l_errmsgn(480, 481, 482, expn);
#else
						errmsg_l ("component types in array conversion must",
						  " be both constrained or unconstrained", 
						  "4.6 (11)", expn);
#endif
						return;
					}
					resolve2 (expn1, t);
					N_AST2 (expn) = expn1;
					N_TYPE (expn) = target_type;

					check_array_conversion(expn, t, target_type);
				}
				else {
#ifdef ERRNUM
					errmsgn(258, 257, expn);
#else
					errmsg("Invalid array conversion", "4.6", expn);
#endif
					return;
				}
			}
			else {
#ifdef ERRNUM
				id_errmsgn(259, target_type, 257, expn);
#else
				errmsg_id("cannot convert to %", target_type, "4.6", expn);
#endif
			}
		}
		/* if (N_KIND(expn) == as_insert) expn = N_AST1(expn);
		 *   	 N_TYPE(expn) = base_type(type2);
		 * 	  the result of the conversion must belong to the target subtype.
		 *   	 if (!is_array(t)) {
		 *   	     apply_constraint(expn, type2);
		 */

		apply_constraint (expn, target_type);
		break;
	case as_qualify:

		/* proc resolve2_qualify(expn, context_type);
		 * sem_trace3(3, 'At proc resolve2_qualify ', expn);
		 * [-, to_type, expn1] := expn;
		 * $ No sliding for aggregates here.
		 * may_others := full_others;
		 * full_others := true;
		 * expn2 := eval_static(apply_constraint(resolve2(expn1, to_type),
		 * to_type));
		 * full_others := may_others;                 
		 * return [['qualify', expn2], to_type];
		 */
		t_node = N_AST1(expn);
		expn1 = N_AST2(expn);
		type2 = N_UNQ(t_node);

		/* This is non-sliding context for aggregates. */
		may_others = full_others;
		full_others = TRUE;
		resolve2(expn1, type2);
		eval_static(expn1);

		apply_constraint(expn1, type2);			/* impose checks. */

		full_others = may_others;
		break;
	/* For a subtype, complete the evaluation of the bounds.
	 * If the bounds are literal, the type may be a universal one.
	 * replace it now by the corresponding non-literal type.
	 */
	case as_subtype:
		type_node = N_AST1(expn);
		constraint = N_AST2(expn);
		low = N_AST1(constraint);
		high = N_AST2(constraint);
		/* If the bounds are overloaded, the subtype itself may be an
		 * overloaded expression. Extract the type(s) that are compatible
		 * with context .
		 */
		ntypes = set_new(0);
		FORSET(ntype_sym = (Symbol), types, fs1);
			if (compatible_types(context_typ, ntype_sym))
				ntypes = set_with(ntypes, (char *) ntype_sym);
		ENDFORSET(fs1); 
		set_free(types);
		types = ntypes;
		/* Make sure that only one type is possible. */
		if (set_size(types) > 1) {
			/*types = set_diff(types, univ_types);*/
			ntypes = set_new(0);
			FORSET(ntype_sym = (Symbol), types, fs1);
				if (ntype_sym != symbol_universal_integer
				  && ntype_sym != symbol_universal_real)
					ntypes = set_with(ntypes, (char *) ntype_sym);
			ENDFORSET(fs1); 
			set_free(types); 
			types = ntypes;
		}
		if (set_size(types) != 1) {
			type_error(set_new1((char *)symbol_any), context_typ, 
			  set_size(types), expn);
			N_TYPE(expn) = symbol_any;
			return;
		}
		else
			b_type = base_type((Symbol)set_arb(types));

		/* In the case of a range in a membership op, the type may be a real
		 * one, in which case the precision is inherited from the context .
		 */
		rtype = root_type(context_typ);

		if (rtype == symbol_float || rtype == symbol_universal_real)
			kind = as_digits;
		else if (is_fixed_type(rtype))
			kind = as_delta;
		else
			kind = as_range;/* $ Discrete type. */

		if (type_node != OPT_NODE)
			b_type = N_UNQ(type_node);
		else {
			if (kind == as_range) {
				if (b_type == symbol_universal_integer) {
					b_type = symbol_integer;
					if (context_typ == symbol_universal_integer
					  && (N_KIND(low) == as_op 
					  || N_KIND(low) == as_un_op 
					  || N_KIND(high) == as_op
					  || N_KIND(high) == as_un_op)) {
						/* i.e. discrete range in arr def. or iteration rule.*/
						/* Not a literal, named number, or attribute(3.6.1(2))*/
#ifdef ERRNUM
						l_errmsgn(260, 261, 195, expn);
#else
						errmsg_l("Invalid universal expression in",
						  " discrete range", "3.6.1", expn);
#endif
						N_TYPE(expn) = symbol_any;
						return;
					}
				}
			}
			else if (kind == as_delta)
				b_type = context_typ;
			else if (kind == as_digits)
				b_type = symbol_float;
		}
		/* If the type name was not specified, then it is the type
		 * of the bounds.
		 */
		if (type_node == OPT_NODE) {
			type_node = node_new(as_simple_name);
			copy_span(constraint, type_node);
			N_UNQ(type_node) = b_type;
			N_AST1(expn) = type_node;
			N_AST2(expn) = constraint;
			if (N_AST3_DEFINED(N_KIND(expn))) N_AST3(expn) = (Node)0;
			if (N_AST4_DEFINED(N_KIND(expn))) N_AST4(expn) = (Node)0;
		}
		resolve2(low, b_type);
		resolve2(high, b_type);
		/* An index constraint may depend on a discriminant . Verify that
		 * if a discriminant appears, it is by itself, and not as part of
		 * a larger expression. 
		 */
		check_discriminant(low);
		check_discriminant(high);
		eval_static(low);
		eval_static(high);
		if (is_discrete_type(b_type)) check_bounds_in_range(low, high, b_type);

		/* No constraint is imposed on the subtype node itself.*/
		type2 = b_type;
		context_typ = b_type;
		break;
	case as_call_or_index:
		/* Find the tree which has a type compatible with the context, and
		 * resolve it.
 		 */
		call_node = N_AST1(expn);
		index_node = N_AST2(expn);
		exists = FALSE;
		FORSET(t = (Symbol), N_PTYPES(call_node), fs1);
			if (compatible_types(t, context_typ)) {
				exists = TRUE;
				break;
			}
		ENDFORSET(fs1);
		if (exists) {
			node = call_node;
			exists = FALSE;
			FORSET(t2 = (Symbol), N_PTYPES(index_node), fs1);
				if( compatible_types(t2, context_typ)) {
					exists = TRUE;
					break;
				}
			ENDFORSET(fs1);
			if (exists) {
				remove_conversions(call_node);	/* last chance */
				remove_conversions(index_node);
				exists = FALSE;
				FORSET(t = (Symbol), N_PTYPES(call_node), fs1);
					if ( compatible_types(t, context_typ)) {
						exists = TRUE;
						break;
					}
				ENDFORSET(fs1);
				if (exists) {
					node = call_node;
					exists = FALSE;
					FORSET(t2 = (Symbol), N_PTYPES(index_node), fs1);
						if (compatible_types(t2, context_typ)) {
							exists = TRUE;
							break;
						}
					ENDFORSET(fs1);
					if (exists) {
#ifdef TBSL
						type_error(set_new1('call or index'), context_typ, 2,
						  expn);
#endif
					}
				}
				else node = index_node;
			}
		}
		else node = index_node;
		resolve2(node, context_typ);
		copy_attributes(node, expn);
		type2 = N_TYPE(node);
		break;
	default:
		/* Other operators require no propagation */
		type2 = (Symbol) set_arb(types);
		break;
	}

	if (compatible_types(context_typ, type2)) N_TYPE(expn) = type2;
	else {
#ifdef ERRNUM
		type_errmsgn(262, context_typ, 10, expn);
#else
		errmsg_type("Incorrect type for expression. Expect %", context_typ,
		  "none", expn);
#endif
	}
}

static Symbol resolve2_attr(Node expn, Symbol context_typ)	/*;resolve2_attr*/
{
	Forset	fs1;
	Set		types;
	int		attrkind, dim, out_c;
	Symbol	type2;
	Const	con;
	Node	attr_node, arg1, arg2;
	Set		types1, types2;
	Symbol	type1, t2, itype1;

	types = N_PTYPES(expn);
	attr_node = N_AST1(expn);
	arg1 = N_AST2(expn);
	arg2 = N_AST3(expn);
	/*  The type of the right argument is determined by the attribute,
	 *  and has already been evaluated in the case of array attributes.
	 */
	/*attribute = N_VAL(attr_node); -- should be dead  ds 3-13-86*/
	attrkind = (int) attribute_kind(expn);
	types1 = N_PTYPES(arg1);
	types2 = N_PTYPES(arg2);
	type1 = (Symbol) set_arb(types1);

	if (attrkind == ATTR_PRED 
	  ||attrkind == ATTR_SUCC 
	  ||attrkind == ATTR_POS 
	  ||attrkind == ATTR_IMAGE)
		t2 = base_type(type1);
	else if (attrkind == ATTR_VALUE)
		t2 = symbol_string;
	else if (attrkind == ATTR_VAL) {
		Symbol t;
		Set otypes2;
		otypes2 = types2;
		types2 = set_new(0);
		FORSET(t = (Symbol), otypes2, fs1);
			if (compatible_types(t, symbol_integer_type))
				types2 = set_with(types2, (char *) t);
		ENDFORSET(fs1);
		if (set_size(types2) == 0) {
#ifdef ERRNUM
			errmsgn(263, 233, arg2);
#else
			errmsg("Second argument of VAL must be of some integer type",
			 "Annex A", arg2);
#endif
			return (Symbol)0;
		}
		else if (set_size(types2) == 1)
			t2 = (Symbol) set_arb(types2);
		else if (set_mem((char *) symbol_universal_integer, types2))
			t2 = symbol_universal_integer;
		else {
#ifdef ERRNUM
			errmsgn(264, 233, arg2);
#else
			errmsg("ambiguous argument for attribute VAL", "Annex A", arg2);
#endif
			return (Symbol)0;
		}
	}
	else
		t2 = symbol_integer;

	if  (attrkind != ATTR_O_FIRST && attrkind != ATTR_T_FIRST
	  && attrkind != ATTR_O_LAST && attrkind != ATTR_T_LAST
	  && attrkind != ATTR_O_RANGE && attrkind != ATTR_T_RANGE
	  && attrkind != ATTR_O_LENGTH && attrkind != ATTR_T_LENGTH)
		resolve2(arg2, t2);
	if (t2 == symbol_universal_integer)		/* possible for VAL */
		specialize(arg2, symbol_integer);
	if ((attrkind == ATTR_POSITION || attrkind == ATTR_FIRST_BIT
	  || attrkind == ATTR_LAST_BIT) && N_KIND(arg1) != as_selector) {
#ifdef ERRNUM
		errmsgn(265, 266, arg1);
#else
		errmsg("attribute must apply to selected component", "13.7.2", arg1);
#endif
	}
	/*
	 * If the left argument is a type, or if it is a constrained
	 * object, then evaluate the attribute on the type, statically if
	 * possible.
	 */
	/*
	 * All attributes, except those that  are functions,  can be applied
	 * to  an out parameter, because  they do not require reading of the
	 * object, or read  only its bounds. On the other hand, 	 if the pre-
	 * fix is an access type, it cannot be an an out parameter (4.1(4)).
	 */
	out_c = out_context; /* Save current setting*/
	out_context = !reads_prefix(attrkind, type1);
	itype1 = type1;

	if (is_array(type1)
	  && (attrkind == ATTR_O_FIRST || attrkind == ATTR_T_FIRST
	  || attrkind == ATTR_O_LAST || attrkind == ATTR_T_LAST
	  || attrkind == ATTR_O_RANGE || attrkind == ATTR_T_RANGE
	  || attrkind == ATTR_O_LENGTH || attrkind == ATTR_T_LENGTH)) {
		/*	The second argument indicates the dimension whose attribute
		 * is sought. It must be a static integer(this has been checked
		 * already).
		 */
		if (!is_static_expr(arg2))
			dim = 1;	/* By default. */
		else {
			con = (Const) N_VAL(arg2);
			dim = con->const_value.const_int;
		}
		itype1 = (Symbol) (index_types(type1)[dim]);
	}

	if (is_type_node(arg1)) {
		/* This might cause problems in eval_static. */
		/* In at least some cases, N_PTYPES has been set (cf. 4a.c line 1009),
		 * so here we clear N_PTYPES lest it be mistaken for N_TYPE (DS 9-18-86)
		 */
		N_PTYPES(arg1) = (Set) 0;
		N_UNQ(arg1) = itype1;
	}
	else if (attrkind == ATTR_COUNT) {
		/* entry name is fully resolved in first pass. */
		;		/* no op */
	}
	else {
		resolve2(arg1, type1);
	}
	out_context = out_c; /* restore	*/

	if (in_univ_attributes(attrkind)) {
		if (is_static_expr(expn)) {
			/* Specialize value if context is not universal.*/
			eval_static(expn);
			specialize(expn, context_typ);
		}
		/* in nay case indicate desired context type for subsequent conversion*/
		type2 = base_type(context_typ);
	}
	else {				 /*$$$ TBSL: check for FIRST_BIT, LAST_BIT*/
		type2 = (Symbol) set_arb(types);
	}
	return type2;
}

static int in_univ_attributes(int attrkind)				/*;in_univ_attributes*/
{
	/* test if type of attribute is universal type */
	static int attrs[] = {
		ATTR_AFT, ATTR_COUNT, ATTR_DIGITS, ATTR_EMAX, ATTR_FIRST_BIT, ATTR_FORE,
		ATTR_LAST_BIT, ATTR_O_LENGTH, ATTR_T_LENGTH, ATTR_MACHINE_EMAX,
		ATTR_MACHINE_EMIN, ATTR_MACHINE_MANTISSA, ATTR_MACHINE_RADIX,
		ATTR_MANTISSA, ATTR_POS, ATTR_POSITION, ATTR_SAFE_EMAX, ATTR_O_SIZE,
		ATTR_T_SIZE, ATTR_STORAGE_SIZE, ATTR_WIDTH, ATTR_DELTA, ATTR_EPSILON,
		ATTR_LARGE, ATTR_SMALL, ATTR_SAFE_LARGE, ATTR_SAFE_SMALL,
		ATTR_O_CONSTRAINED, ATTR_T_CONSTRAINED, ATTR_MACHINE_OVERFLOWS,
		ATTR_MACHINE_ROUNDS, ATTR_CALLABLE, ATTR_TERMINATED, 999	};
	int i;
	for (i = 0; ; i++) {
		if (attrs[i] == 999) return FALSE;
		if (attrs[i] == attrkind) return TRUE;
	}
}

static void check_bounds_in_range(Node low, Node high, Symbol b_type)
													/*;check_bounds_in_range*/
{
	/* check if the bounds of an array with a subtype_declaration are
	 * in the bounds of the base_type, when static. (When not static,
	 * a qual_range is introduced on as_convert).
	 */

	Node    lbd_range, ubd_range;
	int     low_val, high_val, lbd_val, ubd_val;
	Tuple   b_range_tup;
	Const	  low_const, high_const, lbd_const, ubd_const;

	b_range_tup   = SIGNATURE(b_type);
	lbd_range     = (Node) b_range_tup[2];
	ubd_range     = (Node) b_range_tup[3];
	if (N_KIND(low) == as_qualify) low = N_AST2(low);
	if (N_KIND(high) == as_qualify) high = N_AST2(high);

	if (is_static_expr(low) && is_static_expr(high)
	  && is_static_expr(lbd_range) && is_static_expr(ubd_range))  {
		low_const = (Const) N_VAL(low);
		high_const = (Const) N_VAL(high);
		lbd_const = (Const) N_VAL(lbd_range);
		ubd_const = (Const) N_VAL(ubd_range);
		const_check(low_const, CONST_INT);
		const_check(high_const, CONST_INT);
		const_check(lbd_const, CONST_INT);
		const_check(ubd_const, CONST_INT);
		low_val = INTV(low_const);
		high_val = INTV(high_const);
		lbd_val = INTV(lbd_const);
		ubd_val = INTV(ubd_const);

		if ((lbd_val > ubd_val && low_val <= high_val)
		  || ((low_val <= high_val) && (low_val < lbd_val || low_val > ubd_val
		  || high_val > ubd_val || high_val < lbd_val))) {
			create_raise(low, symbol_constraint_error);
			return;
		}
	}
}

static void check_array_conversion(Node expn, Symbol from_t, Symbol to_t)
												/*;check_array_conversion */
{
	/* verify that in an array conversion, source and target component types
	 * have the same constraints.
	 */

	Symbol from_c, to_c;
	Tuple  checks;
	Tuple from_i, to_i;
	int i;

	checks = tup_new(0);

	from_c = component_type(from_t);
	to_c = component_type(to_t);

	while (is_access (from_c)) {
		from_c = designated_type (from_c);
		to_c = designated_type (to_c); 
	}

	if (from_c == to_c) {
		;
	}
	else if (is_scalar_type(from_c))
		checks = tup_with(checks, (char *) new_check_bounds_node(from_c, to_c));
	else if (is_record (from_c) && has_discriminants (from_c))
		checks = new_check_disc_node (from_c, to_c); 
	else if (is_array(from_c)) {
		/* index subtypes must be equal */
		from_i = index_types(from_c);
		to_i = index_types(to_c);
		for (i = 1; i<= tup_size(from_i); i++) {
			checks = tup_with(checks,
			  (char *) new_check_bounds_node( (Symbol)from_i[i],
			  (Symbol)to_i[i]));
		}
	}
	/* TBSL: check values of discriminants for record types. */

	if (tup_size(checks) > 0) {
		make_insert_node(expn, checks, copy_node(expn));
		/* This line has to be deleted in order to reuse the function
      in case of conversion of array access values 
      N_TYPE(expn) = to_t; */
	}
}

static int reads_prefix(int attrkind, Symbol type1)
															/*;reads_prefix*/
{
	/* Used to determine whether an attribute can apply to an out parameter.
	 * see tests A62006d, B62006c, B85007C.
	 */

	if  (attrkind == ATTR_BASE
	  || attrkind == ATTR_POS
	  || attrkind == ATTR_PRED
	  || attrkind == ATTR_SUCC
	  || attrkind == ATTR_VAL
	  || attrkind == ATTR_VALUE)
		return TRUE;

	if (is_access(type1))  return TRUE;
	return FALSE;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.