/*ident	"@(#)ctrans:src/expr2.c	1.1.5.36" */
/***************************************************************************

	C++ source for cfront, the C++ compiler front-end
	written in the computer science research center of Bell Labs

	Copyright (c) 1984 AT&T, Inc. All rigths Reserved
	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T, INC.

expr2.c:

	type check expressions

************************************************************************/

#include "cfront.h"
#include "size.h"
extern Pname conv_dominates(Pname,Pname);
static int const_obj1,const_obj2;

Pname really_dominate(Pname on1, Pname on2, bit tc)
{
	Pfct f1 = on1->tp->base==FCT ? Pfct(on1->tp) :
		Pfct(Pgen(on1->tp)->fct_list->f->tp);
	Pfct f2 = on2->tp->base==FCT ? Pfct(on2->tp) :
		Pfct(Pgen(on2->tp)->fct_list->f->tp);

	// const check
	int c1 = f1->f_const;
	int c2 = f2->f_const;

	if(c1 == c2) ;
	else if(c1 && !c2) return tc ? on1 : on2;
	else if(c2 && !c1) return tc ? on2 : on1;

	// hierarchy check
	Pname on = conv_dominates(on1,on2);
	if(on) return on;
}

void name::assign()
{
	if (n_assigned_to++ == 0) {
		switch (n_scope) {
		case FCT:
			if (n_used && n_addr_taken==0)  {
				Ptype t = tp;
			ll:
				switch (t->base) {
				case TYPE:
					t=Pbase(t)->b_name->tp; goto ll;
				case VEC:
					break;
				default:
					if (curr_loop)
						error('w',&where,"%n may have been used before set",this);
					else
						error('w',&where,"%n used before set",this);
				}
			}
		}
	}
}

void name::take_addr()
{
// error('d', "%n->take_addr tp: %t", this, tp?tp:0 );
// error('d', "%n->take_addr tp: %d %d", this, tp?tp:0, tp?tp->base:0 );
	if ( (warning_opt) && (! n_addr_taken) && (tp) && (tp->base==FCT) && Pfct (tp)->f_inline)
		error('w',"can't take address of inline function %n, %n not inlined", this, this);  
	n_addr_taken++; 
	if ( n_sto == EXTERN && tp ) {
		Ptype t = tp;
		while ( t->base == TYPE )  
			t = Pbase(t)->b_name->tp;
		switch ( t->base ) {
			case COBJ:
				t = Pbase(t)->b_name->tp; // no break
			case CLASS: {
				Pclass cl = Pclass(t);
				if ( cl->c_body == 1 )
					cl->dcl_print(0);
			}
		}
	}
}

int ignore_const;	// for use by ref_init
static is_dataMemPtr(Pexpr);

int expr::lval(TOK oper)
{
	register Pexpr ee = this;
	register Pname n;
	int deref = 0;
	char* es;

//error('d',"%k expr::lval %k",base,oper);

	switch (oper) {
	case ADDROF:
	case G_ADDROF:	es = "address of";	break;
	case DEREF:	es = "dereference of";	break;
	case INCR:	es = "increment of";	goto def;
	case DECR:	es = "decrement of";	goto def;
	default:	es = "assignment to";
	def:
		if (ignore_const==0 && tp->tconst()) {
			if (oper) {
				if (base == NAME) {
					if (vec_const && Pname(this)->n_scope==ARG) break;
					error("%s constant%n",es,this);
				}
				else
					error("%s constant",es);
			}
			return 0;
		}
	}

	for(;;) {
//error('d',"loop %k",ee->base);
		switch (ee->base) {
	//	case G_CALL:
	//	case CALL:
		default:
		defa:
			if (deref == 0) {
				if (oper) error("%s%k (not an lvalue)",es,ee->base);
				return 0;
			}
			return 1;
		case ZERO:
		case CCON:
		case ICON:
		case FCON:
			if (oper) error("%s numeric constant",es);
			return 0;
		case STRING:
			if (oper) error('w',"%s string constant",es);
			return 1;
		case CAST:
		switch( oper ) {
		case 0:
		case ADDROF:
		case G_ADDROF:
		case DEREF:
     			goto defa;
		default:
			if ( ee->tp->base == PTR 
			     && is_dataMemPtr(ee) ) 
			{ // check for const class object
    				Pexpr te;
				te = ee->e1->e1->e1;	 
       				if ( te->base == G_ADDROF )
					te = te->e2;
       				if ( te->base == NAME ) {
					Ptype pt = te->tp;
					if ( pt->base == PTR )
						pt = Pptr(pt)->typ;
					if ( pt->tconst() ) 
						error("%sCMP of const%n",es,te);
					return 0;
				}
			}
			goto defa;
		}

		case DEREF:
		{	
			Pexpr ee1 = ee->e1;
// error( 'd', "ee1: %k", ee1->base );
			switch (ee1->base) {	// *& vanishes
			case ADDROF:	// *&
				return 1;
			case G_CM:
			case CM:	// look for *(a,&b)
				if (ee1->e2->base==G_ADDROF
				|| ee1->e2->base==ADDROF)
					return 1;
				goto defaa;
			case QUEST:	// look for *(q?&a:&b)
				if ((ee1->e1->base==G_ADDROF
					|| ee1->e1->base==ADDROF)
				&& (ee1->e2->base==G_ADDROF
					|| ee1->e2->base==ADDROF))
					return 1;
				// no break
			default:
			defaa:
				ee = ee1;
				deref = 1;
			}
			break;
		}

		case QUEST:
		{	int x1 = ee->e1->lval(deref?0:oper);
			int x2 = ee->e2->lval(deref?0:oper);
			if (ee->e1->tp->check(ee->e2->tp,0)) return 0;
			if (deref) return 1;
			return x1 && x2;
		}

		case INCR:
		case DECR:
			if (e1) goto defa;	// postfix does not preserve lval
		case ASSIGN:
		case ASPLUS:
		case ASMINUS:
		case ASMUL:
		case ASDIV:
		case ASMOD:
		case ASAND:
		case ASOR:
		case ASER:
		case ASLS:
		case ASRS:
			return 1;

		case CM:
		case G_CM:
			if (ee->e2->lval(deref?0:oper)==0) return deref;
			return 1;

		case MEMPTR:
			ee = ee->e2;
			break;

		case MDOT:
			ee = ee->mem;
			break;

		case DOT:
		{
// error('d',"dot %k oper %k",ee->e1->base,oper);
			Pexpr e = 0;
			int e_const = 0; // to catch x.y.val = 1, where x is const

			switch (ee->e1->base) {		// update use counts, etc.
			case NAME:
				switch (oper) {
				case ADDROF:
				case G_ADDROF:	Pname(ee->e1)->take_addr();
				case 0:		break;
				case ASSIGN:	Pname(ee->e1)->n_used--;
				default:	Pname(ee->e1)->assign(); // asop
				}
				break;
			case DOT:
				e = ee->e1;
				do e=e->e1; while (e->base==DOT);
				if (e->base == NAME) {
					e_const = e->tp->tconst();
					switch (oper) {
					case ADDROF:
					case G_ADDROF:	Pname(e)->take_addr();
					case 0:		break;
					case ASSIGN:	Pname(e)->n_used--;
					default:	Pname(e)->assign(); // asop
					}
				}
			}

			n = Pname(ee->mem);
			while (n->base == MDOT) n = Pname(Pref(n)->mem);

			if (deref==0 && 
				(ee->e1->tp->tconst() || e_const)) {

				switch (oper) {
				case 0:
				case ADDROF:
				case G_ADDROF:
				case DEREF:
					break;
				default:
					error("%sM%n of%t",es,n,e_const?e->tp:ee->e1->tp);
				}
				return 0;
			}
		}
		goto xx;

		case REF:
			n = Pname(ee->mem);
			while (n->base == MDOT) n = Pname(Pref(n)->mem);

			if (deref==0 && ee->e1) {  //BR
				Ptype p = ee->e1->tp;
			zxc:
				switch (p->base) {
				case TYPE:	p = Pbase(p)->b_name->tp; goto zxc;
				case PTR:
				case VEC:	break;
				default:	error('i',"expr::lval %t->%n",p,n);
				}
				if (Pptr(p)->typ->tconst()) {
					switch (oper) {
					case 0:
					case G_ADDROF:
					case DEREF:
						break;
					case ADDROF:
						if ( cm_const_save == 0 && const_ptr == 0 ) 
							error(strict_opt?0:'w',"%sM%n of%t",es,n,Pptr(p)->typ);
						break;
					default:
						error("%sM%n of%t",es,n,Pptr(p)->typ);
					}
					return 0;
				}
			}
			goto xx;

		case NAME:
			n = Pname(ee);
		xx:
// error('d',"name xx: %n oper %d lex_level: %d",n,oper,n->lex_level);
			if (deref) return 1;
			if (oper==0) return n->n_stclass != ENUM ;

			if (n->tp->base==FIELD && Pbase(n->tp)->b_bits==0) {
				error("%s 0-length field%n",es,n);
				return 0;
			}

			switch (oper) {
			case ADDROF:
			case G_ADDROF:
			{	Pfct f = (Pfct)n->tp;

				if (n->n_sto == REGISTER) {
					if (warning_opt) error('w',"& register%n",n);
				//	return 0;
					n->n_sto = 0;
					n->n_stclass = AUTO;
				}

				if (f == 0) {
					error("& label%n",n);
					return 0;
				}

				if (n->n_stclass == ENUM) {
					error("& enumerator%n",n);
					return 0;
				}

				if (n->tp->base == FIELD) {
					error("& field%n",n);
					return 0;
				}

				n->n_used--;
				if (n->n_qualifier) { // oops, not the real one
					Pname tn = Pclass(n->n_table->t_name->tp)->memtbl->look(n->string,0);
					n = tn ? tn : n;
				}
				n->take_addr();

				// suppress hoisting of local consts
				int statmem = n->n_scope==0 || n->n_scope==PUBLIC || n->n_scope == FCT;
				if (n->n_evaluated && n->n_scope!=ARG) { // &const
					if (statmem == 0 && n->n_dcl_printed==0) {
						n->n_initializer = new ival(n->n_val);
						n->dcl_print(0);
					}
				}
				else if (f->base==FCT && n->n_dcl_printed==0)
					n->dcl_print(0);
				break;
			}

			case ASSIGN:
//error('d',"ass %n %d",n,n->n_used);
				n->n_used--;
				n->assign();
				break;
			//	goto check_void;
			default:	/* incr ops, and asops */
				if (cc->tot && n==cc->c_this) {
					error("%n%k",n,oper);
					return 0;
				}
		//	check_void:
		//		{	Ptype t = n->tp;
		//			while (t->base==TYPE) t = Pbase(t)->b_name->tp;
		//			if (t==Pvoid_type) {
		//				error("%s%t",es,n->tp);
		//				return 0;
		//			}
		//		}
				n->assign();
			}
			return 1;
		}
	}
}

