/*ident	"@(#)cls4:src/expr.c	1.3" */
/*******************************************************************************
 
C++ source for the C++ Language System, Release 3.0.  This product
is a new release of the original cfront developed in the computer
science research center of AT&T Bell Laboratories.

Copyright (c) 1991 AT&T and UNIX System Laboratories, Inc.
Copyright (c) 1984, 1989, 1990 AT&T.  All Rights Reserved.

THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System
Laboratories, Inc.  The copyright notice above does not evidence
any actual or intended publication of such source code.

expr.c:

	type check expressions

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

#include "cfront.h"
#include "size.h"

int const_save;
int const_ptr;
extern int no_const;
extern Ptype Pfct_type;
extern Pexpr make_dot(Pexpr, Ptable, char* c = "i");

int processing_sizeof = 0;

Pexpr expr::address()
{
// error('d',"address %k %d %s",base,base,base==NAME||base==ANAME?string:"?");
// error('d',"address e1 %d %k e2 %d %k", e1, e1?e1->base:0, e2, e2?e2->base:0); 
	switch (base) {
	case DEREF:
		if (e2 == 0) return e1;			// &*e	=> e
		break;
	case QUEST:					// &(a?b:c) => a?&b:&c
		e1 = e1->address();
		// no break;
	case G_CM:
		if ( e1 && e1->base == G_CALL &&
			e2 && e2->base == G_ADDROF)
			return this;
	case CM:
		if (e2 && e2->base == G_CALL) {  // (x,f())=>(x,(tmp=f(),&tmp))
                        Pname tmp = make_tmp('Q',e2->tp,cc->ftbl);
			e2 = init_tmp(tmp,e2,cc->ftbl);
			Pexpr aa = tmp->address();
			e2 = new expr(G_CM,e2,aa);
			e2->tp = aa->tp;
		}
		else e2 = e2->address();		// &(e1,e2) => (e1,&e2)
		tp = e2->tp;	//LLL
		return this;
	case INCR:
	case DECR:					// &(++a) => (++a,&a)
		if (e1) break;
		nin++;
		if (e2->not_simple()) error('s',"& of%k",base);
		nin--;
		e1 = new expr(base,0,e2);
		e2 = e2->address();
		base = G_CM;
		tp = e2->tp;
		return this;
	case ASSIGN:					// &(a=b) => ((a=b),&a)
	case ASPLUS:
	case ASMINUS:
	case ASMUL:
	case ASDIV:
	case ASMOD:
	case ASAND:
	case ASOR:
	case ASER:
	case ASLS:
	case ASRS:
		{
		nin++;
		if (e1->not_simple()) error('s',"& of%k",base);
		nin--;
		Pexpr a = new expr(base,e1,e2);
		a->tp = a->e1->tp;
		base = G_CM;
		e1 = a;
		e2 = a->e1->address();
		tp = e2->tp;
		return this;
		}
	case NAME:
		Pname(this)->take_addr();
//              if (Pname(this)->n_xref) return this;   // probably the right
                                                        // thing to do, but untested
		break;
	case CALL:
	case CAST:
	case G_CAST:
	case NEW:
	case GNEW:
		if (tp && tp->is_ptr_or_ref()) {	// hack?
			return this;
		}
		break;
	}

	register Pexpr ee = new expr(G_ADDROF,0,this);
	if (tp) {					// tp==0 ???
		ee->tp = tp->addrof();

		switch (tp->base) {
		case PTR:
			Pptr(ee->tp)->memof = Pptr(tp)->memof;
			break;
		case FCT:
			if (Pfct(tp)->f_static==0)
			Pptr(ee->tp)->memof = Pfct(tp)->memof;
			break;
		case OVERLOAD:
			if (Pfct(Pgen(tp)->fct_list->f->tp)->f_static==0)
			Pptr(ee->tp)->memof = Pfct(Pgen(tp)->fct_list->f->tp)->memof;
		}
	}

	return ee;
}

Pexpr make_dot(Pexpr e, Ptable tbl, char* c)
{
	if(!e->tp->memptr())
		return e;

	if(e->base==CM || e->base==G_CALL || e->base==CALL || e->base==ASSIGN) {
		Pname atmp = make_tmp('A',e->tp,tbl);
		Pexpr as = init_tmp(atmp,e,tbl);
		e = new mdot(c,atmp);
		e->i1 = 9;
		e = new expr(G_CM,as,e);
	}
	else if(e->base == ILIST) {
		if (c[0]=='i') e = e->e1->e2;
		if (c[0]=='f') e = e->e2;
	}
	else {
		e = new mdot(c,e);
		e->i1 = 9;
	}
	return e;
}

Pexpr expr::contents()
{
//error('d',"deref %k %d %t",base,base,tp);
	switch (base) {
	case ADDROF:
	case G_ADDROF:
		return e2;		// *&
	case ELIST:
//error('d',"contents of  elist");
		e1 = e1->contents();
		tp = e1->tp;
		return this;
	};

	register Pexpr ee = new expr(DEREF,this,0);
	if (tp) {			// tp==0 ???
		Ptype tt = tp->skiptypedefs();
		ee->tp = Pptr(tt)->typ;
		Pname cn = ee->tp->is_cl_obj();
		if (cn) {
			// look for first use of class
			Pclass cl = Pclass(cn->tp);
			if (cl->c_body==1) cl->dcl_print(0);	
		}
	}
	return ee;
}

static Pexpr 
make_postfix( Pexpr op, Pname fn=0 )
{
/* apply the postfix form of increment/decrement operator
 * if (fn) special call from check_postscipt to handle
 * the two operator instances declared at different scopes
 * i.e., member and non-member, therefore stored as FCTs.
 */
	Pfct f;
	Plist fl;
	Pname n = fn?fn:0; 
	Pexpr e = op->e1->base==NAME?op->e1:op->e1->mem;
	if (n) goto mk_postfix; // sorry(!!!)

	for (fl=Pgen(e->tp)->fct_list;fl; fl=fl->l) {
		n = fl->f;
		f = Pfct(n->tp);
		if ((f->nargs==1 && f->f_this) ||
		    (f->nargs==2 && f->f_this==0)) {

mk_postfix: // rewrite the call expression
			if (op->e1->base == NAME) {
				op->e1 = n;	
				op->e2->e2 = new expr(ELIST,zero,0);
			} else {
				op->e1->mem = n;  
				op->e2 = new expr(ELIST,zero,0); 
			}

			op->fct_name = n; 
			n->dcl_print(0); 
			return op;
		}
	}
	error('w',"no postfix instance of%n, although overloaded",Pname(e));
	return op; // as it were
}

static Pexpr 
check_postfix( Pexpr op )
{
/* special case: member and non-member operator++/-- instances 
 * each stored as FCT, and so oper_overload() returns prefix instance
 * need to make an explicit check if postfix instance exists
 */
	int is_glob = op->e1->base==NAME ? 1: 0;
	Pname fn = is_glob ? Pname(op->e1) : Pname(op->e1->mem);

// error('d',"check_postfix: fn: %n is_glob: %d",fn,is_glob);

	if (is_glob == 0) { // found operator++/--() as member
		Pname n = gtbl->look(fn->string,0);
		Ptype arg_tp = op->e1->e1->tp;

		if (n==0) return op;
		if (n->tp->base == FCT) { // one global instance
			Pfct ff = Pfct(n->tp); 

// error('d',"check_postfix: global n:%n arg: %t act: %t",n,ff->argtype->tp,arg_tp);
extern int exact1(Pname,Ptype); // place in cfront.h

			if (ff->nargs != 2) return op;
			if (exact1(ff->argtype, arg_tp)==0) return op;
			return make_postfix(op,n);
		}
		if (n->tp->base == OVERLOAD) { // multiple global instances
			for (Plist fl=Pgen(n->tp)->fct_list; fl; fl=fl->l) {
				Pname nn = fl->f;
				Pfct ff = Pfct(nn->tp);
				if (ff->nargs != 2) continue;
				if (exact1(ff->argtype, arg_tp)==0) continue;
				return make_postfix(op,nn);
			}
			return op;
		}
	}
	else {  // examine class table for postfix member
		Pfct f = Pfct(fn->tp);
		Pptr p = f->argtype->tp->is_ptr_or_ref();
		Ptype t = p?p->typ:f->argtype->tp;
		Pname cn = t->is_cl_obj();
		if (cn == 0) error('i',"check_postfix: %n(%n %t),CTX",fn,f->argtype,t);

		Pname nn = Pclass(cn->tp)->memtbl->look(fn->string,0);
// error('d',"check_postfix: found: %n %t", nn, nn->tp);
		if (nn) return make_postfix(op,nn);
		return op;
	}
}

int bound;
int chars_in_largest;	// no of characters in largest int

static bit 
ptr_is_template_formal(Pptr p) 
{
// error('d',"ptr_is_template_formal: %t",p);

	if (p->typ->base != TYPE) return 0;
	Pname n = p->typ->bname();
	return n->is_template_arg();
}

