/*ident	"@(#)ctrans:src/find.c	1.1.5.23" */
/******************************************************************

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

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

find.c:

	name lookup and visibility checks

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

#include "cfront.h"

Pname undef(Pname n, Ptable tbl, TOK f)
{
	switch (f) {
	case 0:		error("%nU",n);			break;
	case CALL:	error("UF%n called",n);		break;
	case REF:
	case DOT:	error("M%nU",n);		break;
	case ADDROF:	error("address ofU%n",n);	break;
	}

	if (tbl == gtbl) {
		Pname nn = tbl->insert(n,0);
		if (f == CALL) {
			nn->tp = new fct(defa_type,0,0);
			nn->n_sto = nn->n_scope = EXTERN;
		}
		else
			nn->tp = any_type;
		delete n;
		return nn;
	}
	
	n->tp = any_type;
	return n;
}



// static int ref_seen;
static int mptr;	// &C::m
//static int mqua;	// C::m
Pname me;		// name of fct requesting access to name using find_name()
Pfct mef;		// fct requesting access to name using find_name()
Pclass tcl;		// class of original ``this''
int mex;
Pclass mec;	 	// class requesting access to name using find_name()
// int no_ambig;	// suppress ambiguity checking after virtual seen

Ptable lcl_tbl = 0;	// handle local function reference of local const for dimen size


Pexpr find_name(Pname n, Pclass cl, Ptable tbl, int f, Pname m)
/*
	in function ``m'' find the true name for "n",
	implicitly define if undefined

	f==CALL:	n()		cl == cc->cot
	f==REF:		p->n		cl == class of *p
	f==DOT:		obj.n		cl == class of obj
	f==ADDROF:	&n		cl == cc->cot
	f==0		n   		(none of the above)

	"tbl" defines local scope (block or global)

*/
{
	Pname q = n->n_qualifier;
	char* s = n->string;
	Pexpr ee;
//error('d',"find_name %s cl %t f %d m %n",s,cl,f,m);

	tcl = cl;
// 	no_ambig = 0;

	if (n) {
		mex = 1;
		if (me = m) {
			mef = Pfct(me->tp);
                        if (mef->base!=FCT) 
                                error('i',"mef %d %k",mef,mef->base);
			mec = mef->memof;
		}
		else {
			mef = 0;
			mec = cl;
		}
	}
	else
		mex = 0;

	if (n->base == MDOT) error('i',"find (mdot %n)",n);

	if (n->n_table) return n;

// ref_seen = f;
//error('d',"q %n %t f %k",q,q?q->tp:0,f);
	if (q) {			// qualified name:	q::s

		if (q == sta_name) {	// explicitly global:	::s
			Pname nn = gtbl->look(s,0);
			if (nn == 0) return undef(n,gtbl,f);
			nn->use();
			delete n;
			return nn;
		}

		if (q->tp->base != COBJ) {
			//error('d',"Qr%nnot aCN",q);
			return undef(n,gtbl,f);
		}
		Pclass qcl = Pclass(Pbase(q->tp)->b_name->tp);

		Pclass bcl = cl;

		if (cl==0 || f==ADDROF)
			bcl = cl = qcl; // Pclass(Pbase(q->tp)->b_name->tp);
		else {
		//	if (strcmp(q->string,cl->string)) {	// really a base?
		//		bcl = cl->is_base(q->string);
			if (strcmp(qcl->string,cl->string)) {	// really a base?
				bcl = cl->is_base(qcl->string);
				if (bcl == 0) {
					if (f==REF || f==DOT) {
						error("%s is not aBC of %t",q->string,cl);
						return undef(n,cl->memtbl,7);
					}
					goto sss;
				}
				// else try in base or for static
			}
		}

		if (f == ADDROF) mptr = 1;		// &C::m
	//	mqua = 1;
		ee = cl->find_name(s,bcl==cl?0:bcl);	// really a member?
		mptr = 0;
	//	mqua = 0;

		if (ee == 0) {
		sss:
//error('d',"sss %k",f);
			if (f!=REF && f!=DOT) {
				// try for static member of other class:
				Pclass qcl = Pclass(Pbase(q->tp)->b_name->tp);
				mptr = 1;
				ee = qcl->find_name(s,qcl);
				mptr = 0;
				if (ee && ee->base==NAME) {
				//	switch (ee->tp->base) {
				//	case FCT:
				//	case OVERLOAD:
				//		break;
				//	default:
						delete n;
						return ee;
				//	}
				}
			}
			error("QdN%n::%n not found in %t",q,n,cl);
			return undef(n,bcl?bcl->memtbl:cl->memtbl,7);
		}

		if (ee->base==REF && ee->e1==0) {	// &C::f, no ``this''
//error('d',"ee %k %d f %k",ee->base,ee->e1,f);
			switch (f) {
			case 0:
			case CALL:	//SSS
				{	Pexpr mm = ee->mem;
					while (mm->base==REF || mm->base==MDOT) mm = mm->mem;
					if (mm->base==NAME)
						switch (mm->tp->base) {
						case FCT:
						case OVERLOAD:
							goto addrof;
						default:
							if (Pname(mm)->n_stclass == STATIC) goto addrof;
						}
				}
				error("O orOP missing forM%n",n);
					
			case ADDROF:
			addrof:
				Pexpr x = ee;
				ee = ee->mem;
				delete x;
			case REF:
			case DOT:
				break;
			default:
				error("QdN%n::%n used in nonC context",q,n);
			}
		}

		delete n;
		return ee;
	}

	if (f!=DOT && f!=REF) {	// not .s or ->s: look for local, global, and member

		Pname nn = tbl->look(s,0);
// error('d',"nn %n f %k",nn,f);

		if (nn) {	// local, global, or member
			if (nn->n_table->t_name) {	// global or member
				if (cl) {		// might be member
					if (ee = cl->find_name(s,0)) {
			if (ee->base==REF && ee->e1==0) {
				Pexpr mm = ee->mem;
				while (mm->base==REF || mm->base==MDOT) mm = mm->mem;
				if (mm->base==NAME)
					switch (mm->tp->base) {
					default:
						if (Pname(mm)->n_stclass != STATIC)
							break;
					case FCT:
					case OVERLOAD:
						delete n;
						return mm;
					
					}
				error("O orOP missing for%n",Pref(ee)->mem);
			}
						delete n;
						return ee;
					}
				}
			} // else local

			nn->use();
			delete n;
			return nn;
		}

		if (cl) {
			if (ee = cl->find_name(s,0)) {
			if (ee->base==REF && ee->e1==0) {
				Pexpr mm = ee->mem;
				while (mm->base==REF || mm->base==MDOT) mm = mm->mem;
				if (mm->base==NAME)
					switch (mm->tp->base) {
					default:
						if (Pname(mm)->n_stclass != STATIC)
							break;
					case FCT:
					case OVERLOAD:
						delete n;
						return mm;
					
					}
				error("O orOP missing for%n",Pref(ee)->mem);
			}
				delete n;
				return ee;
			}
		}

		if ( lcl_tbl && (nn=lcl_tbl->look(s,0)) ) {
			nn->use();
			delete n;
			return nn;
		}

		if (nn = gtbl->look(s,0)) {	// global
			nn->use();
			delete n;
			return nn;
		}

		return undef(n,gtbl,f);
	}

	if (ee = cl->find_name(s,cl)) {	// .s or ->s
		delete n;
		return ee;
	}

	return undef(n,gtbl,f);
}