int char_to_int(char* s)
/*	assume s points to a string:
		'c'
	or	'\c'
	or	'\0'
	or	'\ddd'
	or multi-character versions of the above
	(hex constants have been converted to octal by the parser)
*/
{
	register int i = 0;
	register char c, d, e;

	switch (*s) {
	default:
		error('i',"char constant store corrupted");
	case '`':
		error("bcd constant");
		return 0;
	case '\'':
		break;
	}

	for(;;)			/* also handle multi-character constants */
	switch (c = *++s) {
	case '\'':
		return i;
	case '\\':			/* special character */
		switch (c = *++s) {
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7':	/* octal representation */
			c -= '0';
			switch (d = *++s) {		/* try for 2 */
				
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7':
				d -= '0';
				switch (e = *++s) {	/* try for 3 */
					
				case '0': case '1': case '2': case '3': case '4':
				case '5': case '6': case '7':
					c = c*64+d*8+e-'0';
					break;
				default:
					c = c*8+d;
					s--;
				}
				break;
			default:
				s--;
			}
			break;
		case 'a':
			c = '\a';
			break;
		case 'b':
			c = '\b';
			break;
		case 'f':
			c = '\f';
			break;
		case 'n':
			c = '\n';
			break;
		case 'r':
			c = '\r';
			break;
		case 't':
			c = '\t';
			break;
		case 'v':
			c = '\v';
			break;
		case '\\':
			c = '\\';
			break;
		case '\'':
			c = '\'';
			break;
		}
		/* no break */
	default:
		if (i) i <<= BI_IN_BYTE;
		i += c;
	}
}

const A10 = 'A'-10;
const a10 = 'a'-10;

long str_to_long(register const char* p)
{
	register c;
	register unsigned long i= 0;
	const char* pp = p;

// error( 'd', "str_to_long: %s", p );

	if ((c=*p++) == '0') {
		switch (c = *p++) {
		case 0:
			return 0;

		case 'l':
		case 'L':	/* long zero */
			return 0;

		case 'x':
		case 'X':	/* hexadecimal */
			while (c=*p++) {
				switch (c) {
				case 'l':
				case 'L':
				case 'U':
				case 'u':
					return i;
				case 'A':
				case 'B':
				case 'C':
				case 'D':
				case 'E':
				case 'F':
					i = i*16 + c-A10;
					break;
				case 'a':
				case 'b':
				case 'c':
				case 'd':
				case 'e':
				case 'f':
					i = i*16 + c-a10;
					break;
				default:
					i = i*16 + c-'0';
				}
			}
			return i;

		default:	/* octal */
			do 
				switch (c) {
				case 'l':
				case 'L':
				case 'U':
				case 'u':
					return i;
				default:
					i = i*8 + c-'0';
				}
			while (c=*p++);
			return i;
		}
	}	
				/* decimal */
	i = c-'0';
	while (c=*p++)
		switch (c) {
		case 'l':
		case 'L':
		case 'U':
		case 'u':
			return i;
		default:
		{       unsigned long ii = i;
			i = i*10 + c-'0';
			if (i<ii) goto bad;
		}
		}
	return i;
bad:
	error("integer constant %s larger than the largest long",pp);
	return i;
	
		
}

bit type::is_unsigned()
{
	Ptype t = this;
	while (t->base==TYPE) t = Pbase(t)->b_name->tp;
	if (t->base == PTR) return 0;
	return Pbase(t)->b_unsigned;
}

char* Neval;
bit binary_val;