Ptype common_base(Pclass cl1, Pclass cl2)
/*
	do cl1 and cl2 have one common base class?
*/
{
	if (cl1 == 0 || cl2 == 0) return 0;
	if (cl1->baselist == 0 || cl2->baselist == 0) return 0;
	Pbase answer = 0;

	for (Pbcl b = cl1->baselist; b; b=b->next) {
		if (b->ppp!=PUBLIC && cc->cot!=cl1
		    && 
		    (cc->nof==0 || cl1->has_friend(Pfct(cc->nof->tp))==0) 
		    && 
		    (cc->cot==0 || cl1->has_friend(cc->cot)==0)
		)
			continue;
		Nvis = 0;
		if (cl2->has_base(b->bclass)) {
			if (Nvis) continue;
			if (answer) return 0;
			Pname bn = new name(b->bclass->string);
			bn->tp = b->bclass;
			answer = new basetype(COBJ,bn);
		}
	}
	return answer;
}

static Pexpr
do_qualifiers( Ptype t, Pexpr mem )
/*
	saw ( (t*) p )->mem
	if mem has unprocessed qualifiers, look up qualifier names
*/
{
	// qualifiers after . or -> processed after type of base object is known
	Pname m = Pname(mem);
	if ( m->base != NAME && m->base != DTOR
	||   m->n_qualifier == 0
	||   m->n_qualifier->base != MEMQ )
		return mem;
	Pname cn = t ? t->is_cl_obj() : 0;
	Pname tn = 0;
	Pname q = m->n_qualifier;
	m->n_qualifier = 0;
	if ( q->string == 0 ) { // ->:: 
		//error("scope qualification syntax");
		do {
			Pname qx = q->n_list;
			delete q;
			q = qx;
		} while ( q && q->string == 0 );
		if ( q == 0 ) { m->n_initializer = sta_name; return mem; }
		tn = k_find_name(q->string,Gtbl,HIDDEN);
	} else if ( cn )
		tn = k_find_name(q->string,Pclass(cn->tp)->k_tbl,HIDDEN);
	else
		tn = k_find_name(q->string,Ctbl,HIDDEN);

/*	// check if first qualifier is a base class of cn
	if ( cn && tn ) {
		Pclass cl = Pclass(cn->tp);
		Pname qn = tn->is_cl_obj();
		if (qn && qn->tp != cl) {	// really a base?
			Pclass qcl = Pclass(qn->tp);
			Pclass bcl = cl->is_base(qcl->string);
			if (bcl == 0 || bcl != qcl) {
				error("(%n*)->%n:: --%n is not aBC of%n",cn,qn,qn,cn);
				return mem;
			}
		}
	}
*/
	Pname cx = 0;
	for(;;) {
		if ( tn == 0 ) {
		    error("%n :: --TN%n not found",q,q);
		    return mem;
		}
		cn = tn->tp->is_cl_obj();
		if ( cn == 0 ) {
			// if at the final qualifier and mem is
			//    a dtor, mem could be a basic type dtor,
			//    in which cae the qualifier doesn't have 
			//    to be a class name 
			if ( q->n_list==0 && m->base==DTOR )
				m->n_qualifier = tn;
			else
				error("%n :: --%n is not aCN",tn,tn);
			return mem;
		}
		if ( cx ) {  // cx::cn::
			if ( Pclass(cx->tp)->has_base(Pclass(cn->tp)) )
				error("%n ::%n :: --%n not aM of%n",cx,cn,cn,cx);
		}
		cx = cn;
		{ Pname qx = q->n_list; delete q; q = qx; }
		if ( q == 0 ) break;
		if ( q->string == 0 ) { // ->X:: ::
			error("scope qualification syntax");
			return mem;
		}
		tn = k_find_member(q->string,Pclass(cn->tp),HIDDEN);
	}
	m->n_qualifier = tn;
	return mem;
}

static Pexpr
do_dtor( Ptype t, Pexpr mem)
{
	Pname m = Pname(mem);
	// m->base == DTOR
	// represents 
	//            p->something::~something 
	//            something::~something 
	//            p->~something
	// where t == type of *p or 0
	// fields of m (see dummy_dtor() in gram.y):
	//     X::~Y	m->n_qualifier==X m->n_dtag==Y
	//     int::~int	m->tp2==int_type m->tpdef==int_type
	//     X::~int	m->n_qualifier==X m->tpdef==int_type
	//     int::~Y	m->tp2==int_type m->n_dtag==Y
	// etc
	Pname q = m->n_qualifier;
	Pname d = m->n_dtag;
	Pname cn = (t ? t->is_cl_obj() : 0);
	DB(if(Edebug>=1){
		error('d',"do_dtor: t%t b%k cn%n",t,m->base,cn);
		error('d',"         q%n d%n tp2%t tpdef%t",q,d,m->tp2,m->tpdef);
		error('d',"         qtp%t dtp%t",q->tp,d->tp);
	});
	if ( cn ) { // cp->something::~something -- look up qualifier again
	    // q already looked up in do_qualifiers()
	    if ( d ) {
		Pclass cl = q ? q->tp->classtype() : Pclass(cn->tp);
		Pname dx = k_find_name(d->string,cl->k_tbl,HIDDEN);
		if (dx == 0) { error("TN%n not found in%t",d,t); dx = q; }
		d = m->n_dtag = dx;
	    }
	}
	DB(if(Edebug>=1){
		error('d',"     new q%n%t d%n%t",q,q?q->tp:0,d,d?d->tp:0);
	});
	if ( q ) {
	    if ( d && d->tp->check(q->tp,0) ) {
		error("bad syntax for destructor call:N andQr do not match");
		q = d;
	    } else if ( m->tpdef && m->tpdef->check(q->tp,0) )
		error("bad syntax for destructor call:N andQr do not match");
	    if ( d && d->tp->is_cl_obj() ) {
	    real_dtor:
		m->base = NAME;
		m->n_dtag = 0;
		m->tp = m->tpdef = m->tp2 = 0;
		m->string = oper_name(DTOR);
		m->n_oper = DTOR;
	    } else {
		if ( m->tpdef ) m->tp2 = m->tpdef;
		else m->tp2 = m->tpdef = q->tp;
		m->n_qualifier = m->n_dtag = 0;
	    }
	}
	else { // int::~something or p->~something
	    if ( d && m->tp2==0 && d->tp->is_cl_obj() ) {
		goto real_dtor;
	    }
	    if ( d && m->tp2 && d->tp->check(m->tp2,0) )
		error("bad syntax for destructor call:N andQr do not match");
	    if ( d ) { m->tp2 = m->tpdef = d->tp; m->n_dtag = 0; }
	}
	DB(if(Edebug>=1)error('d',"do_dtor: returning%k%t",mem->base,mem->tp););
	return mem;
}

#define nppromote(b)	t=np_promote(b,r1,r2,t1,t2,1)
#define npcheck(b)	(void)np_promote(b,r1,r2,t1,t2,0)

Pname Ntmp_flag_list;
bit in_quest;
con_dtor *pdlist;