int classdef::check_dup(Pclass cl, TOK bb)
/*
	 see if cl is a base of this; return 0 if no clash
*/
{
	for (Pbcl b = baselist; b; b=b->next) {
		if (cl == b->bclass) {
			if (bb!=VIRTUAL) {
				if (b->base==VIRTUAL)
					error("%t inaccessible because of virtual %t in %t",cl,cl,this);
				else
					error("%t inaccessible because of %t in %t",cl,cl,this);
				return 1;
			}
			else if (b->base!=VIRTUAL) {
				error("virtual %t inaccessible because of %t in %t",cl,cl,this);
				return 1;
			}
		}
		if (b->bclass->check_dup(cl,bb)) return 1;
	}
	return 0;
}


int Nvis;
TOK Nvirt;
TOK ppbase;

Pclass classdef::is_base(char* s)
/*
	is "s" a public base class of this?
*/
{
//error('d',"%s->is_base(%s) %k",string,s,ppbase);
	TOK pp = ppbase;
	for (Pbcl b = baselist; b; b=b->next) {
/*
		if (b->ppp!=PUBLIC
		&& cc->cot!=this
		&& (cc->nof==0 || this->has_friend(Pfct(cc->nof->tp))==0))
			ppbase = b->ppp<pp?pp:b->ppp;	// PUBLIC<PROTECTED<PRIVATE
*/
		if (strcmp(s,b->bclass->string) == 0) {
		if (b->ppp!=PUBLIC
		&& cc->cot!=this
		&& (cc->nof==0 || this->has_friend(Pfct(cc->nof->tp))==0))
			ppbase = b->ppp<pp?pp:b->ppp;	// PUBLIC<PROTECTED<PRIVATE
			Nvirt = b->base;
			return b->bclass;
		}
		else {
			Pclass bc = b->bclass->is_base(s);
			if (bc) {
		if (b->ppp!=PUBLIC
		&& cc->cot!=this
		&& (cc->nof==0 || this->has_friend(Pfct(cc->nof->tp))==0))
			ppbase = b->ppp<pp?pp:b->ppp;	// PUBLIC<PROTECTED<PRIVATE
				return bc;
			}
		}
	}
	ppbase = pp;
	return 0;
}