unsigned long expr::ueval(long x1, long x2)
{
	unsigned long i1 = (unsigned long) x1;	
	unsigned long i2 = (unsigned long) x2;
//error('d',"ueval %k %ld %ld",base,x1,x2);
	switch (base) {
	case UMINUS:	return -i2;
	case UPLUS:	return i2;
	case NOT:	return !i2;
	case COMPL:	return ~i2;
	case CAST:	return i1;
	case PLUS:	return i1+i2;
	case MINUS:	return i1-i2;
	case MUL:	return i1*i2;
	case LS:	return i1<<i2;
	case RS:	return i1>>i2;
	case NE:	return i1!=i2;
	case EQ:	return i1==i2;
	case LT:	return i1<i2;
	case LE:	return i1<=i2;
	case GT:	return i1>i2;
	case GE:	return i1>=i2;
	case AND:	return i1&i2;
 	case ANDAND:	return i1&&i2;
	case OR:	return i1|i2;
	case OROR:	return i1||i2;
	case ER:	return i1^i2;
	case MOD:	if (i2 == 0) {
				if (Neval == 0) { 
					Neval = "mod zero";
					error("mod zero");
				}
				return 1;
			}
			return i1%i2;
	case QUEST:	return (cond->eval()) ? i1 : i2;
	case DIV:	if (i2 == 0) {
				if (Neval == 0) { 
					Neval = "divide by zero";
					error("divide by zero");
				}
				return 1;
			}
			return i1/i2;
	case CM:
	case G_CM:
		return i2;
	}

	Neval = "unsigned expression";
	return 0;
}

long expr::eval()
{
	if (Neval) return 1;
// error('d',"eval %k",base);

	static int targno=0;
	static int icallflag=0;

	switch (base) {
	case ZERO:	return 0;
	case IVAL:	return i1;
	case ICON:	return str_to_long(string);
	case CCON:	return char_to_int(string);
	case FCON:	Neval = "float in constant expression"; return 1;
	case STRING:	Neval = "string in constant expression"; return 1;
	case EOBJ:	return Pname(this)->n_val;
	case SIZEOF:
		extern no_sizeof;
		if (no_sizeof) Neval = "sizeof";
		return tp2->tsizeof();
		
	case NAME:
	{	Pname n = Pname(this);
// error('d',"eval %n eval %d %d",n,n->n_evaluated,n->n_val);
// error('d',"eval tp->tconst() %d, n->n_initializer: %k", n->tp->tconst(), n->n_initializer?n->n_initializer->base:0 );
		if (n->n_evaluated && n->n_scope!=ARG) return n->n_val;
		if (binary_val && strcmp(string,"_result")==0) return 8888;
		Neval = "cannot evaluate constant";
		return 1;
	}
	case ICALL:
		if (e1) {
			icallflag=1;
			targno=0;
			il->i_next = curr_icall;
			curr_icall = il;
			long i = e1->eval();
			curr_icall = il->i_next;
			icallflag=0;
			return i;
		}
		Neval = "void inlineF";
		return 1;
	case ANAME:
	{	Pname n = (Pname)this;

/*
		int argno;
		if (icallflag) {
		argno=targno;
		targno++;
		}
*/
		int argno = (int) n->n_val;
	
		Pin il;
		for (il=curr_icall; il; il=il->i_next)
			if (il->i_table == n->n_table) goto aok;
		goto bok;
	aok:
		if (il->i_args[argno].local) {
	bok:
			Neval = "inlineF call too complicated for constant expression";
			return 1;
		}
		Pexpr aa = il->i_args[argno].arg;
		return aa->eval();
	}
	case CAST:
	{	if (e1->base==FCON && tp2->base!=FLOAT && tp2->base!=DOUBLE) {
			char* p = e1->string;
			while (*p!='.') p++;
			if (p==e1->string) *p++ = '0';
			*p = 0;
			e1->base = ICON;
		}		
		long i = e1->eval();
		Ptype tt = tp2;
	strip:
		switch (tt->base) {
		default:
			Neval = "cast to non-integral type in constant expression";
			break;
		case TYPE:
			tt = Pbase(tt)->b_name->tp;
			goto strip;
		case EOBJ:
		case LONG:
		case INT:
		case CHAR:
		case SHORT:
			i &= ~(((~(unsigned long)0)<<(BI_IN_BYTE*(tp2->tsizeof()-1)))<<BI_IN_BYTE);
			break;
		}
		return i;
	}
	case UMINUS:
	case UPLUS:
	case NOT:
	case COMPL:
	case PLUS:
	case MINUS:
	case MUL:
	case LS:
	case RS:
	case NE:
	case LT:
	case LE:
	case GT:
	case GE:
	case AND:
	case OR:
	case ER:
	case DIV:
	case MOD:
	case QUEST:
	case EQ:
	case ANDAND:
		break;
	case OROR:
		if (binary_val) {	// a||b, don't evaluate b if a!=0
			long i1 = (e1) ? e1->eval() : 0;
			if (Neval==0 && i1 && e1->tp->is_unsigned()==0) return i1;
		}
		break;
	case CM:
	case G_CM:
		break;
	case G_ADDROF:
	case ADDROF:
		if (binary_val) {	// beware of &*(T*)0
			switch (e2->base) {
			case NAME:
			case DOT:
			case REF:	return 9999;
			}
		}
	default:
		Neval = "bad operator in constant expression";
		return 1;
	}

	long i1 = (e1) ? e1->eval() : 0;
	long i2 = (e2) ? e2->eval() : 0;

	if (binary_val && i1==9999 && i2==9999) {
		Neval = "";
		return 1;
	}
	
	if (Neval==0
	&& ((e1&&e1->tp->is_unsigned()) || (e2&&e2->tp->is_unsigned())))
		return (long) ueval(i1,i2);
	
	switch (base) {
	case UMINUS:	return -i2;
	case UPLUS:	return i2;
	case NOT:	return !i2;
	case COMPL:	return ~i2;
	case CAST:	return i1;
	case PLUS:	return i1+i2;
	case MINUS:	return i1-i2;
	case MUL:	return i1*i2;
	case LS:	return i1<<i2;
	case RS:	return i1>>i2;
	case NE:	return i1!=i2;
	case EQ:	return i1==i2;
	case LT:	return i1<i2;
	case LE:	return i1<=i2;
	case GT:	return i1>i2;
	case GE:	return i1>=i2;
	case AND:	return i1&i2;
 	case ANDAND:	return i1&&i2;
	case OR:	return i1|i2;
	case OROR:	return i1||i2;
	case ER:	return i1^i2;
	case MOD:	if (i2 == 0) {
				if (Neval == 0) { 
					Neval = "mod zero";
					error("mod zero");
				}
				return 1;
			}
			return i1%i2;
	case QUEST:	return (cond->eval()) ? i1 : i2;
	case DIV:	if (i2 == 0) {
				if (Neval == 0) { 
					Neval = "divide by zero";
					error("divide by zero");
				}
				return 1;
			}
			return i1/i2;
	case CM:
	case G_CM:
		return i2;
	}
}
bit classdef::baseof(Pname f)
/*
	is ``this'' class a public base class of "f"'s class
	or its immediate base class
*/
{
	Ptable ctbl = f->n_table;
	Pname b = ctbl->t_name;

	if (b == 0) return 0;
	Pclass cl = Pclass(b->tp);
	if (cl == 0) return 0;
	if (cl == this) return 1;
	ppbase = PUBLIC;
	Pclass bcl = is_base(cl->string);
	return (bcl && ppbase==PUBLIC);
}

bit classdef::baseof(Pclass cl)
/*
	is ``this'' class a public base class of "cl"
*/
{
	if (cl == 0) return 0;
	if (cl == this) return 1;
	ppbase = PUBLIC;
	Pclass bcl = is_base(cl->string);
	return (bcl && ppbase==PUBLIC);
}