Pexpr expr::typ(Ptable tbl)
/*
	find the type of "this" and place it in tp;
	return the typechecked version of the expression:
	"tbl" provides the scope for the names in "this"
*/
{
	Pname n;
	Ptype t = 0;
	Ptype t1, t2;
	TOK b = base;
	TOK r1, r2;
	if (tbl->base != TABLE) error('i',"expr::typ(%d)",tbl->base);

  	DB( if(Tdebug>=1) {
  		error('d',"%d->expr::typ(%d) %k %t",this,tbl,b,tp);
  		display_expr(this);
  	});
// error('d',"%k->typ %n tp: %t", b,b==NAME?this:0,tp);
// error('d',"     e1 %d %k e1 %d %k",e1,e1?e1->base:0,e2,e2?e2->base:0);

	if (tp) {
		switch (b) {
		case NAME:
			break;
		case MDOT:
			mem = mem->typ(tbl);
		}	;
		return this;
	}

	switch (b) {		// is it a basic type
	case MDOT:
		error('i',"mdot %s",string2);
	case DUMMY:
		error("emptyE");
		tp = any_type;
	case DTOR: // dummy type destructor
	{
		Pexpr e = do_dtor(0,this);
		return e->base==NAME ? e->typ(tbl) : e;
	}

	case ZERO:
		tp = zero_type;
		return this;

	case IVAL:
		tp = int_type;
		return this;

	case ICON:
		/*	is it long?
			explicit long?
			decimal larger than largest signed int
			octal or hexadecimal larger than largest unsigned int
		 */
	{	int ll = strlen(string);
		switch (string[ll-1]) {
		case 'l':
		case 'L':
			switch (string[ll-2]) {
			case 'u':
			case 'U':
				string[ll-2] = 0;
				tp = ulong_type;
				goto cast_n_save;
			}
		lng:
			tp = long_type;
			goto save;
		case 'u':
		case 'U':			// 1u => unsigned(1)
			switch (string[ll-2]) {
			case 'l':
			case 'L':
				string[ll-2] = 0;
		ulng:
				tp = ulong_type;
				goto cast_n_save;
			default:
				string[ll-1] = 0;
		labuint:
				tp = uint_type;
				goto cast_n_save;
			}
		}

		// no suffix - see if we can figure it out
		if  (string[0] == '0') {	// assume 8 bits in byte
			register index = 1;
			switch (string[1]) {
			case 'x':
			case 'X':
				{
				while(string[++index]=='0') ;
				ll -= index;
				int HSZ = SZ_INT+SZ_INT;
				if(ll < HSZ) goto nrm;
				if(ll == HSZ)
					if(string[2]>='8') goto labuint;
					else goto nrm;
				if(SZ_INT==SZ_LONG) break;
				HSZ = SZ_LONG+SZ_LONG;
				if(ll < HSZ) goto lng;
				if(ll == HSZ)
					if(string[2]>='8') goto ulng;
					else goto lng;
				break;
				}
			default:   // OCTAL
				{
				register IBITS = BI_IN_BYTE*SZ_INT;
				while(string[index]=='0') index++;
				register char x = string[index];
				int lbt = x=='1' ? 1 :
					( x=='2' || x=='3' ? 2 : 3 );
				int nbits = (ll-index-1)*3 + lbt;
				if(nbits < IBITS) goto nrm;
				if(nbits == IBITS) goto labuint;
				if(nbits < BI_IN_BYTE*SZ_LONG) goto lng;
				}
			}
			goto ulng;
		}
		else {  // DECIMAL
			if (ll<chars_in_largest) {
		nrm:
				tp = int_type;
				goto save;
			}
			if (ll>chars_in_largest) {
				if(SZ_INT==SZ_LONG || ll>2*chars_in_largest)
					goto ulng;
				goto lng;
			}
			// ll == chars_in_largest
			char* p = string;
			char* q = LARGEST_INT;
			do if (*p>*q) {
				if(SZ_INT==SZ_LONG) goto ulng;
				goto lng; 
			} while (*p++==*q++ && *p);
		}

		goto nrm;
	}
	case CCON:
		tp = c_strlen(string)<5 ? char_type : int_type;	// stored as 'a'
		goto save;

	case FCON:
	{	int ll = strlen(string);
		int last = string[ll-1];
		tp = double_type;
		if (last=='F' || last=='f') {
			tp = float_type;
			if (!ansi_opt) {
				string[ll-1] = 0;
				goto cast_n_save;
			}
		}
		else if (last=='L' || last=='l') {
			if (ansi_opt == 0) string[ll-1] = 0;
			tp = ldouble_type;
		}
		goto save;
	}
	case STRING:			// type of "as\tdf" is char[6]
					// c_strlen counts the terminating '\0'
	{	Pvec v = new vec(char_type,0);
		v->size = c_strlen(string);
		tp = v;
	}
	save:
		if (const_save) {	// "as\tdf" needs 7 chars for storage
			char* p = new char[strlen(string)+1];
			strcpy(p,string);
			string = p;
		}

		return this;

	cast_n_save:
		if (const_save) {	// "as\tdf" needs 7 chars for storage
			char* p = new char[strlen(string)+1];
			strcpy(p,string);
			string = p;
		}
		
		return new cast(tp,this);

	case THIS:
		delete this;
		if (cc->c_this) {
			cc->c_this->use();
			return cc->c_this;
		}
		error("``this'' used in nonC context");
		n = new name("this");
		n->tp = any_type;
		return tbl->insert(n,0); 

	case NAME:
	{	
		Pname q = Pname(this)->n_qualifier; // suppress virtual iff x::
		Pexpr ee = find_name(Pname(this),cc->cot,tbl,0,cc->nof);
		if (q && (ee->base==REF || ee->base==DOT))
			ee->n_initializer = Pexpr(q);

		if(
			cc->nof
			&&
			Pfct(cc->nof->tp)->f_const
			&&
			(ee->base==REF||ee->base==DOT)
			&&
			ee->tp
			&&
			ee->tp->skiptypedefs()->base!=FCT
			&&
			ee->tp->skiptypedefs()->base!=OVERLOAD
		)
			ee->tp = ee->tp->mkconst();

//error('d',"ee %k %t %n",ee->base,ee->tp,ee->base==NAME?ee:ee->base==REF?ee->mem:0);
		if (ee->tp->is_ref()) return ee->contents();

		if (ee->base==NAME && Pname(ee)->n_xref) {
		// fudge to handle X(X&) args
			ee = new expr(DEREF,ee,0);
			ee->tp = ee->e1->tp;	// !!
		}

		return ee;
	}

	case ADDROF:
	case G_ADDROF:	// handle lookup for &s::i
		if (e2->base == NAME) e2 = find_name(Pname(e2),cc->cot,tbl,ADDROF,cc->nof);

		if(
			cc->nof
			&&
			Pfct(cc->nof->tp)->f_const
			&&
			(e2->base==REF||e2->base==DOT)
			&&
			e2->tp
			&&
			e2->tp->skiptypedefs()->base!=FCT
			&&
			e2->tp->skiptypedefs()->base!=OVERLOAD
		)
			e2->tp = e2->tp->mkconst();

		if (e2->base==NAME && Pname(e2)->n_xref) {
			// fudge to handle X(X&) args
			e2 = new expr(DEREF,e2,0);
			e2->tp = e2->e1->tp;	// !!
		}
		if (e2->base==DOT) {	// &f().x  = > &(tmp=f(),&tmp)->x
			switch (e2->e1->base) {
			case CALL:
			case G_CALL:
				{
					Pcall c=Pcall(e2->e1);
					if (c && c->e1) {
						c->e1=c->e1->typ(tbl);
						if (c->e1->tp && Pfct(c->e1->tp)->returns->base==RPTR) break;
					}
				}
			case VALUE:
				error("& non-lvalue");
			}
		}
		break;

	case SIZEOF:
		if (tp2) {
			tp2->dcl(tbl);
			switch (tp2->base) {
			case VOID:
				error("sizeof(void)");
				break;
			case CLASS:
			{
				Pclass cl = Pclass(tp2);
				if (cl->c_body==1
				&& (cl->defined&(DEFINED|SIMPLIFIED)) == 0) 
					error('s',"class defined within sizeof");
			}
			}
	
			if (e1 && e1!=dummy) {
				e1 = e1->typ(tbl);
				DEL(e1);
				e1 = dummy;
			}
			Pptr r = tp2->is_ref();
			if (r) tp2 = r->typ;	// sizeof(T&)==sizeof(T)
		}
		else if (e1 == dummy) {
			error("sizeof emptyE");
			tp = any_type;
			return this;
		}
		else {
			++processing_sizeof;
			e1 = e1->typ(tbl);
			--processing_sizeof;
			tp2 = e1->tp;
			if(tp2->base == VEC) tp2->permanent = 1;
			if (e1->base==ILIST)	// PtoM
				e1 = dummy;
			else if (tp2 == char_type)	// sizeof ('a')
				e1 = dummy;
		}
		(void) tp2->tsizeof();
		if (tp2->base==VOID) error("sizeof(void)");
		tp = size_t_type;
		return this;

	case CAST:
	case G_CAST:
	{	Pexpr ee = docast(tbl);
		return ee->tp->is_ref() ? ee->contents() : ee;
	}

  	case PT_VALUE: 
	{
  		// need to defer setting tp2 until instantiation
  		Ptype t = pt_name->tp->skiptypedefs();
// error('d',"pt_value: %n %t",pt_name,t);
  		base = VALUE; 
  		tp2 = t;
  		// no break
	}
	case VALUE:
		//SYM obsolete table hack removed 
		return dovalue(tbl);

	case NEW:
	case GNEW:
		return donew(tbl);

	case DELETE:	// delete e1 OR delete[e2] e1
	case GDELETE:
	{	int i;
		//if (e1->base == ADDROF) error('w',"delete &E");
		e1 = e1->typ(tbl);
		if (e1->tp->skiptypedefs()->base == COBJ) {
			Pexpr x = try_to_coerce(Pvoid_type,e1,"argument",tbl);
			if (x) e1 = x;
		}
		i = e1->tp->num_ptr(DELETE);
		if (i != 'P') error("nonP deleted");
		if (e2) {
			e2 = e2->typ(tbl);
			e2->tp->integral(DELETE);
		}
		tp = void_type;
		return this;
	}

	case ILIST:	/* an ILIST is pointer to an ELIST */
		e1 = e1->typ(tbl);
		tp = any_type;
		return this;

	case ELIST:
	{	Pexpr e;
		Pexpr ex;

		if (e1 == dummy && e2==0) {
			error("emptyIrL");
			tp = any_type;
			return this;
		}
				
		for (e=this; e; e=ex) {
			Pexpr ee = e->e1;
//error('d',"e %d %d ee %d %d",e,e?e->base:0,ee,ee?ee->base:0);
			if (e->base != ELIST) error('i',"elist%k",e->base);
			if (ex = e->e2) {	/* look ahead for end of list */
				if (ee == dummy) error("EX in EL");
				if (ex->e1 == dummy && ex->e2 == 0) {
					/* { ... , } */
					DEL(ex);
					e->e2 = ex = 0;
				}
			}
			e->e1 = ee->typ(tbl);
			t = e->e1->tp;
			if (t->base == FCT) {	// yuk!
				ee = new expr(G_ADDROF,0,e->e1);
				e->e1 = ee->typ(tbl);
				t = e->e1->tp;
			}
		}
		tp = t;
		return this;
	}

	case DOT:
	case REF:
	{	
		if (e2) { // .* or ->*
			if (b == REF) b = base = REFMUL;
			if (e2->base == NAME && Pname(e2)->permanent != 1) PERM(Pname(e2));
			break;
		}

		Pbase b;
		bit bcc;

		e1 = e1->typ(tbl);
		t = e1->tp->skiptypedefs(bcc);

		if (	base==DOT && 
			e1->base==DEREF && 
			e1->e1->base==NAME && 
			is_probably_temp(Pname(e1->e1)->string)
		) 
			bcc=0;

		// check that . (->) is applied to class object (pointer)
		if (base == REF) {
			switch (t->base) {
			case COBJ:
			{	
				Pname n = t->classtype()->has_oper(REF);
				if (n) {
					n->n_used += 2;
					e1 = new call(new ref(DOT,e1,n),0);
					return typ(tbl);
				}
				// no break;
			}
			default:
				error("nonP ->%n",mem);
				t = any_type;
				// no break;
			case ANY:
				goto qqq;
			case PTR:
			case VEC:
				b = Pbase(Pptr(t)->typ->skiptypedefs(bcc));
				mem = do_qualifiers(b,mem);
				if (mem->base == DTOR) mem = do_dtor(b,mem);
			}
		}
                else {  // base == DOT
		qqq:
			mem = do_qualifiers(t,mem);
			switch (t->base) {
			default:
				if (mem->base == DTOR) mem = do_dtor(t,mem);
				if ( mem->base == DTOR ) // i.int::~int(), etc
					break;
				error("nonO .%n",mem); t = any_type;
			case ANY:
				break;
			case COBJ:
				if (mem->base == DTOR) mem = do_dtor(t,mem);
			}
//error('d',"dot %k",e1->base);
			switch (e1->base) { 
			case QUEST:
			case ASSIGN:
			case INCR:
			case DECR:
			case ASPLUS:
			case ASMINUS:
			case ASMUL:
			case ASDIV:
			case ASMOD:
			case ASAND:
			case ASOR:
			case ASER:
			case ASLS:
			case ASRS:
			case CM:
			case G_CM:
				base = REF;
				e1 = e1->address();
				break;
                        case CALL:
                        case G_CALL:
//error('d'," f().  %n mem->tp %t",e1->fct_name,mem->tp);
#ifdef FDOTRIGHT
// this rewrite is only necessary on machines with broken C compilers
// there seems to be a lot of those
// and for inlines (a,b).c
                        if (e1->fct_name && Pfct(e1->fct_name->tp)->f_inline)
#endif
                        {       // f(). => (tmp=f(),&tmp)->
                                Pname tmp = make_tmp('Q',e1->tp,tbl);
//error('d',"fdot2 %k",e1->base);
                                e1 = init_tmp(tmp,e1,tbl);
                                Pexpr aa = tmp->address();
                                e1 = new expr(G_CM,e1,aa);
                                e1->tp = aa->tp;
                                base = REF;
                                break;
                        }
                        }
			b = Pbase(t);
		}

		switch (b->base) {
		default:
			if ( mem->base == DTOR ) { // i.int::~int(), etc.
				if ( mem->tp2 && Ptype(b)->check(mem->tp2,0) )
					error("T mismatch for simpleT destructor:XO ofT%t",mem->tp2);
				return mem;
			} else 
				error("(%t) before%k%n (%n not aM)",e1->tp,base,mem,mem);
		case ANY:
			tp = any_type;
			return this;
		case COBJ:
		{	
			Pclass cl = b->classtype();
			if (cl->c_body == 1) cl->dcl_print(0);
			break;
		}
		}	

/*
	x.m is not a const even if x is a const object, this case is handled
	by lval() rejecting it
*/

		if (mem->tp) {
			tp = mem->tp;
			for (Pexpr ee = mem; ee->base==REF; ee = ee->e1) {
				if (ee->e1 == cc->c_this) { // this-> => p->
					ee->e1 = e1;
					ee->base = base;
					if(
						bcc
						&&
						mem->tp
						&&
						mem->tp->skiptypedefs()->base!=FCT
						&&
						mem->tp->skiptypedefs()->base!=OVERLOAD
					)
						mem->tp = mem->tp->mkconst();
					return mem->tp->is_ref() ? mem->contents() : mem;
				}
			}

			return tp->is_ref() ? contents() : this;
		}

		
		Pname q = Pname(mem)->n_qualifier;
		Pexpr e = find_name(Pname(mem),b->classtype(),0,base,cc->nof);
		if (q && (e->base==REF || e->base==DOT)) {
			Pname(e)->n_initializer = Pexpr(q);
//error('d',"q%n e%k%t%n",q,e->base,e->tp,e->base==NAME?e:e->base==REF?e->mem:0);	
		}

		for (Pexpr ee = e; ee->base==REF; ee = ee->e1) {
			if (ee->e1 == cc->c_this) { // this-> => p->
				ee->e1 = e1;
				ee->base = base;
				if(
					bcc
					&&
					e->tp
					&&
					e->tp->skiptypedefs()->base!=FCT
					&&
					e->tp->skiptypedefs()->base!=OVERLOAD
				)
					e->tp = e->tp->mkconst();
				break;
			}
		}

		if (e->base == NAME) {
			switch (e->tp->base) {
			case FCT:
			case OVERLOAD:
				mem = e;
				tp = e->tp;
				e = this;
			}
		}
			
			// function or static member
		return e->tp->is_ref() ? e->contents() : e;
	}

	case CALL:	/* handle undefined function names */
//error('d',"call %k %t %k",e1->base,e1->tp,e2?e2->base:0);
		if (e1->base==NAME && e1->tp==0) {
			Pname q = Pname(e1)->n_qualifier;
			e1 = find_name(Pname(e1),cc->cot,tbl,CALL,cc->nof);
//error('d',"e1 %k %t %n",e1->base,e1->tp,e1->base==NAME?e1:e1->base==REF?e1->mem:0);
			if (q && (e1->base==REF ||e1->base==DOT)) // suppress virtual call
				e1->n_initializer = Pexpr(q);
		}

		if (e1->base==NAME && Pname(e1)->n_xref) {
			// fudge to handle X(X&) args
			e1 = new expr(DEREF,e1,0);
			e1->tp = e1->e1->tp;	// !!
		}

		switch (e1->base) {
		case DOT:
		case REF:			// becomes ob.x::~x(0) or
			if (e2==0		// becomes p->x::~x(0)
			&& Pref(e1)->mem 
			&& Pref(e1)->mem->base==DTOR ) {
				Pref r = Pref(e1);
				if ( r->base == DOT ) {
					r->e1 = new expr(ADDROF,0,r->e1);
					r->base = REF;
				}
				Pname m = Pname( r->mem );
				bit qual = m->n_qualifier != 0;
				int tok = qual ? 0 : REF;
				// tok==REF => allow virtual call in call_dtor()
// error('d',"qual: %d, e1: %k tok: %d", qual, e1->base, tok );
				e1 = e1->typ(tbl);
				if ( m->base == NAME ) { // "real" dtor
					Pexpr ee = call_dtor(e1->e1,Pref(e1)->mem,0,tok,one);
					ee->tp = void_type;
					return ee;
				}
			}
		}
		break;

	case QUEST:
		Ntmp = 0;
		Ntmp_flag = 0;
		if (in_quest < 1) {
			Ntmp_refd = 0;
			Ntmp_flag_list = 0;
		}
		cond = cond->typ(tbl);
		in_quest++;
                if (Ntmp_refd) {
			Pname n = Ntmp_refd; 
			Pname nn = Ntmp_flag_list; 
			for (; n && nn; n = n->n_list, nn = nn->n_list)	{
// error('d',"pdlist: n: %n nn: %n",n,nn);
                        	con_dtor *t = new con_dtor(n,nn);
                        	if (pdlist) t->next = pdlist;
                        	pdlist = t;
			}
                        Ntmp_flag = 0;
                }
		break;

	case ANDAND:
	case OROR:
		Ntmp = 0;
		Ntmp_flag = 0;
		if (in_quest < 2) {
			Ntmp_refd = 0;
			Ntmp_flag_list = 0;
		}
	}

	if (e1) {
		e1 = e1->typ(tbl);
		if (e1->tp->is_ref()) e1 = e1->contents();
		t1 = e1->tp;
		if (Ntmp_flag)  {
                        if (b==QUEST || in_quest ) {
                                Pexpr e = new expr(ASSIGN,Ntmp_flag,one);
                                e->tp = int_type;
                                e1 = new expr(G_CM,e,e1);
                                e1->tp = t1;
                        }
                        Ntmp_flag = 0;
		}
	}
	else
		t1 = 0;

	if (e2) {
		e2 = e2->typ(tbl);
		if (e2->tp->is_ref()) e2 = e2->contents();
		t2 = e2->tp;
		if (Ntmp_flag)  {
                        if (b==QUEST || in_quest) {
                                Pexpr e = new expr(ASSIGN,Ntmp_flag,one);
                                e->tp = int_type;
				if (e2->base == ELIST) {
					Ptype t = e2->e1->tp;
                                	e2->e1 = new expr(G_CM,e,e2->e1);
					e2->e1->tp = t;
				} else {
                                	e2 = new expr(G_CM,e,e2);
                                	e2->tp = t2;
				}
                        }
                        Ntmp_flag = 0;
		}
	}
	else 
		t2 = 0;

	switch (b) {	// filter out non-overloadable operators
	default:
	{	
		Pexpr x = oper_overload(tbl);
		if (x) {
			if (x->tp != any_type
			    &&
			    (b==INCR || b==DECR)
			    &&
			    x->base != INCR && x->base != DECR
			) {
				if (t2==0 && t1) { // postfix
					Pexpr ox = x;
					Pexpr fe;
					if (x->base == DEREF
					    &&
					    x->e1->tp->is_ref()
					) 
						x = x->e1;

					fe = x->e1->base==NAME?x->e1:x->e1->mem;

					if (fe->tp->base==OVERLOAD) 
						x = make_postfix(x);
					else x = check_postfix(x);
					if (
					    (warning_opt || strict_opt)
					    &&
					    x->e2==0
					)
						error(warning_opt?'w':0,"prefix ``%k'' used for postfix call (anachronism)",b);
					if (x != ox) {
						x->tp = 0;
						x = x->typ(tbl);
					}
				}
			}	
			return x;
		}
		if (t2 && t1==0 && t2!=e2->tp) t2 = e2->tp;
	}
	case DOT:
	case G_CM:
	case G_ADDROF:
	case G_CALL:
	case QUEST:
		break;
	}

	switch (b) {
	case QUEST:
		in_quest--;
	case ANDAND:
	case OROR:
		if (Ntmp && b != QUEST ) { 
			Ntmp_dtor = new expr(ELIST,this,Ntmp_dtor);
// error('d',"(e %d) temp ofC%n with dtor needed in%kE",this,Ntmp,b);
		}

                if (b==QUEST && Ntmp_refd) {
			Pname n = Ntmp_refd; 
			Pname nn = Ntmp_flag_list; 
			for (; n && nn; n = n->n_list, nn = nn->n_list)	{
// error('d',"pdlist: n: %n nn: %n",n,nn);
                        	con_dtor *t = new con_dtor(n,nn);
                        	if (pdlist) t->next = pdlist;
                        	pdlist = t;
			}
                        Ntmp_flag = 0;
                }
	}

	t = (t1==0) ? t2 : (t2==0) ? t1 : 0;
	switch (b) {		/* are the operands of legal types */
	case REFMUL:
		base = REF;
		// no break;
	case DOT:
	{	// a .* p => &a MEMPTR p => appropriate indirection
			// to be considered: what happens if a .* expression
			// is used except in a call/=?
		Pexpr a = e1->typ(tbl);
		Ptype at = a->tp->skiptypedefs();
		if (base == DOT) {
			a = a->address();
			at = at->addrof();	// beware of n_xref
		}

		Pname cn = Pptr(at)->typ->is_cl_obj();
		Pclass mm = cn ? Pclass(cn->tp) : 0;

		Pexpr p = e2->typ(tbl);
		Ptype pt = p->tp->skiptypedefs();
		Pname pcn = pt->is_cl_obj();

//error('d',"mm %t pt %t",mm,pt);
		if (pcn) {
			Pclass cl = Pclass(pcn->tp);
			Pname found = 0;
			for (Pname on=cl->conv; on; on=on->n_list) {
				Pfct f = Pfct(on->tp);
				Pptr frt = Pptr(f->returns->skiptypedefs());
				if (frt->base!=PTR || Pptr(frt)->memof==0) break;
				if (Pptr(frt)->memof==mm || mm->has_base(Pptr(frt)->memof)) {
					if (found)
						error("two possible conversions forP toM: %n and %n",found,on);
					else
						found = on;
				}
			}
//error('d',"found %n",found);
			if (found) {
				p = new ref(DOT,p,found);
				p = new call(p,0);
				p = p->typ(tbl);
				pt = p->tp->skiptypedefs();
			}
	
		}
		if (pt->base!=PTR || Pptr(pt)->memof==0) {
			if (b==DOT)
				error("P toMX in .*E: %t",pt);
			else
				error("P toMX in ->*E: %t",pt);
			tp = any_type;
			base = DUMMY;
			return this;
		}
		Pclass pm = Pptr(pt)->memof;

		if (mm != pm && mm->same_class(pm) == 0) {
			if (mm->has_base(pm) == 0) {
				error("badOT in .*E: %t (%s*X)",a->tp,pm->string);
				tp = any_type;
			}
			a = new texpr(G_CAST,pm->this_type,a);
			e1 = a = a->typ(tbl);
		}

		Ptype tpx = Pptr(pt)->typ->skiptypedefs();

		if (tpx->base == FCT) {	// a.*p => (&a MEMPTR p)
			base = MEMPTR;
			tp2 = mm;	// keep the class for simpl.c
			e1 = a;
			e2 = p;
		}
		else {	// a .* p => *(typeof(p))((char*)&a + (int)p-1)
			a = new cast(Pchar_type,a);
			p = new cast(int_type,p);
			p = new expr(MINUS,p,one);
			p->tp = int_type;
			Pexpr pl = new expr(PLUS,a,p);
			pl->tp = Pchar_type;
			base = DEREF;
			pt = new ptr(PTR,tpx);	// need a T* not a T C::*
			Pptr(pt)->b_const = Pptr(tpx)->b_const;
			e1 = new cast(pt,pl);
			e2 = 0;
		}
		tp = tpx;
		return tp->is_ref() ? contents() : this;
	}

	case G_CALL:
	case CALL:
		tp = call_fct(tbl);	/* two calls of use() for e1's names */
		if (tp->is_ref()) return contents();
		return this;

	case DEREF:
		if (e1 == dummy) error("O missing before []\n");
		if (e2 == dummy) error("subscriptE missing");

		if (t) {	/*	*t	*/
			t = t->skiptypedefs();
			if (t->base==PTR && Pptr(t)->memof) error("P toM dereferenced");
			tp = t->deref();
		}
		else {					// e1[e2] that is *(e1+e2)
//error('d',"deref %t[%t]",t1,t2);
			if (t1->vec_type()) {		// e1[e2]
				switch (t2->base) {
				case CHAR:
				case SHORT:
				case INT:
				case LONG:
				case EOBJ:
					break;
				default:
				{	Pname cn = t2->is_cl_obj();
					if (cn)	// conversion to integral?
						e2 = check_cond(e2,DEREF,tbl);
					else
						t2->integral(DEREF);
				}
				}
				t1 = t1->skiptypedefs();
				if (t1->base==PTR && Pptr(t1)->memof) error("P toM dereferenced");
				tp = t1->deref();
				(void) tp->tsizeof();
			}
			else if (t2->vec_type()) {	// really e2[e1]
				t1->integral(DEREF);
				t2 = t2->skiptypedefs();
				if (t2->base==PTR && Pptr(t2)->memof) error("P toM dereferenced");
				tp = t2->deref();
				(void) tp->tsizeof();
			}
			else {
				error("[] applied to nonPT:%t[%t]",t1,t2);
				tp = any_type;
			}
		}

		if (tp->is_ref()) return contents();

		return this;

	case G_ADDROF:
	case ADDROF:
//error('d',"addrof(%d) %k %d",base,e2->base,e2->base);
		{
		switch (e2->base) {	// potential lvalues
		case G_CM:
			if (base==ADDROF && e2->e2->base==NAME) {
				// check for cfront generated result variable
				char* s = e2->e2->string;
				if (s[0]=='_' && s[1] && s[1]=='_') {
					if (s[2] && (s[2]=='R' || s[2]=='V')) {
						error("address of non-lvalue");
						break;
					}
				}
			}
		case CM:	// &(a,b) => (a,&b)
		{
			Pexpr ee = e2;
			ee->tp = 0;
			ee->e2 = new expr(base,0,ee->e2);
			delete this;
			return ee->typ(tbl);
		}
		case QUEST:	// & (a?b:c) => (a?&b:&c)
		{
			Pexpr ee = e2;
			ee->tp = 0;
			ee->e1 = new expr(base,0,ee->e1);
			ee->e2 = new expr(base,0,ee->e2);
			delete this;
			return ee->typ(tbl);
		}
		case INCR:
		case DECR:
			if (e2->e1) break;
		case ASSIGN:
		case ASPLUS:
		case ASMINUS:
		case ASMUL:
		case ASDIV:
		case ASMOD:
		case ASAND:
		case ASOR:
		case ASER:
		case ASLS:
		case ASRS:
			return e2->address();
		}

		int oerror_count = error_count;
		if (e2->lval(ADDROF) == 0) {
			if (error_count>oerror_count) {	// error_count == oerror_count
							// possible in case of & const
				tp = any_type;
				return this;
			}
		}

		tp = t->addrof();

		if (e2->tp->base == VEC) {
			if(e2->base != NAME) {
				base = G_CAST;
				tp2 = tp;
				e1 = e2;
				e2 = 0;
				return this;
			}
		}

		if (t->base==FCT) 
			Pptr(tp)->memof = Pfct(t)->memof;

//		if (t->tconst() && vec_const==0 && fct_const==0) Pptr(tp)->b_const = 1;
// ??? & (const T) is NOT T*const but const T*

		switch (e2->base) {
		case NAME:
		mname:					// check for &s::i
		{	Pname n2 = Pname(e2);
			Pname cn = (n2->n_table && n2->n_table!=gtbl) ? n2->n_table->t_name : 0;
// error('d',"n2 %k cn %n t %t",n2->base,cn,t);
			if (cn == 0) break;

			if (t->base==FCT && Pfct(t)->f_static) {
				Pptr(tp)->memof = 0;
				break;
			}

			switch (t->base) {
			case OVERLOAD:
				return e2;
			case FCT:
// error('d', "cn->tp: %d, %k", cn->tp, cn->tp->base);
				{
				Pclass cl = Pclass(cn->tp);
				Pptr(tp)->memof = cl; //SSS

				if (Pfct(t)->f_virtual)	{
					// { 0,vtbl index,0 }
					e1 = new ival(Pfct(t)->f_virtual);
					e1 = new expr(ELIST,zero,e1);
					e2 = zero;
					base = ILIST;
					return this;
				}		// use the pointer
				
				// { 0,-1,(int(*)())ptr }
				e1 = new ival(-1);
				e1 = new expr(ELIST,zero,e1);
				// e2 is the name
				if(
				    Pname(e2)->n_oper == ASSIGN
				    &&
				    cl->memtbl->look(e2->string,0)->tp->base
				    != OVERLOAD
				) {
				    Pname nmw = new name;
				    *nmw = *((Pname)e2);
				    e2 = new cast(Pfct_type,nmw);
				}
				else {
				    e2 = new cast(Pfct_type,e2);
				}
				base = ILIST;
				return this;
				}

			default:
				if (n2->n_stclass != STATIC) {	// offset + 1
					e1 = new ival(n2->n_offset+1);
					Pptr(tp)->memof = Pclass(cn->tp);
//error('d',"cl %d %s i %d",Pclass(cn->tp),Pclass(cn->tp)?Pclass(cn->tp)->string:"0",n2->n_offset);
				}
				else
					return this;
			}
//error('d',"int_type");
			e1->tp = int_type;
			e2 = 0;
			tp2 = tp;
			base = G_CAST;
			return this;
		}
		case DOT:
                case REF:
                {       
                        Pname m = Pname(e2->mem);
			while ( m->base == MDOT ) m = Pname(Pexpr(m)->mem);

			Pfct f = Pfct(m->tp);

			if (t->base==FCT && Pfct(t)->f_static) {
				Pptr(tp)->memof = 0;
				break;
			}

			bit all_stat = 1;
			if (f->base == OVERLOAD) {
				Plist gl = Pgen(m->tp)->fct_list;
				for(; gl; gl=gl->l) {
					if (Pfct(gl->f->tp)->f_static == 0) {
						all_stat = 0;
						break;
					}
				}
			}

			if (f->base == FCT || f->base == OVERLOAD) {  // &p->f
				Pexpr q = Pname(e2)->n_initializer;  // &p->x::f

				if (q && bound==0 && e2->e1==cc->c_this) {	
					// FUDGE: &this->x::f => &x::f
					DEL(e2);
					e2 = m;
					goto mname;
				}

				bound = 1;
				if (f->base==OVERLOAD && all_stat==0
				    ||
				    f->base==FCT && !f->f_static
				) {
					error(
					    "address of boundF (try using ``%s::*'' forPT and ``&%s::%s'' for address)",
					    m->n_table->t_name->string,
					    m->n_table->t_name->string,
					    m->string
					);
				}

				if (q || f->f_virtual==0) {
					// & x.f  =>  & f
					DEL(e2);
					e2 = m;
				}
			}
			break;

		}
		case MEMPTR:
			// &(p->*q)
			error("& .* E");
		}

		return this;
		}

	case UMINUS:
		t->numeric(UMINUS);
		tp = t;
		return this;

	case UPLUS:
		tp = t;
		if (t->num_ptr(UPLUS)=='P' || ansi_opt==0) {
			base = PLUS;
			e1 = zero;
		}
		return this;

	case NOT:
		e2 = check_cond(e2,NOT,tbl);
		tp = int_type;
		return this;

	case COMPL:
		t->integral(COMPL);
		tp = t;
		return this;

	case INCR:
	case DECR:
		{
// error('d',"incr-decr: e1: %k e2: %k", e1?e1->base:0, e2?e2->base:0);
		Pexpr e = e1?e1:e2;	// e1!=0 ==> e++ or e--
					// e1==0 ==> ++e or --e

		e->lval(b);

		switch(e->base) {
		case QUEST:
			e->tp=0;
			e->e1 = e1 ? new expr(base,e->e1,0) : new expr(base,0,e->e1);
			e->e2 = e1 ? new expr(base,e->e2,0) : new expr(base,0,e->e2);
			delete this;
			return e->typ(tbl);
		case CM:
		case G_CM:
			e->tp=0;
			e->e2 = e1 ? new expr(base,e->e2,0) : new expr(base,0,e->e2);
			delete this;
			return e->typ(tbl);
		case INCR:
		case DECR:
			if(e->e1)
				break;
			nin++;
			if(e->e2->not_simple())
				error('s',"overly complex %k of %k",b,e->base);
			nin--;
			e = new expr(G_CM,e,e->e2->typ(tbl));
			if (e1) e1=e; else e2=e;
			// e1?e1:e2 = e;
			return typ(tbl);
		case ASSIGN:
		case ASPLUS:
		case ASMINUS:
		case ASMUL:
		case ASDIV:
		case ASMOD:
		case ASAND:
		case ASOR:
		case ASER:
		case ASLS:
		case ASRS:
			nin++;
			if(e->e1->not_simple())
				error('s',"overly complex %k of %k",b,e->base);
			nin--;
			e = new expr(G_CM,e,e->e1->typ(tbl));
			if (e1) e1=e; else e2=e;
			return typ(tbl);
		}
		}

		r1 = t->num_ptr(b);
		if (r1=='P' && t->check(Pvoid_type,0)==0 &&
			ptr_is_template_formal(Pptr(t))==0) 
				error("%k of void*",b);
		tp = t;
		return this;
	
	}

	if (e1==dummy || e1==0) error("operand missing for%k",b);
	if (e2==dummy || e2==0) error("initializer missing for%k",b);

	switch (b) {
	case MUL:
	case DIV:
		r1 = t1->numeric(b);
		r2 = t2->numeric(b);
		nppromote(b);
		break;

	case PLUS:
		r2 = t2->num_ptr(PLUS);
		r1 = t1->num_ptr(PLUS);
		nppromote(PLUS);
		goto void_check;

	case MINUS:
		r2 = t2->num_ptr(MINUS);
		r1 = t1->num_ptr(MINUS);
		if (r2=='P' && r1!='P' && r1!='A') error("nonP - P");
		nppromote(MINUS);
	void_check:
// error('d',"t1: %t %k t2: %t %k", t1, t1->base, t2, t2->base);

		if ((r1=='P' && t1->check(Pvoid_type,0)==0 &&
			ptr_is_template_formal(Pptr(t1))==0) 
		||  (r2=='P' && t2->check(Pvoid_type,0)==0 &&
			ptr_is_template_formal(Pptr(t2))==0))
			error("%k of void*",b);
		tp = t;
		break;

	case LS:
	case RS:
	case AND:
	case OR:
	case ER:
		switch (e1->base) {
		case LT:
		case LE:
		case GT:
		case GE:
		case EQ:
		case NE:
			error('w',"%kE as operand for%k",e1->base,b);
		}
		switch (e2->base) {
		case LT:
		case LE:
		case GT:
		case GE:
		case EQ:
		case NE:
			error('w',"%kE as operand for%k",e2->base,b);
		}
	case MOD:
		r1 = t1->integral(b);
		r2 = t2->integral(b);
		nppromote(b);
		break;

	case LT:
	case LE:
	case GT:
	case GE:
	case EQ:
	case NE:
		r1 = t1->num_ptr(b);
		r2 = t2->num_ptr(b);
		if (r1=='P' && r2=='I') {	// allow things like:
						// if (p==2-2)
						// YUCK!
			Neval = 0;
			long i = e2->eval();
			if (Neval==0 && i==0) {
				DEL(e2);
				e2 = zero;
				r2 = 'Z';
			}
		}
		else if (r2=='P' && r1=='I') {
			Neval = 0;
			long i = e1->eval();
			if (Neval==0 && i==0) {
				DEL(e1);
				e1 = zero;
				r1 = 'Z';
			}
		}

		if (b!=EQ && b!=NE) {
			if (r1=='P' && r2=='Z') error("P%k 0",b);
			if (r2=='P' && r1=='Z') error("P%k 0",b);
		}

		// make sure functions are properly converted to pointers to
		// functions and make sure overloaded functions are rejected
		if (r1 == FCT) {
			e1 = ptof(0,e1,tbl);
			t1 = e1->tp;
			r1 = t1->num_ptr(b);
		}
		if (r2 == FCT){
			e2 = ptof(0,e2,tbl);
			t2 = e2->tp;
			r2 = t2->num_ptr(b);
		}

		npcheck(b);

		if (r1=='P') { // need cast for pointers to virtual and second bases
			Pptr p1 = t1->is_ptr();
			Pptr p2 = t2->is_ptr();
			Pname cn = p1?p1->typ->is_cl_obj():0;
			Pname cn2 = p2?p2->typ->is_cl_obj():0;

			if (cn && cn2) {
				Pclass cl = Pclass(cn->tp);
				Pclass cl2 = Pclass(cn2->tp);

				if (cl->has_base(cl2)) {
					e1 = cast_cptr(cl2,e1,tbl,base==CAST?1:0);
					e1 = new cast(p2,e1);
				}
				else if (cl2->has_base(cl)) {
					e2 = cast_cptr(cl,e2,tbl,base==CAST?1:0);
					e2 = new cast(p1,e2);
				}
			}
		}

		if (r1=='P' && t1->memptr()
		    ||
		    t1->base==FCT && Pfct(t1)->memof
		    ||
		    r2=='P' && t2->memptr()
		    ||
		    t2->base==FCT && Pfct(t2)->memof
		) {
			if (r1=='Z') {
				e1 = zero;
				if(e2->base == ILIST) e2 = e2->e1->e2;
				else e2 = make_dot(e2,tbl);
			}
			else if (r2=='Z') {
				e2 = zero;
				if(e1->base == ILIST) e1 = e1->e1->e2;
				else e1 = make_dot(e1,tbl);
			}
			else if (
			    r2=='P' && !t2->memptr()
			    || 
			    r1=='P' && !t1->memptr()
			) {
				;//error('s',"%t %k %t",t1,base,t2);
			}
			else {
				// ERROR: no check for sideeffects
				Pexpr i1;
				i1 = e1;
				while ( i1->base == G_CAST || i1->base == CAST )
				    i1 = i1->e1;
				if ( i1->base == ILIST )
				    e1 = i1;

				i1 = make_dot(i1,tbl);

				Pexpr i2;
				i2 = e2;
				while ( i2->base == G_CAST  || i2->base == CAST)
				    i2 = i2->e1;
				if ( i2->base == ILIST )
				    e2 = i2;

				i2 = make_dot(i2,tbl);

				Pexpr f1;
				if(e1->base == ILIST)
					f1 = e1->e2;
				else {
					f1 = make_dot(e1,tbl,"f");
				}

				Pexpr f2;
				if (e2->base == ILIST)
					f2 = e2->e2;
				else {
					f2 = make_dot(e2,tbl,"f");
					f2->tp = f1->tp;
				}

				Pexpr ei = new expr(base,i1,i2);
				Pexpr fi = new expr(base,f1,f2);

				base = b==NE ? OROR : ANDAND;
				e1 = ei;
				e2 = fi;
			}
		}
		t = int_type;
		break;

	case ANDAND:
	case OROR:
		e1 = check_cond(e1,b,tbl);
		e2 = check_cond(e2,b,tbl);
		t = int_type;
		break;

	case QUEST:
		{
		Pname c1, c2;
		cond = check_cond(cond,b,tbl);

		// still doesn't do complete checking for possible conversions
		bit MPTR = 0; // local hack

		suppress_error++;
		r1 = t1->num_ptr(b);
		r2 = t2->num_ptr(b);
		suppress_error--;
		if (
		    (r1=='P' && t1->memptr() || r1==FCT && Pfct(t1)->memof && Pfct(t1)->f_static==0)
    		    && 
		    (r2=='P' && t2->memptr() || r2==FCT && Pfct(t2)->memof && Pfct(t2)->f_static==0)
		) {

			++MPTR; // prevent later sorry

			if(r1==FCT) {
				e1 = new expr(G_ADDROF,0,e1);
				e1->typ(tbl);
				t1 = e1->tp;
				r1 = t1->num_ptr(b);
			}
			if(r2==FCT) {
				e2 = new expr(G_ADDROF,0,e2);
				e2->typ(tbl);
				t2 = e2->tp;
				r2 = t2->num_ptr(b);
			}
	
			// watch for casts
			Pexpr tt = e1;
			while ( tt->base == CAST || tt->base == G_CAST ) 
				tt = tt->e1;
			if ( tt->base == ILIST ) e1 = tt; 
	
			tt = e2;
			while ( tt->base == CAST || tt->base == G_CAST ) 
				tt = tt->e1;
			if ( tt->base == ILIST ) e2 = tt; 

			// cannot have sides return (expr?{}:{})
			// reuse same temp for both sides ?:
			if (e1->base == ILIST || e2->base == ILIST) {
			    Pname temp = make_tmp( 'A', mptr_type, tbl );
	
			    if (e1->base == ILIST) {
				e1 = mptr_assign( temp, e1 );
				e1 = new expr( G_CM, e1, temp );
				e1->tp = temp->tp;
			    }

			    if (e2->base == ILIST) {
				e2 = mptr_assign( temp, e2 );
				e2 = new expr( G_CM, e2, temp );
				e2->tp = temp->tp;
			    }
			}
		}

		bit redo1 = 0;
		bit redo2 = 0;
		c1 = t1->is_cl_obj();
		Pname eo1 = eobj;
		c2 = t2->is_cl_obj();
		Pname eo2 = eobj;

		if (t1 == t2
		    ||
		    c1 && c2 && c1->tp==c2->tp
		    ||
		    eo1 && eo2 && eo1->tp==eo2->tp
		)
			t = t1;
		else {
			if (c1 && c2) {
				Ptype tt;
				Pclass cl1 = Pclass(c1->tp);
				Pclass cl2 = Pclass(c2->tp);
				if (cl1==cl2 || cl2->has_base(cl1)) {
					t = t1;
					redo2=1;
					goto caca;
				}
				else if (cl1->has_base(cl2)) {
					t = t2;
					redo1=1;
					goto caca;
				}
				else if (tt = common_base(cl1,cl2)) {
					redo1 = redo2 = 1;
					t = tt;
					goto caca;
				}
			}

			r1 = t1->num_ptr(b);
			r2 = t2->num_ptr(b);

			if (r1=='P' && r2=='P') {
				Pptr p1 = t1->is_ptr();
				Pptr p2 = t2->is_ptr();
				Ptype tt;
				if ((c1 = p1->typ->is_cl_obj())
				&& (c2 = p2->typ->is_cl_obj())) {
					Pclass cl1 = Pclass(c1->tp);
					Pclass cl2 = Pclass(c2->tp);
					if (cl1==cl2 || cl2->has_base(cl1)) {
						t = t1;
						goto caca;
					}
					else if (cl1->has_base(cl2)) {
						t = t2;
						goto caca;
					}
					else if (tt = common_base(cl1,cl2)) {
						t = tt->addrof();
						goto caca;
					}
				}
			}

			if (r1==FCT && r2==FCT) {
				if (t1->check(t2,ASSIGN))
					error("badTs in ?:E: %t and %t",t1,t2);
				t = t1;
			}
			else
				nppromote(b);
		caca:
			Pptr pt = t->is_ptr();
			bit t_ptr = pt && pt->base == PTR;
			Pname cn = !t_ptr ? 0 : pt->typ->is_cl_obj();

			if (t!=t1 && t->check(t1,0)) {
				PERM(t);
				if (redo1) {
					e1 = e1->address();
					e1 = new cast(t->addrof(),e1);
					e1 = e1->docast(tbl);
					e1 = new expr(DEREF,e1,0);
				}
				else  {
					e1 = new cast(t,e1);
					if (cn) e1->e1=cast_cptr(Pclass(cn->tp),e1->e1,tbl,e1->base==CAST?1:0);
				}
			}

			if (t!=t2 && t->check(t2,0)) {
				PERM(t);
				if (redo2) {
					e2 = e2->address();
					e2 = new cast(t->addrof(),e2);
					e2 = e2->docast(tbl);
					e2 = new expr(DEREF,e2,0);
				}
				else {
					e2 = new cast(t,e2);
					if (cn) e2->e1=cast_cptr(Pclass(cn->tp),e2->e1,tbl,e2->base==CAST?1:0);
				}
			}

			if (t_ptr && pt->memof && MPTR == 0)
				error('s',"conditionalE with%t",t);
		}
		}
		break;

	case ASPLUS:
		r1 = t1->num_ptr(ASPLUS);
		r2 = t2->num_ptr(ASPLUS);
		nppromote(ASPLUS);
		goto ass;

	case ASMINUS:
		r1 = t1->num_ptr(ASMINUS);
		r2 = t2->num_ptr(ASMINUS);
		if (r2=='P' && r1!='P' && r1!='A') error("P -= nonP");
		nppromote(ASMINUS);
		goto ass;

	case ASMUL:
	case ASDIV:
		r1 = t1->numeric(b);
		r2 = t2->numeric(b);
		nppromote(b);
		goto ass;

	case ASMOD:
		r1 = t1->integral(ASMOD);
		r2 = t2->integral(ASMOD);
		nppromote(ASMOD);
		goto ass;

	case ASAND:
	case ASOR:
	case ASER:
	case ASLS:
	case ASRS:
		r1 = t1->integral(b);
		r2 = t2->integral(b);
		npcheck(b);
		t = int_type;
		goto ass;
	ass:
		if (r1=='P' && t1->check(Pvoid_type,0)==0 &&
			ptr_is_template_formal(Pptr(t1))==0) 
		 		error("%k of void*",b);
		tp = t;
		as_type = t;	/* the type of the rhs */
		t2 = t;

	case ASSIGN:
//error('d',"assign %k: %t %t %s",b,t1,t2,ignore_const?"ignore const":"");
//error('d',"    e1 %d %k e1 %d %k",e1,e1->base,e2,e2->base);
		if (ignore_const) {	// handle static initializers
					// represented as assignments
					// ignore consts.
			tp = e1->tp;
			return this;
		}

		switch (e1->base) {
		case G_CM:
		case CM:	// (a,b)=c => *(a,&b)=c
		{
			e1->e2 = new expr(G_ADDROF,0,e1->e2);
			e1->tp = 0;
			e1 = new expr(DEREF,e1,0);
			return typ(tbl);
		}
		case QUEST:	// (a?b:c)=d => *(a?&b:&c)=c
		{
			e1->e1 = new expr(G_ADDROF,0,e1->e1);
			e1->e2 = new expr(G_ADDROF,0,e1->e2);
			e1->tp = 0;
			e1 = new expr(DEREF,e1,0);
			return typ(tbl);
		}
		case ASSIGN:	// (a*=b)=c => a*=b,a=c
		case ASPLUS:
		case ASMINUS:
		case ASMUL:
		case ASDIV:
		case ASMOD:
		case ASAND:
		case ASOR:
		case ASER:
		case ASLS:
		case ASRS:
		{
			base = G_CM;
			nin++;
			if (e1->e1->not_simple()) error('s',"lvalue %k too complicated",b);
			nin--;
			Pexpr aa = new expr(e1->base,e1->e1,e1->e2);
			Pexpr bb = new expr(b,e1->e1,e2);
			e1 = aa;
			e2 = bb;
			return typ(tbl);
		}
		case INCR:
		case DECR:	// ++a=b => ++a,a=b
		{
			if(!e1->e2) break ;
			base = G_CM;
			nin++;
			if (e1->e2->not_simple()) error('s',"lvalue %k too complicated",b);
			nin--;
			Pexpr aa = new expr(e1->base,0,e1->e2);
			Pexpr bb = new expr(b,e1->e2,e2);
			e1 = aa;
			e2 = bb;
			return typ(tbl);
		}
		case REF:
		{
			Pexpr r = e1;
			// hack to prevent
			//	f().i = j
			// transformed into
			//	((t=f()),&t)->i = j
			if (r->e1->base==G_CM
			&& r->e1->e2->base==G_ADDROF
			&& r->e1->e2->e2->base==NAME) {
				char* s = r->e1->e2->e2->string;
				if (s[0]=='_' && s[1]=='_')
					error("left hand side not lvalue");
			}
		}
		}

		if (e1->lval(b) == 0) {
			tp = any_type;
			return this;
		}

		t1 = t1->skiptypedefs();
		switch (t1->base) {
		case INT:
		case CHAR:
		case SHORT:
		{	Ptype t = e2->tp->skiptypedefs();
			switch (t->base) {
			case LONG:
			case FLOAT:
			case DOUBLE:
			case LDOUBLE:
				error('w',"%t assigned to %t",e2->tp,t1);
			}
		}
			// no break
		case LONG:
			if (b==ASSIGN
			&& Pbase(t1)->b_unsigned
			&& e2->base==UMINUS
			&& e2->e2->base==ICON)
				error('w',"negative assigned to unsigned");
			break;
		case PTR:
			if (b == ASSIGN) {
				e2 = ptr_init(Pptr(t1),e2,tbl);
				t2 = e2->tp;
//error('d',"pchecked %d",Pchecked);
				if (Pchecked) {
					tp = e1->tp;
					return this;
				}
			}
			break;
   		case COBJ:
		{	Pname c1 = t1->is_cl_obj();
				// test of c1->tp necessary for ``fake classes''
				// _Sdd generated for vector assignemnts
			if (c1
			&& c1->tp
		/*	&& Pclass(c1->tp)->memtbl->look("__as",0)==0*/) {
				Pname c2 = t2->is_cl_obj();
// error('d', "expr::typ: c1: %n c2: %n", c1, c2 );
				if (c1 != c2) {
/*
	consider:

	struct A { A(B&); };
	struct B : A {};

	A aa;
	B bb;

	aa = bb;	// aa.operator=(A(bb));
			// optimize to aa.A(bb) when possible
			// avoid temporary where aa = *(A*)&bb is legal
*/
// error('d',"expr::typ c1 %n %d c2 %n %d",c1,c1?c1->tp:0,c2,c2?c2->tp:0);
					if (c2
					&& c2->tp
					&& can_coerce(t1,t2)==0
					&& (vcllist->clear(),vcllist=0,1<is_unique_base(Pclass(c2->tp),c1->string,0))
					&& Pclass(c1->tp)->c_xref&(C_VBASE|C_VPTR|C_ASS)) {
// error('d',"aaa");
						if (make_assignment(c1)) return oper_overload(tbl);
					}
				// optimize
					else {
						e2 = new expr(ELIST,e2,0);
						e2 = new texpr(VALUE,t1,e2);
						if (Pclass(c1->tp)->has_dtor()==0 &&
							Pclass(c1->tp)->has_oper(ASSIGN)==0) {
				// optimize
// error('d',"bbb");
							e2->e2 = e1;
							e2 = e2->typ(tbl);
                                                	if (e2->base==DEREF && e2->e1->base==G_CALL ||
                                                    	e2->base==ASSIGN && e2->e1==e1) {
// error('d',"ccc");
                                                        	*this = *e2;
							}
							tp = t1;
							return this;
						}
						return typ(tbl);
					}
				}
					// test of c1->tp necessary for ``fake classes''
					// _Sdd generated for vector assignemnts
				else if (c1->tp && Pclass(c1->tp)->c_xref&(C_VBASE|C_VPTR|C_ASS)) {
					if (make_assignment(c1)) return oper_overload(tbl);
				}
			}
			(void) t1->tsizeof();
			break;
		}
		}

//error('d',"check(%t,%t) -> %d",e1->tp,t2,try_to_coerce(t1,e2,"assignment",tbl));
		{	
			Pexpr x = try_to_coerce(t1,e2,"assignment",tbl);

			if (t1->base==COBJ && t1->classtype() && t1->classtype()->has_const_mem())
				error("cannot assign to an object of class %t with const member(s)",t1->classtype());

			if (x)
				e2 = x;
			else if (t2->is_or_pts_to(OVERLOAD))
				error("bad assignmentT:%t =&overloaded function",e1->tp);
			else if (e1->tp->check(t2,ASSIGN)) {
				error('e',"bad assignmentT:%t =%t",e1->tp,t2);
				if (no_const) 
					error('c'," (no usable const conversion)\n");
				else error('c',"\n");
			}
			else if ((t1 = t1->is_ptr()) && t1->memptr()) {
				if (t2 == zero_type) {
					Pexpr ee = new expr(ELIST,zero,zero);
					e2 = new expr(ILIST,ee,zero);
				}
				else if (t2->base==PTR && t2->memptr()) {
					// do nothing: structure assignment
				}
				else {
					Pexpr x = ptr_init(Pptr(t1),e2,tbl);
					if (x != e2) e2 = x;
				}
			}
		}		
		t = e1->tp;	// the type of the lhs
		break;
	case CM:
	case G_CM:
		// cannot have sides return ({},{})
		if (t1->base == FCT) {
			e1 = new expr(G_ADDROF,0,e1);
			e1->typ(tbl);
			t1 = e1->tp;
		}
		if (t2->base == FCT){
			e2 = new expr(G_ADDROF,0,e2);
			e2->typ(tbl);
			t2 = e2->tp;
		}
		if (e1->base == ILIST) {
			Pname temp1 = make_tmp( 'A', mptr_type, tbl );
			e1 = mptr_assign( temp1, e1 );
			e1 = new expr( G_CM, e1, temp1 );
			e1->tp = temp1->tp;
		}

		if (e2->base == ILIST) {
			Pname temp2 = make_tmp( 'A', mptr_type, tbl );
			e2 = mptr_assign( temp2, e2 );
			e2 = new expr( G_CM, e2, temp2 );
			e2->tp = temp2->tp;
			//t2 = e2->tp;
		}

		t = t2;
		break;
	default:
		error('i',"unknown operator%k",b);
	}

	tp = t;
	return this;
}

bit 
expr::is_const_obj()
{
	Pexpr ee = this;
	if(ee == 0) return 0;

	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 (base) {
		case REF:
			{
			Pptr p = ttt?ttt->is_ptr():0;
			if (p && p->typ->tconst())
				return 1;
                               	break;
			}
		case DOT:
			tc = ttt ? ttt->tconst() : 0;
			if (ttt && tc && (!strict_opt || tc!=2))
				return 1;
		}
	}
	return 0;
}