bit classdef::has_base(Pclass cl)
/*
	is "cl" a base of this?
*/
{
	if (this == 0) return 0;
	for (Pbcl b = baselist; b; b=b->next) {
		if (b->bclass==cl) {
			if (b->ppp!=PUBLIC
			&& cc->cot!=this
			&& (cc->nof==0 || this->has_friend(Pfct(cc->nof->tp))==0))
				Nvis = 1;	// no standard coercion
			Nvirt = b->base;
			return 1;
		}
		if (b->bclass->has_base(cl)) return 1;
	}
	return 0;
}

int Noffset;
Pexpr Nptr;
clist* vcllist;

int clist::onlist(Pclass c)
{
	for (clist* p = this; p; p = p->next)
		if (p->cl == c) return 1;
	return 0;
}

void clist::clear()
{
	if (this == 0) return;
	clist* p = next;
	while (p) {
		clist* q = p->next;
		delete p;
		p = q;
	};
	delete this;
}

int is_unique_base(Pclass cl, char* s, int offset, int in_base)

//	is "s" a unique base class of this?

{
	int i = 0;
//error('d',"is_unique_base(%t,%s,%d,%d)",cl,s,offset,in_base);
	for (Pbcl b = cl->baselist; b; b=b->next) {
		int no = 0;
		if (b->base!=VIRTUAL)
			no = offset + b->obj_offset;
		else if (in_base)
			continue;
		if (strcmp(s,b->bclass->string) == 0) {
			Noffset = no;
			i++;
			if (b->ppp!=PUBLIC
			&& cc->cot!=cl
			&& (cc->nof==0 || cl->has_friend(Pfct(cc->nof->tp))==0))
				Nvis = 1;	// no standard coercion
			if (b->base==VIRTUAL)
				Nptr = new mdot(s,0);
		}
		else {
			if (b->base==VIRTUAL) {
				if (vcllist->onlist(b->bclass) )continue;
				vcllist = new clist(b->bclass,vcllist);
			}
			int ii = is_unique_base(b->bclass,s,no,1);
//error('d',"base %t i %d ii %d",b->bclass,i,ii);
			i += ii;
			if (ii==1 && b->base==VIRTUAL)
				Nptr = new mdot(b->bclass->string,0);
		}
	}

	return i;
}

/*
int classdef::has_allocated_base(Pclass bcl)

	search the list of !first base classes for this virtual base
	space will have been allocated in !first bases for virtual bases
	declared in !first classes

	in addition bcl may bave been specified explicitly as a base

{
	int off;
	for (Pbcl l = baselist; l; l=l->next) {
		if (l->base == VIRTUAL) continue; // another non-allocated virtual base
		if (l==baselist) continue;	// first base

		Pclass bc = l->bclass;
		off = l->obj_offset;

		for (Pbcl ll = bc->baselist; ll; ll=ll->next) {
			// cannot share non-virtual base
			if (ll->base != VIRTUAL) continue;
			if (ll->bclass==bcl) return off + ll->obj_offset;
		}
	}
	return 0;
}
*/