static int mem_match(Pfct f1, Pfct f2)
/*
	check class membership.

	For some reason checking f_this==0 works and f_static doesn't
*/
{
//	if (f1->memof) return f2->f_this ?f2->memof==f1->memof : 0;
//	if (f1 && f1->memof) return f2->f_this?f2->memof==f1->memof : 0;
//	return f2->f_this==0;
	if (f1==0 || f2==0) return 0;
	if (f1->memof && f2->f_this && f2->memof!=f1->memof) return 0;
	if (f2->f_this) return 0;
	if (f1->memof && f2->f_static) return 0;
	if (f1->check(f2,ASSIGN)) return 0;
	return 1;
}

int Pchecked;

Pexpr ptof(Pfct ef, Pexpr e, Ptable tbl)
/*
	a kludge: initialize/assign-to pointer to function
*/
{
	Pfct f;
	Pname n = 0;
eee:
//error('d',"ptof %t %t %k",ef,e->tp,e->base);
	switch (e->base) {
	case QUEST:
		e->e1 = ptof(ef,e->e1,tbl);
		e->e2 = ptof(ef,e->e2,tbl);
		return e;
	case CM:
	case G_CM:
		e->e2 = ptof(ef,e->e2,tbl);
		return e;
	case NAME:
		f = Pfct(e->tp);
		n = Pname(e);

		switch (f->base) {
		case OVERLOAD:
			e = Pgen(f)->find(ef,0);
			if (e == 0) {
				error("cannot deduceT for &overloaded%n",n);
				return e;
			}
		//	e = n;
			// no break
		case FCT:
			Pchecked = mem_match(ef,Pfct(e->tp));
			e = new expr(G_ADDROF,0,e);
			return e->typ(tbl);	// handle &B::f
			//e->tp = f;
		}
		goto ad;

	case ZERO:
		if (ef->memof) {
			e = new expr(ELIST,zero,zero);
			e = new expr(ILIST,e,zero);
			e->tp = zero_type;
			return e; 
		}
		break;

	case MDOT:
		// ?? error('s',"P toM of not firstB");
		do e = e->mem; while (e->base == MDOT);
		goto eee;

	case DOT:
	case REF:
		f = Pfct(e->mem->tp);

		switch (f->base) {
		case OVERLOAD:
			n = Pgen(f)->find(ef,0);
			if (n == 0) error("cannot deduceT for &overloaded%n",e->mem);
			else e = n;
			// no break
		case FCT:
			Pchecked = mem_match(ef,Pfct(e->tp));
			e = new expr(G_ADDROF,0,e);
			return e->typ(tbl);	// handle &B::f
		//	n = Pname(e->mem);
		//	e = n->address();
		}
		goto ad;

	case ADDROF:
	case G_ADDROF:
		f = Pfct(e->e2->tp);
	ad:
		if (f->base == OVERLOAD) {
			n = Pgen(f)->find(ef,0);
			if (n == 0) error("cannot deduceT for &overloaded %s()",Pgen(f)->fct_list->f->string);
			Pchecked = mem_match(ef,Pfct(n->tp));
			e->e2 = n;
			e->tp = n->tp;
		}
		if (n) n->lval(ADDROF);
		break;

	case CAST:
	{
		Pexpr te = e->e1;
		if (e->e1->base == G_ADDROF) te = e->e1->e2;
		(void) ptof(ef,te,tbl);
	}
	}
	return e;
}

Pexpr ptr_init(Pptr p, Pexpr init, Ptable tbl)
/*
	check for silly initializers

	char* p = 0L;	 ??	fudge to allow redundant and incorrect `L'
	char* p = 2 - 2; ??	worse
*/
{
// error('d',"ptr_init: p=%t init->tp=%t init->base %k",p,init->tp,init->base);

	Pchecked = 0;

	Ptype it = init->tp;
itl:
	switch (it->base) {
	case TYPE:
		it = Pbase(it)->b_name->tp; goto itl;
	case ZTYPE:
//		if (init == zero) break;
		break;
	case EOBJ:
	case INT:
	case CHAR:
	case SHORT:
	case LONG:
	{	Neval = 0;
		long i = init->eval();
		if (Neval)
			error("badPIr: %s",Neval);
		else
		if (i)
			error("badPIr value %d",i);
		else {
			DEL(init);
			init = zero;
		}
		break;
	}		
	}

	Pclass c1 = p->memof;

	if (c1) {
		if (init==zero)
			;
		else {
			Pclass c2;
// error('d',"it %t %d",it,it->base);
			switch (it->base) {
			case FCT:
				c2 = Pfct(it)->memof;
				break;
			case OVERLOAD:
				c2 = Pfct(Pgen(it)->fct_list->f->tp)->memof;
				break;
			case PTR:
			case RPTR:
				c2 = Pptr(it)->memof;
				break;
			default:
				c2 = 0;
			}

			if (c2 == 0) {
				// initialization by &A::f
//error('d',"curious");
			}
			else if (c1 != c2) {
                                Nptr = 0;
				Noffset = 0;
				vcllist->clear();
				vcllist=0;
				int u1 = is_unique_base(c1,c2->string,0);
//error('d',"c1 %t c2 %t u1 %d off %d",c1,c2,u1,Noffset);
                                if (u1 && (Nptr || Noffset)) {
					// requires offset manipulation
					int bad = 0;
        				if (u1 == 1 && !Nptr) {
						if (init->base==ILIST) {
							// d = d+Noffset;
							switch (init->e1->e1->base) {
								case IVAL:
									init->e1->e1->i1 += Noffset;
									break;
								case ZERO:
									init->e1->e1 = new ival(Noffset);
									break;
								default:
									bad = 1;
								}

							// if (i<0) f = vptroffset
							switch (init->e1->e2->base) {
								case IVAL:
									if (0<init->e1->e2->i1) {
									// extern Ptype Pfct_type;
									// store vptr offset
									//  init->e2=new cast(Pfct_type,zero);
									}
									else
										break;
								default:
									bad = 1;
								}	
						} // end if (init->base==ILIST)
						else
							bad = 1;
					} // end if (u1 == 1 ...
					else
						bad = 1;

				if (bad) error('s',"%t assigned to %t (too complicated)",init->tp,p);
				} // end if (u1 && ...

                                Nptr = 0;
				Noffset = 0;
				vcllist->clear();
				vcllist=0;
				int u2 = is_unique_base(c2,c1->string,0);
//error('d',"c1 %t c2 %t u2 %d off %d",c1,c2,u2,Noffset);
                                if (u2 && (Nptr || Noffset)) {
					// requires offset manipulation
					error('s',"%t assigned to %t",init->tp,p);
				}
			} // end if (c1 != c2
		} // end else
	} // end if (c1)

	Ptype pit = p->typ;
lll:
// error('d',"p %t pit %t",p,pit);
	switch (pit->base) {
	case TYPE:
		pit = Pbase(pit)->b_name->tp;
		goto lll;
	case FCT:
		return ptof(Pfct(pit),init,tbl);
	case COBJ:
	{	Pptr r;
// error('d',"cobj: ptr %t, ref %t",it->is_ptr(),it->is_ref());
		if (r=it->is_ptr_or_ref()) {
			Pchecked = 1;
			TOK b = p->base;		// could be REF
			TOK bb = r->base;
			if (b==RPTR) p->base = PTR;
			if (bb==RPTR) r->base = PTR;
			if (p->check(r,ASSIGN)) {
				if ( cc && cc->nof && 
					Pfct(cc->nof->tp)->f_const &&
        				cc->c_this == init )
						error("%n const: assignment of%n (%t) to%t",cc->nof,init,init->tp,p);
				else
					error("no standard conversion of %t to %t",init->tp,p);
			}
			p->base = b;
			r->base = bb;
			Pexpr cp = cast_cptr(Pclass(Pbase(pit)->b_name->tp),init,tbl,0);
			if (cp != init) {
				PERM(p);	// or else it will be deleted twice!
				return new cast(p,cp);
			}
		}
		// no break
	}
	default:
		return init;
	}
}

static Pname Lcoerce, Rcoerce;
extern int suppress_error;

int try_to_demote(TOK oper, Ptype t1, Ptype t2)
/*
	look at t1 and t2 and see if there are ``demotions'' of t1 and/or t2
	so that ``t1 oper t2'' can be made legal

	return	0 is not
		1 if there is exactly one way
		>1 if there is more than one way (if in doubt return 2)
*/
{
//error('d',"try_to_demote(%k : %t : %t)",oper,t1,t2);

	Pname n1 = t1 ? t1->is_cl_obj() : 0;
	Pclass c1 = n1 ? Pclass(n1->tp) : 0;
	Pname n2 = t2 ? t2->is_cl_obj() : 0;
	Pclass c2 = n2 ? Pclass(n2->tp) : 0;

	Ptype lt = t1;
	Ptype rt = t2;

	Lcoerce = Rcoerce = 0;

//	if (oper == DOT) return 0;

	if (c1)
		switch (oper) {
		case ASSIGN:
		case ASPLUS:
		case ASMINUS:
		case ASMUL:
		case ASDIV:
		case ASMOD:
		case ASAND:
		case ASOR:
		case ASER:
		case ASLS:
		case ASRS:	// don't coerce left hand side of assignment
		//	c1 = 0;
			if (c1->memtbl->look("__as",0)) return 0;
		}
	else
		switch (oper) {
		case ADDROF:
		case INCR:
		case DECR:	// don't coerce unary requiring an lval
			return 0;
		}

	if (c1) {
//error('d',"c1 %t",c1);
		for (Pname on1 = c1->conv; on1; on1=on1->n_list) {
// error( 'd', "on1: %s tp: %k", on1->string, on1->tp->base );
			Pfct f = Pfct(on1->tp);
			lt = f->base==FCT ? f->returns :
				Pfct(Pgen(on1->tp)->fct_list->f->tp)->returns;
			Pname cn = lt->is_cl_obj();

			if (cn && (Lcoerce==0 || Lcoerce->tp->check(f,0))) {
				Pclass cl = Pclass(cn->tp);
				Pname n = cl->has_oper(oper);
				if (n == 0) continue;
		//		while (n->base==REF || n->base==MDOT) n=Pname(n->mem) ;
				Pfct nf = Pfct(n->tp);
// error( 'd', "nf: %d %k", nf->base, nf->base );
				if (nf->base == FCT) {
					if (nf->nargs==1
					&& t2
					&& (nf->argtype->tp->check(t2,ARG)==0
						|| can_coerce(nf->argtype->tp,t2)==1)
					) {
						if (Lcoerce) return 2;
						Lcoerce = on1;
					}
				}
				else {
					for (Plist gl=Pgen(nf)->fct_list; gl; gl=gl->l) {
						Pfct nf = Pfct(gl->f->tp);
						if (nf->nargs==1
						&& t2
						&& (nf->argtype->tp->check(t2,ARG)==0
							|| can_coerce(nf->argtype->tp,t2)==1)
						) {
							if (Lcoerce) return 2;
							Lcoerce = on1;
						}
					}
				}
				continue;
			}
			//if (lt->is_cl_obj()) continue;
			if (c2) {
//error('d',"c2 %t",c2);
				for (Pname on2 = c2->conv; on2; on2=on2->n_list) {
					Pfct f = Pfct(on2->tp);
					rt = f->base==FCT ? f->returns :
						Pfct(Pgen(on2->tp)->fct_list->f->tp)->returns;
					if (rt->is_cl_obj()) continue;

					suppress_error = 1;
					int r1 = lt->kind(oper,0);
					int r2 = rt->kind(oper,0);
					if (np_promote(oper,r1,r2,lt,rt,1)!=any_type) {
						Pname sn = on1;
						if (Lcoerce) {
						    Pname tn = really_dominate(
							Lcoerce, 
							on1, 
							const_obj1
						    );
				    		    if(!tn) {
							suppress_error = 0;
							return 2;
						    }
						    else sn = tn;
						}
						Lcoerce = sn;
						Rcoerce = on2;
					
					}
					suppress_error = 0;
				}
					
			}
			else if (rt) {
				suppress_error = 1;
				int r1 = lt->kind(oper,0);
				int r2 = rt->kind(oper,0);
				if (np_promote(oper,r1,r2,lt,rt,1)!=any_type) {
					Pname sn = on1;
					if (Lcoerce) {
						Pname tn = really_dominate(
							Lcoerce, 
							on1, 
							const_obj1
						);
				    		if(!tn) {
							suppress_error = 0;
							return 2;
						}
						else sn = tn;
					}
					Lcoerce = sn;
				}
				suppress_error = 0;
			}
			else {
				Pname sn = on1;
				if (Lcoerce) {
					Pname tn = really_dominate(
						Lcoerce, 
						on1, 
						const_obj1
					);
				    	if(!tn) return 2;
					else sn = tn;
				}
				Lcoerce = sn;
			}
		}
	}
	else if (c2) {
//error('d',"c2 %n",c2);
		for (Pname on = c2->conv; on; on=on->n_list) {
			Pfct f = Pfct(on->tp);
			rt = f->base==FCT ? f->returns :
				Pfct(Pgen(on->tp)->fct_list->f->tp)->returns;
			Pname cn = rt->is_cl_obj();
//error('d',"cn %n",cn);
			if (cn && (Rcoerce==0 || Rcoerce->tp->check(f,0))) {
				Pclass cl = Pclass(cn->tp);
				Pname n = cl->has_oper(oper);
				if (n == 0) continue;

		//		while (n->base==REF || n->base==MDOT) n=Pname(n->mem);
				Pfct nf = Pfct(n->tp);
				if (nf->base == FCT) {
					if (nf->nargs == 0) {
						if (Lcoerce || Rcoerce) return 2;
						Rcoerce = on;
					}
				}
				else {
					for (Plist gl=Pgen(nf)->fct_list; gl; gl=gl->l) 
						if (Pfct(gl->f->tp)->nargs == 0) {
							if (Lcoerce || Rcoerce)	return 2;
							Rcoerce = on;
						}
				}
				continue;
			}
			//if (rt->is_cl_obj()) continue;
			if( lt ) {
				suppress_error = 1;
				int r1 = lt->kind(oper,0);
				int r2 = rt->kind(oper,0);

				if (np_promote(oper,r1,r2,lt,rt,1)!=any_type) {
					Pname sn = on;
					if (Lcoerce || Rcoerce) {
						Pname tn = really_dominate(
							Rcoerce, 
							on, 
							const_obj2
						);
				    		if(!tn) {
							suppress_error = 0;
							return 2;
						}
						else sn = tn;
					}
					Rcoerce = sn;
				}
				suppress_error = 0;
			}
		}
	}

//error('d',"->%d || %d",Lcoerce,Rcoerce);
	return (Lcoerce || Rcoerce);
}