int classdef::has_allocated_base(Pclass bcl)
/*
	search the list of base classes for this virtual base
	space will be allocated in first virtual version found.
	return offset.

	A virtual base cannot have offset 0 (its pointer at least is ahead)
*/
{
//error('d',"%t->has_allocated_base(%t) ",this,bcl);
	for (Pbcl l = baselist; l; l=l->next) {
//error('d',"l %k %t %d",l->base,l->bclass,l->obj_offset);
		if (l->base==VIRTUAL
		&& l->bclass==bcl
		&& l->obj_offset)
			return l->obj_offset;

		if (l->base==VIRTUAL || l!=baselist) {
			// allocated as an object,
			// not unravelled as a set of members
			int i = l->bclass->has_allocated_base(bcl);
//error('d',"i %d",i);
			if (i) return l->obj_offset + i;
		}
	}
//error('d',"return 0");
	return 0;
}

/*
int allocated_base(Pclass cl,Pclass bcl)
{
static second;
int s2 = second;
//error('d',"allocated_base(%t,%t) %d",cl,bcl,second);
	for (Pbcl l = cl->baselist; l; l=l->next) {
//error('d',"find %t: %k %t %d",bcl,l->base,l->bclass,l->obj_offset);
//error('d',"s %d first %d",second,l!=cl->baselist);
		if (l->base==VIRTUAL
		&& l->bclass==bcl
		&& l->obj_offset
		&& (second || l!=cl->baselist)) return (second=s2,1);
		int i = allocated_base(l->bclass,bcl);
//error('d',"i %d",i);
		if (i) return (second=s2,1);
		second = 1;
	}
	second = s2;
//error('d',"s2 %d",s2);
	return 0;
}
*/
Pname vfct(Pclass cl, char* s)
/*
	Called for each name "s" in a vtbl for "cl"
	Find the "s" to go in the vtbl.
	The "s" that caused the vtbl entry to be created
		is found if nothing else is
*/
{
	Pname n = cl->memtbl->look(s,0);
	if (n) return n;

	for (Pbcl b = cl->baselist; b; b=b->next) {
		Pname nn = vfct(b->bclass,s);
		if (nn) {
//error('d',"nn %nn",nn);
			if (n && n!=nn) {
				Pclass ncl = Pclass(n->n_table->t_name->tp);
				Pclass nncl = Pclass(nn->n_table->t_name->tp);
//error('d',"ncl %t nncl %t",ncl,nncl);
				if (nncl->is_base(ncl->string))
					n = nn;		// use nn
			}
			else
				n = nn;
		}
	}

//	if (n == 0) error('i',"vfct(%t,%s)",cl,s);

	return n;
}

Pexpr rptr(Ptype t, Pexpr p, int offset)
/*
	return rvalue of offset off pointer:
	(t)((char*)p+offset)
*/
{
	Pexpr pp = p;
//error('d',"rptr %t %d",t,offset);
	if (offset) {
		Pexpr i = new ival(offset);
	//	pp = new texpr(CAST,Pchar_type,pp);
		pp = new cast(Pchar_type,pp);
		pp = new expr(PLUS,pp,i);
	}
//	pp = new texpr(CAST,t,pp);
//	pp->tp = t;
	pp = new cast(t,pp);
	return pp;
}
/*
Pexpr lptr(Ptype t, Pexpr p, int offset)

	return lvalue of offset off pointer:
	*(t*)((char*)p+offset)

{
	Pexpr pp = p;
	if (offset) {
		Pexpr i = new ival(offset);
	//	pp = new texpr(CAST,Pchar_type,pp);
		pp = new cast(Pchar_type,pp);
		pp = new expr(PLUS,pp,i);
	}
//	pp = new texpr(CAST,t->addrof(),pp);
	pp = new cast(t->addrof(),pp);
	pp = new expr(DEREF,pp,0);
	pp->tp = t;
	return pp;
}
*/
int friend_check(Pclass start,Pclass stop, Pfct f)
/*
	is `f' a friend of a class between `start' and `stop'
	in a class DAG?
*/
{
//error('d',"friend_check(%t,%t)",start,stop);
	
	if (start->has_friend(f)) return 1;
	if (stop == start) return 0;
	for (Pbcl b = start->baselist; b; b = b->next) {
		if (b->bclass->has_friend(f)) return 1;
		if (friend_check(b->bclass,stop,f)) return 1;
	}
	return 0;
}

bit classdef::has_friend(Pfct f)
/*
	does this class have function "f" as its friend?
*/
{
//error('d',"%t->has_friend(%t) %d %d ",this,f,f,f->base);

	for (Plist l=friend_list; l; l=l->l) {
		Pname fr = l->f;
		Ptype frt = fr->tp;
//error('d',"frt %t %d %d",frt,frt,frt->base);
		switch (frt->base) {
		case FCT:
			if (f == frt) return 1;
			break;
		case OVERLOAD:
			l->f = fr = Pgen(frt)->fct_list->f;	// first fct
			if (fr->tp == f) return 1;
		case CLASS:
			break;
		default:
			error('i',"bad friend %k",fr->tp->base);
		}
	}
	return 0;
}

bit classdef::has_friend(Pclass cl)
/*
	does this class have class "cl" as its friend?
*/
{
	for (Plist l=friend_list; l; l=l->l) {
		Pname fr = l->f;
		Ptype frt = fr->tp;
		switch (frt->base) {
		case CLASS:
			if (frt == cl) return 1;
		case FCT:
		case OVERLOAD:
			break;
		default:
			{error('i',"bad friend %k",frt->base);}
		}
	}
	return 0;
}

Pname find_virtual(Pclass cl, Pname s)
/*
	does ``cl'' have a virtual function ``s'' in some base class
*/
{
	for (Pbcl b = cl->baselist; b; b = b->next) {
		Pclass bcl = b->bclass;
		Pname n;
		if (n = bcl->memtbl->look(s->string,0)) {
			Pfct f = Pfct(n->tp);
			if (f->base == OVERLOAD) {
				for (Plist gl=Pgen(f)->fct_list; gl; gl=gl->l) {
					n = gl->f;
					// if (f != s->tp) continue;
					// Replaced by:
					if (n->tp->check(s->tp,VIRTUAL)) continue;
					if (Pfct(n->tp)->f_virtual) return n;
				}
			}
			// else if (f->f_virtual)
			else if (f->f_virtual && n->tp->check(s->tp,VIRTUAL)==0)
				return n;
		}
		else if (n = find_virtual(bcl,s))
			return n;
	}
	return 0;
}

Pexpr classdef::find_name(char* s, Pclass cl)
/*
	look for "s" in "this" class and its base classes
	if (cl)
		accept only a member of "cl" or "cl"'s base classes
		(that is cl::s was seen)

	complicated by hiding rules: private, protected, friend
	
*/
{
//error('d',"%s->find_name(%s,%s) memtbl %d",string,s,cl?cl->string:"",memtbl);
//	int ambig_set = 0;

	Pname n = memtbl->look(s,0);

	if (n) {
//error('d',"n %n tp %t",n,n->tp);
		if (n->tp) {
			switch (n->tp->base) {
			case OVERLOAD:
				break;
			case FCT:
				if (Pfct(n->tp)->f_virtual==0) {
					if (n->n_dcl_printed==0) n->dcl_print(0);
					break;
				}
			default:
                                 if (c_body==1 /*&& n->n_stclass!=ENUM*/) dcl_print(0);
			}
		}

		if (n->base == PUBLIC) {
			if (mex && n->n_scope==0) {
				if (this==mec
				|| (mec && has_friend(mec))
				|| (mef && has_friend(mef))
				|| (n->n_protect
					&& mec
					&& mec->has_base(this)
					&& tcl
					&& (tcl==mec || tcl->has_base(mec)))
				|| (n->n_protect
					&& mec
					&& tcl
					&& tcl->has_friend(mec))
				|| (n->n_protect
					&& mef
					&& tcl
					&& tcl->has_base(this)
				//	&& tcl->has_friend(mef))
					&& friend_check(tcl,this,mef))

//     static member can be initializer with
//     private member of its class
|| ( Cdcl 
        && Cdcl->base == NAME
        && Cdcl->n_stclass == STATIC
        && Cdcl->n_initializer
	&& Cdcl->n_qualifier
        && (Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp) == this
          || (n->n_protect && Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)->has_base(this))
          || Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)->has_friend(this)
        )))
					;
				else {
//error('d',"ee1");
					error("%n cannot access%n: %sM",me,n,n->n_protect?"protected":"private");
					mex = 0;	// suppress further error messages
				}
			}
			n = n->n_qualifier;
			mex = 0;
		}

		if (cl==0 || cl==this) {
			if (mptr==0
			&& n->n_stclass!=STATIC
			&& n->n_stclass!=ENUM) {
				Ptype t = n->tp;
//error('d',"cl %d this %d",cl,this);
//error('d',"%n: %k mef %t",n,n->n_protect,mef);
//error('d',"		this %d %t",this,this);
//error('d',"		mec  %d %t",mec,mec);
//error('d',"		tcl  %d %t",tcl,tcl);
//error('d',"mec->has_base(this) %d",mec?mec->has_base(this):0);
//error('d',"tcl->has_base(mec) %d",tcl?tcl->has_base(mec):0);
//error('d',"tcl->has_base(this) %d",tcl?tcl->has_base(this):0);
//error('d',"tcl->has_friend(mef) %d",tcl?tcl->has_friend(mef):0);
				if (mex && n->n_scope==0 && n->tp->base!=OVERLOAD) {
					if (this==mec
					|| (mec && has_friend(mec))
					|| (mef && has_friend(mef))
					|| (n->n_protect
						&& mec
						&& mec->has_base(this)
						&& tcl
						&& (tcl==mec || tcl->has_base(mec)))
					|| (n->n_protect
						&& mec
						&& tcl
						&& tcl->has_friend(mec))
					|| (n->n_protect
						&& mef
						&& tcl
						&& tcl->has_base(this)
					//	&& tcl->has_friend(mef))
						&& friend_check(tcl,this,mef))
//     static member can be initializer with
//     private member of its class
||
  (Cdcl && Cdcl->base == NAME
  && Cdcl->n_stclass == STATIC
  && Cdcl->n_initializer
  && Cdcl->n_qualifier
  && (
  Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp) == this 
  || (n->n_protect && Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)->has_base(this))
  || Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)->has_friend(this)
  )))
						;
					else {
//error('d',"ee2");
						error("%n cannot access%n: %sM",me,n,n->n_protect?"protected":"private");
						mex = 0;	// suppress further error messages
					}
				}