Pexpr expr::try_to_overload(Ptable tbl)
{
//	TOK bb = (base==DEREF && e2==0) ? MUL : base;
//error('d',"try_to_overload %k %d",base,base);

	Pname n1 = 0;
	Ptype t1 = 0;
	const_obj1 = 0;
	const_obj2 = 0;

	if (e1) {
		t1 = e1->tp;
		Ptype tpx = t1;
		while (tpx->base == TYPE) tpx = Pbase(tpx)->b_name->tp;
		n1 = tpx->is_cl_obj();
		const_obj1 = t1->tconst();

		Pexpr ee = e1;
                while (ee && (ee->base==DOT || ee->base==REF)) {
                       	Pexpr m = ee->mem;
                       	if ( ee->base==REF && m->tp &&  m->tp->is_ptr())
                               	break;
                       	ee = ee->e1;
                }
                if (ee) {
			int tc;
                       	Ptype ttt = ee->tp;
                       	switch (e1->base) {
                       	case REF:
                       	        Pptr p = ttt?ttt->is_ptr():0;
                               	if (p && p->typ->tconst())
                                       	const_obj1 = 1;
                               	break;
                       	case DOT:
				tc = ttt ? ttt->tconst() : 0;
                               	if (ttt && tc && (!strict_opt || tc!=2))
                                       	const_obj1 = 1;
                       	}
                }
	}

	TOK bb = base;
	switch (bb) {
	case DEREF:
		if (e2 == 0) bb = MUL;
		// no break;
	case CALL:
	case G_CALL:
		if (n1 == 0) return 0;	// ignore type of argument list
	}

	Pname n2 = 0;
	Ptype t2 = 0;

	if (e2 && e2->base!=ELIST) {
		t2 = e2->tp;
		Ptype tpx = t2;
		while (tpx->base == TYPE) tpx = Pbase(tpx)->b_name->tp;
		n2 = tpx->is_cl_obj();
		const_obj2 = t2->tconst();
		Pexpr ee = e2;
                while (ee && (ee->base==DOT || ee->base==REF)) {
                       	Pexpr m = ee->mem;
                       	if ( ee->base==REF && m->tp &&  m->tp->is_ptr())
                               	break;
                       	ee = ee->e1;
                }
                if (ee) {
			int tc;
                       	Ptype ttt = ee->tp;
                       	switch (e2->base) {
                       	case REF:
                       	        Pptr p = ttt?ttt->is_ptr():0;
                               	if (p && p->typ->tconst())
                                       	const_obj2 = 1;
                               	break;
                       	case DOT:
				tc = ttt ? ttt->tconst() : 0;
                               	if (ttt && tc && (!strict_opt || tc!=2))
                                       	const_obj2 = 1;
                       	}
                }
	}

	if (n1==0 && n2==0) return 0;
	if (n1 && n1->tp == 0) return 0;	// make_assign() fudge
// error('d',"e1: %k e2: %k", e1?e1->base:0, e2?e2->base:0 );
// error('d',"t1 %t t2 %t",t1,t2);
// error('d',"n1 %n n2 %n",n1,n2);
	/* first try for non-member function:	op(e1,e2) or op(e2) or op(e1) */
	Pexpr oe2 = e2;
	Pexpr ee2 = (e2 && e2->base!=ELIST) ? e2 = new expr(ELIST,e2,0) : 0;
	Pexpr ee1 = e1 ? new expr(ELIST,e1,e2) : ee2;
	char* obb = oper_name(bb);
	Pname gname = tbl->look(obb,0);
	// if necessary check for ambiguities
	int go = gname ? over_call(gname,ee1) : 0;
//error('d',"go %d",go);
	if (go) gname = Nover;

	if (n1) {
		if (bb == ASSIGN) {
			Pclass c1 = Pclass(n1->tp);
//error('d',"look %k %d",bb,c1->memtbl->look(obb,0));
			if (c1->memtbl->look(obb,0)==0) {
				Pclass bcl = c1->baselist?c1->baselist->bclass:0;
				if (n2==0
				|| (Pclass(n2->tp)!=c1
					&& Pclass(n2->tp)->has_base(c1)==0)) {
					// if legal, a=1 can be optimized to a.ctor(1)
						if (2 < go) goto glob;
						return 0;
					}

				if (bcl
				&& c1->obj_size!=bcl->obj_size
				&& bcl->memtbl->look(obb,0)) {
					// cannot inherit from smaller base class
				//	make_assignment(n1);
				//	return try_to_overload(tbl);
					goto mkas;
				}

				if (c1->c_xref&(C_VBASE|C_VPTR|C_ASS)) {
					// make operator=() if
					//	no base (shouldn't happen
					//	different (smaller) sized base
					//	two bases
				mkas:
					if (2 < go) goto glob;
				//	make_assignment(n1);
				//	return try_to_overload(tbl);
					return make_assignment(n1) ? try_to_overload(tbl) : 0;
				}
// error('d',"n2 %n",n2);
			//	if (n2 && Pclass(n2->tp)==c1)
				return 0;
			}
			// now take care of other assignments, 
		}

		int dbconv = 0;
		Pclass ccl = Pclass(n1->tp);
		// Pexpr mn = Pclass(n1->tp)->memtbl->look(obb,0);
		Pexpr mn = ccl->memtbl->look(obb,0);
// error('d', "tcl %d %t cl %d %t", tcl, tcl, ccl, ccl);
  		if(strcmp(obb,"__as")) {
			tcl = ccl; // ugh!!! 
			if(!mn) dbconv = 2;
		}
//		tcl = ccl; // ugh!!! 
		mn = ccl->find_name(obb,0);

		Pname mname = Pname(mn);
// error('d',"mn %n %d %k %s",mname,mn,mn?mn->base:0,obb);
		if (mname == 0) goto glob;

	zaq:
		switch (mname->base) {
		case REF:
		case MDOT:
			mname = Pname(Pexpr(mname)->mem);
			goto zaq;
		}

		int mo = over_call(mname,e2);
		if(mo && dbconv && mo >= dbconv) mo = dbconv;
//error('d',"mo %d (go %d)",mo,go);
		if (mo==0 || mo<go)
			goto glob;
		else if (mo && mo==go) {
//error('d',"t1 %t t2 %t",t1,t2);
			if (gname->tp->base == OVERLOAD) { // find right version
				for (Plist l = Pgen(gname->tp)->fct_list; l; l=l->l) {
					Pname n = l->f;
					int x = over_call(n,ee1);
					if (x == go) {
						gname = n;
						break;
					}
				}
			}
//error('d',"gname %n: %t",gname,gname->tp);
			Pname aa = Pfct(gname->tp)->argtype;
			Pptr p;
			Ptype gt1 = aa->tp;
			if (p = gt1->is_ref()) gt1 = p->typ;
			Ptype gt2 = aa->n_list->tp;
//error('d',"gt1 %t gt2 %t",gt1,gt2);
			if (mname->tp->base == OVERLOAD) { // find right version
				for (Plist l = Pgen(mname->tp)->fct_list; l; l=l->l) {
					Pname n = l->f;
					int x = over_call(n,e2);
					if (x == mo) {
						mname = n;
						break;
					}
				}
			}
//error('d',"mname %n: %t",mname,mname->tp);
			Ptype mt1 = Pfct(mname->tp)->f_this->tp;
			mt1 = Pptr(mt1)->typ;
			Ptype mt2 = Pfct(mname->tp)->argtype->tp;
//error('d',"mt1 %t mt2 %t",mt1,mt2);
			Pname mm = new name;
			Pname a1 = new name;
			a1->tp = mt1;
			Pname a2 = new name;
			a2->tp = mt2;
			a1->n_list = a2;
			mm->tp = new fct(void_type,a1,2);
			Pname gg = new name;
			Pname a3 = new name;
			a3->tp = gt1;
			Pname a4 = new name;
			a4->tp = gt2;
			a3->n_list = a4;
			gg->tp = new fct(void_type,a3,1);
extern Pname dominate(Pname,Pname,Pexpr,int,int);
			aa = dominate(gg,mm,ee1,0,1);
			delete a1;
			delete a2;
			delete a3;
			delete a4;
			delete gg->tp;
			delete mm->tp;
			if (aa == 0) {
				delete gg;
				delete mm;
				error("ambiguous operandTs%n and%t for%k",n1,t2,bb);
				tp = any_type;
				return this;
			}
			else if (aa == gg) {
				delete gg;
				delete mm;
				goto glob;
			}
			delete gg;
			delete mm;
		}
		else if (mo < 2) {	// user-defined conversion user

			if (try_to_demote(bb,t1,t2))
				error("ambiguous use of overloaded%k",bb);
		}

		base = G_CALL;				// e1.op(e2) or e1.op()
		Pname xx = new name(mname->string);	// do another lookup
							// . suppresses virtual
		e1 = new ref(DOT,e1,xx);
		if (ee1) delete ee1;
		return typ(tbl);
	}
	
	if (n2 && e1==0) {			/* look for unary operator */
		suppress_error++;
		Pexpr mn = Pclass(n2->tp)->find_name(obb,0);
		suppress_error--;
		Pname mname = Pname(mn);
		if (mname == 0) goto glob;
	zaqq:
		switch (mname->base) {
		case REF:
		case MDOT:
			mname = Pname(Pexpr(mname)->mem);
			goto zaqq;
		}	

		switch (mname->n_scope) {
		default:	goto glob;
		case 0:
		case PUBLIC:	break;		// try e2.op()
		}
		
		int mo = over_call(mname,0);
//error('d',"e2 mo %d (go %d)",mo,go);
		if (mo==0 || mo<go)
			goto glob;
		else if (mo==go) {
			error("ambiguous operandT%n for%k",n2,bb);
			tp = any_type;
			return this;
		}
		else if (mo < 2) {	// user-defined conversion user
			if (try_to_demote(bb,t1,t2))
				error("ambiguous use of overloaded%k",bb);
		}
		base = G_CALL;				// e2.op()
		Pname xx = new name(Nover->string);	// do another lookup
							// . suppresses virtual
		e1 = new ref(DOT,oe2,xx);
		e2 = 0;
		if (ee2) delete ee2;
		if (ee1 && ee1!=ee2) delete ee1;
		return typ(tbl);
	}
	
glob:
//error('d',"glob %d",go);

	if (go) {
		if (go < 2) {	// user-defined conversion necessary => binary
			if (try_to_demote(bb,t1,t2))
				error("ambiguous use of overloaded%k: %t and %t",bb,t1,t2);
		}

		base = gname->n_table == gtbl ? G_CALL : CALL;
//error('d',"gname %n %t",gname,gname->tp);
		e1 = new name(gname->string);
		// if global scope, look only for globals
		if(gname->n_table == gtbl) Pname(e1)->n_qualifier = sta_name;
		e2 = ee1;
		return typ(tbl);
	}

	if (ee2) delete ee2;
	if (ee1 && ee1!=ee2) delete ee1;
	e2 = oe2;

//error('d',"bb %d %k",bb,bb);
	switch (bb) {
	case CM:
	case G_CM:
	case G_ADDROF:
		return 0;
	case ASSIGN:
		if (n1
		&& n2
		&& (n1->tp==n2->tp || Pclass(n2->tp)->has_base(Pclass(n1->tp)))) {
			if (make_assignment(n1))
				return try_to_overload(tbl);
			else
				return 0;
		}
	case DEREF:
	case CALL:
		if (n1 == 0) break;

	default:	/* look for conversions to basic types */
		if (n1
		&& Pclass(n1->tp)->conv
		&& (bb==ANDAND || bb==OROR)) {
			e1 = check_cond(e1,bb,tbl);
			return 0;
		}

		if (n2
		&& Pclass(n2->tp)->conv
		&& (bb==ANDAND || bb==OROR || bb==NOT)) {
			e2 = check_cond(e2,bb,tbl);
			return 0;
		}

// error( 'd', "bb: %k t1: %k t2: %k", bb, t1?t1->base:0, t2?t2->base:0 );
		switch (try_to_demote(bb,t1,t2)) {
		default:
			if (Lcoerce) error("ambiguous conversion of%n",n1);
			if (Rcoerce) error("ambiguous conversion of%n",n2);
		case 0:
			break;
		case 1:
			if (Lcoerce) {
				Pname xx = new name(Lcoerce->string);
				Pref r = new ref(DOT,e1,xx);
				e1 = new expr(G_CALL,r,0);
			}

			if (Rcoerce) {
				Pname xx = new name(Rcoerce->string);
				Pref r = new ref(DOT,e2,xx);
				e2 = new expr(G_CALL,r,0);
			}
			return typ(tbl);
		}

		switch (bb) {
		case CM:
		case ADDROF:	// has legal built-in meaning
			return 0;
		}

		if (t1 && t2)
			error("bad operandTs%t%t for%k",t1,t2,bb);
		else
			error("bad operandT%t for%k",t1?t1:t2,bb);

		tp = any_type;
		return this;
	}

	return 0;
}