//error('d',"n %n %k %d",n,n->n_stclass,cc->c_this);
				Pname th = cc->c_this;
				Pexpr r = new ref(REF,th,n);
				if (th) th->use();
				n->use();
				r->tp = t;
				return r;
			}
//error('d',"mex %d",mex);
			if (mex && n->n_scope==0 && n->tp->base!=OVERLOAD) {
				if (this==mec
				|| (mec && has_friend(mec))
				|| (mef && has_friend(mef))
				|| (n->n_protect
					&& mec
					&& mec->has_base(this)
					&& tcl
					&& (tcl==mec || tcl->has_base(mec)))
				|| (n->n_protect
					&& mec
					&& tcl
					&& tcl->has_friend(mec))
				|| (n->n_protect
					&& mef
					&& tcl
					&& tcl->has_base(this)
				//	&& tcl->has_friend(mef))
					&& friend_check(tcl,this,mef))

//     static member can be initializer with
//     private member of its class
||
  (	Cdcl
	&& Cdcl->base==NAME
  	&& Cdcl->n_stclass==STATIC
  	&& Cdcl->n_initializer
	&& Cdcl->n_qualifier
  	&& (
  		Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)==this 
  		|| (n->n_protect
			&& Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)->has_base(this))
  		|| Pclass(Pbase(Cdcl->n_qualifier->tp)->b_name->tp)->has_friend(this)
  ))

)
					;
				else {
//error('d',"ee3");
					error("%n cannot access%n: %sM",me,n,n->n_protect?"protected":"private");
					mex = 0;	// suppress further error messages
				}
			}
			n->use();
			return n;
		}
	}

	if ((cl==0 || cl==this)	// not qualified to a base class
	&& csu!=UNION
	&& csu!=ANON
	&& strcmp(s,"__as")==0) { // assignment is special: you cannot inherit it
		if (baselist==0
		|| (baselist && baselist->bclass->obj_size!=obj_size)) {
			Pname cn = ktbl->look(string,0);
			if (cn->tp->base == COBJ) cn = Pbase(cn->tp)->b_name;
			Pname x = gtbl->look("__as",0);
			if (x) {	// what if there is an (illegal) global assignment operation?
					// hack hack don't declare global assignment operations!
				Pfct f = Pfct(x->tp);
				if (f->base == FCT) {
					Pptr r = f->argtype->tp->is_ref();
					if (r) {
						Pname cnn = r->typ->is_cl_obj();
						if (cnn && cn==cnn) return 0;
					}
				} else {
					for (Plist gl = Pgen(f)->fct_list; gl; gl=gl->l) {
						Pptr r = Pfct(gl->f)->argtype->tp->is_ref();
						if (r) {
							Pname cnn = r->typ->is_cl_obj();
							if (cnn &&cn==cnn) return 0;
						}
					}
				}
			}
			return make_assignment(cn) ? find_name(s,cl) : 0;
		}
	}

	return find_in_base(s, cl);
}