Pexpr cast_cptr(Pclass ccl, Pexpr ee, Ptable tbl, int real_cast)
/*
	"ee" is being cast to pointer object of class "ccl"
	if necessary modify "ee"
*/
{

//	Ptype etp = ee->tp;
// error('d',"cast_cptr %k ccl %t ee->tp %t",ee->tp->base,ccl,ee->tp);
//	if (etp->base!=PTR && etp->base!=RPTR) return ee;
	Ptype etp = ee->tp->is_ptr_or_ref();
	if (etp == 0) return ee;

	Pname on = Pptr(etp)->typ->is_cl_obj();
	if (on == 0) return ee;

	Pclass ocl = Pclass(on->tp);
	if (ocl==ccl || ccl==0 || ocl==0) return ee;

// error('d',"cast_cptr %t(%t) real %d",ccl,ocl,real_cast);
	int oo = 0;
	Pexpr r = 0;

	if (ocl->baselist
	&& (ocl->baselist->bclass!=ccl || ocl->baselist->base!=NAME)) {
		// casting derived to second or virtual base?
		Nptr = 0;
		Nvis = 0; 
		Nalloc_base = 0;
		vcllist->clear();
		vcllist=0;
		int x = is_unique_base(ocl,ccl->string,0);
		if (Nvis) {
			if (real_cast==0)
				error("cast:%n* ->B%t*; privateBC",on,ccl);
			else if (warning_opt)
				error('w',"cast:%n* ->B%t*; privateBC",on,ccl);
			real_cast = 1;	// suppress further error mesages
			Nvis = 0;
		}

		switch (x) {
		default:
			error("cast:%n* ->B%t*;%t isB more than once",on,ccl,ccl);
		case 0:		// unrelated;
			break;
		case 1:
			oo = Noffset;
			break;
		}

		if (Nptr) {	// => ee?Nptr:0
                        if (ocl->c_body==1) ocl->dcl_print(0);
                        Nptr->mem = ee; // ee->Pbase_class
			if ( Nalloc_base ) {
// error('d', "cast_cptr: nalloc_base: %s", Nalloc_base);
				Nptr->i1 = 5;
				Nptr->string4 = new char[strlen(Nalloc_base)];
				strcpy(Nptr->string4,Nalloc_base);
				Nalloc_base = 0;
			}
			else Nptr->i1 = 3;

                        if (ee->base==ADDROF || ee->base==G_ADDROF)   
                                ee = Nptr;
                        else {
				Pexpr p = new expr(QUEST,Nptr,zero);
				nin = 1;
				if (ee->not_simple()) {	// need temp
					Ptype t = ee->tp;
					Pname pp = make_tmp('N',t,tbl);
					Pname(pp)->n_assigned_to = 1;
					ee = new expr(ASSIGN,pp,ee);
					ee->tp = t;
					Nptr->mem = pp;
				}
				nin = 0;
				p->cond = ee;
				p->tp = ee->tp;
                                ee = p;
			}
		}			
	}

	if (ccl->baselist
	&& (ccl->baselist->bclass!=ocl || ccl->baselist->base!=NAME)) {
		// casting second or virtual base to derived?
		Nptr = 0;
		vcllist->clear();
		vcllist=0;
		int x = is_unique_base(ccl,ocl->string,0);
		switch (x) {
		default:
			error("cast:%n* ->derived%t*;%n isB more than once",on,ccl,on);
		case 0:		// unrelated;
			break;
		case 1:
			oo = -Noffset;
                        if (Nptr)
                                error("cast:%n* ->derived%t*;%n is virtualB",on,ccl,on);
                        break;
		}
		Nvis = 0;	// visibility no concern when converting from base to derived
	}
// error('d',"oo %d ee %k",oo,ee->base);
	if (oo) {	// => ee?ee+offset:0
                if (ee->base==ADDROF || ee->base==G_ADDROF)
                        ee = rptr(ee->tp,ee,oo);
                else {
			Pexpr p;
			nin = 1;
			if (ee->not_simple()) {	// need temp
				Ptype t = ee->base==MDOT?ee->mem->tp:ee->tp;
				Pname pp = make_tmp('M',t,tbl);
				Pname(pp)->n_assigned_to = 1;
				ee = new expr(ASSIGN,pp,ee);
				ee->tp = t;
				p = rptr(t,pp,oo);
			}
			else
				p = rptr(ee->base==MDOT?ee->mem->tp:ee->tp,ee,oo);
			nin = 0;
			Pexpr pp = new expr(QUEST,p,zero);
			pp->tp = ee->tp;
			pp->cond = ee;
                        ee = pp;
		}
	}

	Nvis = 0; // Nvis set by has_base()
	if (ocl->has_base(ccl) && Nvis) {
		if (real_cast==0)
			error("cast:%n* ->B%t*; privateBC",on,ccl);
		else if (warning_opt)
			error('w',"cast:%n* ->B%t*; privateBC",on,ccl);
		Nvis = 0;
	}

// error('d',"return %d %k %t",ee,ee->base,ee->tp);
	return ee;
}

Pexpr expr::donew(Ptable tbl)
{
	Ptype tt = tp2;
	Ptype tpx = tt;
	bit v = 0;
	bit old = new_type;
	int init = 0;	// non-constructor initialization
	new_type = 1;

	tt->dcl(tbl);
	new_type = old;
// error('d',"donew %d %d (%k) tt %t",e1,e2,e2?e2->base:0,tt);
	if (e1) e1 = e1->typ(tbl);
	if (e2) e2 = e2->typ(tbl);
ll:
//error('d',"ll %d",tt->base);
	switch (tt->base) {
	default:
		if ( e1) {
			if (v) {
				error("Ir for array created using \"new\"");
				break;
			}
			init = 1;
		} 
	//	if (e1) {
	//		error("Ir for nonCO created using \"new\"");
	//		e1 = 0;
	//	}
		break;
	case VEC:
		if (v && Pvec(tt)->dim) error("only 1st array dimension can be non-constant");
		if (Pvec(tt)->size==0 && Pvec(tt)->dim==0) error("array dimension missing in `new'");
	//	if (Pvec(tt)->dim==zero) {
	//		Pvec(tt)->size = 0;
	//		Pvec(tt)->dim = 0;
	//	}
		v++;
		tt = Pvec(tt)->typ;
		goto ll;
	case TYPE:
		tt = Pbase(tt)->b_name->tp;
		goto ll;
	case VOID:
		error("badT for `new': void");
		break;
	case COBJ:
	{	Pname cn = Pbase(tt)->b_name;
		Pclass cl = Pclass(cn->tp);
		Pname icn = 0;

		if ( e1 ) { // arguments
			if ( e1->e2 == 0 && e1->base == ELIST ) {
				e1 = e1->e1;
				e1 = e1->typ(tbl);
			}
			icn = (e1->base!=ELIST)?e1->tp->is_cl_obj():0;
		}
		//Pname icn = (e1 && e1->base!=ELIST)?e1->tp->is_cl_obj() : 0;

		Pclass icl = icn ? Pclass(icn->tp) : 0;

		if (cl->c_abstract) {
			error("`new' of abstractC%t",cl);
			break;
		}

		if (v && e1) {
			error("Ir for array ofCO created using \"new\"");
			break;
		}

		if ((cl->defined&(DEFINED|SIMPLIFIED)) == 0) {
			error("new%n;%n isU",cn,cn);
			break;
		}

		Pname ctor = cl->has_ctor();

		if (ctor) {
			if (v) {
				Pname ic;
				if ((ic = cl->has_ictor())==0) {
					error("array ofC%n that does not have aK taking noAs",cn);
					break;
				}
					
				if (Pfct(ic->tp)->nargs) {
					error("defaultAs forK for array ofC%n",cn);
					break;
				}
			}

			if (icl
			&& cl->has_itor()==0	// incomplete:
						// what if X(Y&) exists
						// for class Y : X ?
			&& (icl==cl || icl->has_base(cl))) {
				init = 1;
				break;
			}
			e1 = call_ctor(tbl,0,ctor,e1);
		}
		else if (e1) {
			if (icl==cl || icl->has_base(cl)) 
				init = 1;
			else
				error("new%n(As ); %n does not have aK",cn,cn);
		}
	}
	}

	if (init) {
		Pname tmp = make_tmp('N',tt->addrof(),tbl);
		e1 = e1->typ(tbl);
		if (tt->check(e1->tp,ASSIGN))
			error("badIrT %t for new operator (%t X)",e1->tp,tt);
		e1 = new expr(0,tmp,e1);
		tmp->assign();
	}

//	tp = (v) ? tpx : tpx->addrof();
	switch (v) {
		case 0:
			tp = tpx->addrof();
			break;
		case 1:
			tp = tpx;
			break;
		default:
			tp = tpx;
	}
//error('d',"donew(%d) -> %t",v,tp);
	return this;
}

static is_dataMemPtr( Pexpr ee ) 
/* this is utterly implementation dependent 
 * called by expr::lval to determine 
 * const objects bounds to pointers to data members 
 */
{
	Pexpr te = ee->e1;
	if ( te == 0 ) return 0;
	if ( te->base != PLUS ) return 0;
	if ( (te = te->e2) == 0 ) return 0;
	if ( te->base != MINUS ) return 0;
	if ( (te = te->e1) == 0 ) return 0;
	if ( te->base != CAST ) return 0;
	if ( (te = te->e1) == 0 ) return 0;
	if ( te->tp->base != PTR ) return 0;
        if ( Pptr(te->tp)->memof == 0 ) return 0;
	return 1;
}