Pexpr classdef::find_in_base(char* s, Pclass cl)
{
	Pbcl bc = 0;
	Pexpr e = 0;
//error('d',"%s->find_in_base(%s)",string,s);
	for (Pbcl b=baselist; b; b=b->next) {
		Pclass ccl = cl==this?0:cl;

//error('d',"try %t %s",b->bclass,b->bclass?b->bclass->string:"?");
		Pexpr ee = b->bclass->find_name(s,ccl);

		if (ee) {
			if (c_body == 1) {	// look for first use (through this)
				if (b!=baselist || b->base==VIRTUAL)
					dcl_print(0);
				else {
					Pexpr ex = ee;
					while ((ex->base==MDOT && ex->i1==1)
					|| (ex->base==REF && ex->e1==cc->c_this)) ex = ex->mem;
					switch (ex->tp->base) {
					case OVERLOAD:
						break;
					case FCT:
						if (Pfct(ex->tp)->f_virtual==0) break;
					default:
						dcl_print(0);
					}
				}
			}

			if (e) {
//error( 'd', "find_in_base: b: %k e %k etp %t",b->base,e->base,e->tp);
				Pexpr ex = e;
				int evb = 0; 	// number of vbase indirections
				int eb = 0;
// note that this does not catch enum members
				while (ex->base == MDOT
				|| (ex->base==REF && ex->e1==cc->c_this)) {
					if (ex->base==MDOT) evb += int(ex->i1);
					ex = ex->mem;
				}

				Pexpr eex = ee;
				int eevb = b->base==VIRTUAL; 	// number of vbase indirections (incl. possibly this one)
				int eeb = b->base!=VIRTUAL;

				while (eex->base == MDOT
				|| (eex->base==REF && eex->e1==cc->c_this)) {
					if (eex->base==MDOT) eevb += int(eex->i1);
					eex = eex->mem;
				}

//error('d', "find_in_base: ex: %k eex: %k, eevb: %d evb: %d", ex->base, eex->base, eevb, evb );
// relying on simple counts for sub-object identification isn't good enough

				if (ex != eex)	{
//error('d',"diff %n eb %d eeb %d mqua %d",ex,eb,eeb,mqua);
				//	if (!mqua) {
						Pclass ocl = Pfct(ex->tp)->memof;
						Pclass ncl = Pfct(eex->tp)->memof;
						int eb = ocl->has_base(ncl);
						int eeb = ncl->has_base(ocl);
//error('d',"eb %d eeb %d evb %d eevb %d",eb,eeb,evb,eevb);
						if (eb==0 && eeb==0) {
							// different
							error("ambiguous%n and%n",ex,eex);
							break;
						}
						else if (eb) {	// ex dominates
							if (eevb<evb) error("ambiguous%n and%n (different sub-objects)",ex,eex);
						}
						else {	// eex dominates
							e = ee;
							bc = b;
							if (evb<eevb) error("ambiguous%n and%n (different sub-objects)",ex,eex);
						}
						if (evb==0 && eevb==0) {
							error("ambiguous%n and%n (different sub-objects)",ex,eex);
						}
				//	}
				//	else {
				//		// different
				//		error("ambiguous%n and%n",ex,eex);
				//		break;
				//	}
				}
				else if (ex->base==NAME
				&& (Pname(ex)->n_evaluated || Pname(ex)->n_sto==EXTERN)) {
//error('d',"enum");
				}
				else if (evb==0 && eevb==0) {
//error('d',"e %k",e->base);
					// no virtual base => different
					error("ambiguous%n and%n (no virtualB)",ex,eex);
					break;
				}
				else if ((evb && eevb==0) || (eevb && evb==0)) {
					// only one virtual base => different
					error("ambiguous%n and%n (one not in virtualB)",ex,eex);
					break;
				}
			}
			else {
				e = ee;
				bc = b;
			}
		}
	}

	if (e == 0) return 0;

	if (mex && bc->ppp==PRIVATE) {	// private base
		if (this==mec
		|| (mec && has_friend(mec))
		|| (mef && has_friend(mef)))
			;
		else {
			error("%n cannot access %s: %s is a privateBC",me,s,bc->bclass->string);
			mex = 0;
		}
	}

	if (e->base==NAME
	&& Pname(e)->n_stclass==STATIC) {	// static member
		Pname(e)->use();
		return e;
	}
	if (e->base != NAME)
	if (bc->base == VIRTUAL) {	// this->mem => this->Pbclass->mem
		e->mem = new mdot(bc->bclass->string,e->mem);
		e->mem->i1 = 1;
		e->mem->tp = e->mem->mem->tp;
	}
	else if (bc!=baselist) {	// not first base
		if (e->e1 == cc->c_this) {	// this->mem => this->Obcl.mem
			e->mem = new mdot(bc->bclass->string,e->mem);
			e->mem->tp = e->mem->mem->tp;
		}
		else {	// this->p->mem => this->Obcl.p->mem
			Pexpr ee = e;
			while (ee->e1->base == REF) ee = ee->e1;
			ee->mem = new mdot(bc->bclass->string,ee->mem);
			ee->mem->tp = ee->mem->mem->tp;	
		}
	}

	return e;
}

int has_virt(Pclass cl)
{
	if (cl->virt_count) return 1;
	for (Pbcl b = cl->baselist; b; b = b->next)
		if (b->bclass->virt_count || has_virt(b->bclass)) return 1;
	return 0;
}

Pname find_vptr(Pclass cl)
/*
	find virtual function table
	in memtbl or memtbl of ``first bases''
*/
{
//error('d',"find_vptr %t",cl);
	while (cl) {
		Pname vp = cl->memtbl->look("__vptr",0);
		if (vp) return vp;
		Pbcl b = cl->baselist;
		cl = 0;
		for (; b; b = b->next)
			if (b->base == NAME) {
				cl = b->bclass;
				break;
			}
	}
//error('d',"return 0");
	return 0;
}

Pname dummy_fct;
void make_dummy()
// a function with no special priviledges
{
        Pname x = new name(".." /*"__static_initializer"*/);
        x->tp = new fct(Pvoid_type,0,1);
        dummy_fct = x->dcl(gtbl,EXTERN);
//	dummy_fct->string = "";
        delete x;
}

void check_visibility(Pname n, Pname q, Pclass cl, Ptable tbl, Pname fn)
/*
	"fn" calls "n" a member function of "cl"
	fn can be zero (for functions called in arguments to static constructors) 
*/
{
//error('d',"check_visibility(%n, %t, %d, %n)",n,cl,tbl,fn);
	if (fn==0) {
                if (dummy_fct == 0) make_dummy();
                fn = dummy_fct;
	}
	Pname nn = new name;
	char* s = Pchar(n->n_tbl_list);	// overloaded name
	nn->string = s?s:n->string;
	nn->n_qualifier = q;
	Pname nx = Pname(find_name(nn,cl,tbl,REF,fn)); // nn deleted by find_name
	if (nx->tp->base != OVERLOAD) return;

	// can we get here?


	// overloaded not checked by find_name()
	// (since it looks for NAMEs not functions)

	for (Plist gl=Pgen(nx->tp)->fct_list; gl; gl=gl->l) {
		Pname nn = gl->f;
		if (n == nn) {
			if (nn->n_scope) return;	// public member

			if (fn) {
				Pclass ncl = Pclass(nn->n_table->t_name->tp);
				Pname fncn = fn->n_table->t_name;
				Pclass mec = fncn?Pclass(fncn->tp):0;
				Pfct f = Pfct(fn->tp);
				if (ncl==mec
				|| (mec && ncl->has_friend(mec))
				|| ncl->has_friend(f)
				|| (n->n_protect
					&& mec
					&& mec->has_base(ncl)
                                      )
                                     //  && (cl==mec || cl->has_base(mec)))
				|| (n->n_protect
					&& mec
					&& cl->has_friend(mec))
				|| (n->n_protect
					&& cl->has_base(ncl)
					&& cl->has_friend(f)
					&& friend_check(cl,ncl,f))
				)
					return;
			}

//error('d',"ee4");
			error("%n cannot access%n: %sM",fn,nn,nn->n_protect?"protected":"private");
			return;
		}
	}
	error('i',"visibility check failed");
}
