/*ident	"@(#)cls4:src/expand.c	1.4" */
/*******************************************************************************
 
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.

expand.c:

	expand inline functions

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

#include "cfront.h"
#include <malloc.h>
static Ptable Scope;
// extern void display_expr(Pexpr);

static char* temp(char* vn, Pname fn)
//
//	make the name of the temporary: __Xvn00xxxxxx
//	xxxxxx is a representation of fn's address -
//	anything unique will do.  we use radix 32 here
//	add two bytes to make the name sensitive to the scope
//	to avoid re-using temps when an inline is expanded in
//	more than one other inline that is then called in the
//	same expression
//
{
	if (vn[0]!='_' || vn[1]!='_' || vn[2]!='X') {
		unsigned long a = (unsigned long) fn->tp;

		// al = the number of radix-32 chars in a
		unsigned long aa = a;
		int al = 0;
		while (aa) {
			al++;
			aa >>= 5;
		}

		// allocate memory for the result
		int ll = strlen(vn);
		char* s = new char[ll+al+8];
		register char* p = s;

		// append _ _ X vn
		*p++ = '_';
		*p++ = '_';
		*p++ = 'X';
		strcpy(p,vn);
		p += ll;
		*p++ = '0';
		*p++ = '0';

		// append representation of `a'
		while (a) {
			*p++ = "abcdefghijklmnopqrstuvwxyz012345"[a&037];
			a >>= 5;
		}

	        // append scope representation and trailing null
	        *p++ = "abcdefghijlkmnop" [ ((int(Scope)>>4)&15) ];
	        *p++ = "abcdefghijlkmnop" [ ((int(Scope)>>8)&15) ];
		*p = '\0';
//tmp error('d',"temp(%s,%n) -- scope %d -- returning %s",vn,fn,scope,s);
		return s;
	}
	else {
//tmp error('d',"temp(%s,%n) -- scope %d -- returning %s",vn,fn,scope,vn);
		return vn;
	}

}

Plist inllist; // keep track of ctors defined inline so dtors won't be called if succeeding goto

Pname dcl_local(Ptable scope, Pname an, Pname fn, bit tempflag = 0)
{
	if (scope == 0 || scope->base == 0) {
		if (sti_tbl == 0) sti_tbl = new table(8,gtbl,0);
		scope = sti_tbl;
	//	error('s',"cannot expand inlineF needing temporaryV in nonF context");
	//	return an;
	}

	if (an->n_stclass == STATIC) {
		if (an->tp->base!=FCT && an->tp->base!=OVERLOAD) error('s',&fn->where,"cannot expand inlineF with static%n",an);
		return an;
	}

	Pname cn = fn->n_table->t_name;
	char *s;
	if (tempflag)
		s = make_name('X');
	else s = temp(an->string,fn);
	Pname nx = new name(s);
	Ptype atp = an->tp;
//error('d',"dcl_local(%d,%n,%n) -> %s",scope,an,fn,s);
	if (ansi_opt)
		nx->n_initializer=an->n_initializer;
	while (atp->base == TYPE) atp = Pbase(atp)->b_name->tp;
	if (atp->base == VEC) {
		if (an->n_scope == ARG) {
			Pptr t = new ptr(PTR,Pvec(atp)->typ);
			nx->tp = t;
		}
		else {
			error('s',&fn->where,"cannot expand inlineF needing temporaryV of arrayT");
			return an;
		}
	}
	else
		nx->tp = atp;
	nx->tp->ansi_const=1;
	PERM(nx->tp);

	nx->n_used = an->n_used;
	nx->n_assigned_to = an->n_assigned_to;
	nx->n_addr_taken = an->n_addr_taken;
	nx->n_xref = an->n_xref;
//error('d',"nx %n %t,",nx,nx->tp);
	Pname r = scope->look(nx->string,0);
	if (r) {
//error('d',"r%n rtp%t nxtp%t", r, r->tp,nx->tp);
		if (r->tp->check(nx->tp,0))
			error('s',&fn->where,"cannot expand inlineF %a with two local variables with the sameN (%s)",fn,an->string);
		else
			r = 0;
	}
	if (r==0) {
		r = scope->insert(nx,0);
//error('d',"%d %d %d %d",r->n_stclass,an->n_stclass,r->lex_level,an->lex_level);

		if (r->tp->base==COBJ) 
			inllist=new name_list(r,inllist); 

		r->n_stclass = an->n_stclass;
		r->lex_level = an->lex_level;
		delete nx;
		r->where.line = 0;
	}
	return r;
}

int ck_cast(Ptype t1, Ptype t2)
/*
	return a value of type t2 from a function returning a t1
	return 1 if cast is needed
*/
{
st:
	while (t1->base == TYPE) t1 = Pbase(t1)->b_name->tp;
	while (t2->base == TYPE) t2 = Pbase(t2)->b_name->tp;

	if (t1 == t2) return 0;

	if (t1->base != t2->base) return 1;

	switch (t1->base) {
	case RPTR:
	case PTR:
	{	t1 = Pptr(t1)->typ;
		if (t1->base == VOID) return 1;
		t2 = Pptr(t2)->typ;
		goto st;
	}
	case CHAR:
	case SHORT:
	case INT:
	case LONG:
		if (Pbase(t1)->b_unsigned != Pbase(t2)->b_unsigned) return 1;
		break;
	case COBJ:
	{
		Pname nn = Pbase(t1)->b_name;

		if (Pclass(nn->tp)->csu==UNION ) return 0; 

		if (t2->base==COBJ && nn->tp==Pbase(t2)->b_name->tp) return 0;

		return 1;
	}
	}

	return 0;
}

int makeCM( Ptype t )
/* return 1 if the type is not a primitive type
 * this will cause the generation of (t,0) 
 * note: ``t'' is guaranteed not to be 0
 */
{
    while ( t->base == TYPE )
	t = Pbase(t)->b_name->tp;

    switch (t->base) {
	case FLOAT:
	case DOUBLE:
	case LDOUBLE:
	case CHAR:
	case SHORT:
	case INT:
	case LONG:
	case EOBJ:
		return 0;
	default: 
		return 1;
	}
}

static ret_seen = 0;

Pstmt stmt::expand()
/*
	copy the statements with the formal arguments replaced by ANAMES 

	called once only per inline function
	expand_tbl!=0 if the function should be transformed into an expression
	and expand_tbl is the table for local variables
*/
{
	if (this == 0) error('i',"0->S::expand() for%n",expand_fn);
	Pstmt ostmt = Cstmt;
	if ( where.line ) Cstmt = this;

	if (memtbl) {	/* check for static variables */
		register Ptable t = memtbl;
		register int i;
		for (register Pname n = t->get_mem(i=1); n; NEXT_NAME(t,n,i)) {
			if (n->n_stclass == STATIC) {
				if (n->tp->base == FCT || n->tp->base==OVERLOAD) continue;
				error('s',"cannot expand inlineF with static%n",n);
				n->n_stclass = AUTO;
			}
			n->where.line = 0;
		}
	}

	if (expand_tbl) {	/* make expression */
		Pexpr ee;

		if (memtbl) { // temporaries
			int i;
			for (Pname n=memtbl->get_mem(i=1); n; NEXT_NAME(memtbl,n,i)) {
//error('d',"block %n %k %d %d",n,base,memtbl->real_block == this,n->lex_level);
				if (n->base!=NAME || n->tp==any_type || n->n_key==CLASS) continue;

				if (base==BLOCK 
				&& memtbl->real_block == this 
				&& n->lex_level < 2
				//&& n->n_table == Pfct(expand_fn->tp)->body->memtbl 
				&& (n->string[0]!='_'	// promoted from called
							// inlines
					|| n->string[1]!='_'
					|| (  n->string[2]!='X'
					   && n->string[2]!='K'))
				) {
//error('d',"not promoting%n",n);
					continue;
				}

 				if (memtbl != scope)
 				{
 			 		Pname nn = dcl_local(scope,n,expand_fn);
					nn->base = NAME;
					n->string = nn->string;
				}
				else if (n->tp && n->tp->base!=FCT && n->tp->base!=OVERLOAD){
 					n->string = temp(n->string, expand_fn);
 					n->where.line = 0;
 				}
			}
		}

		switch (base) {
		default:
			error('s',"cannot expand inlineF%a with %kS in inline",expand_fn,base);
			Cstmt = ostmt;
			return Pstmt(dummy);

		case BLOCK:
DB(if(Edebug>=2){error('d',"stmt::expand() -- block");display_stmt(this);});
			if (s_list) {
				ee = Pexpr(s_list->expand());
				if (s) {
					ee = new expr(CM, Pexpr(s->expand()), ee);
					ee->tp = ee->e2->tp;
					PERM(ee);
				}
				Cstmt = ostmt;
				return Pstmt(ee);
			}

			if (s) {
				Pstmt st = s->expand();
				Cstmt = ostmt;
				return st;
			}

			Cstmt = ostmt;
			return Pstmt(zero);

		case PAIR:
			ee = s2 ? Pexpr(s2->expand()) : 0;
			ee = new expr(CM, s?Pexpr(s->expand()):0, ee);
			ee->tp = ee->e2->tp;
			if (s_list) {
				ee = new expr(CM, ee, Pexpr(s_list->expand()));
				ee->tp = ee->e2->tp;
			}
			PERM(ee);
			Cstmt = ostmt;
			return Pstmt(ee);

		case RETURN:
			ret_seen = 1;
			s_list = 0;

			if (e == 0) 
				ee = zero;
			else {
				ee = e->expand();
				Ptype tt = Pfct(expand_fn->tp)->returns;
				if (tt == 0) tt = Pfct(expand_fn->tp)->returns;
//error('d',"return::expand() -- ee==%k  tt==%t",ee?ee->base:0,tt);
//display_expr(ee);
				if (tt!=ee->tp && ck_cast(tt,ee->tp)) ee = new cast(tt,ee);
			}
	
			Cstmt = ostmt;
			return Pstmt(ee);

		case SM:
			if (e== 0 || e==dummy)
				ee = zero;
			else {
				if (e->base == DEREF) e = e->e1;
				ee = e->expand();
			}
			// no break;

		case ASM:
			if (s_list) {
				ee = new expr(CM, ee, (Pexpr)s_list->expand());
				ee->tp = ee->e2->tp;
				PERM(ee);
			}
			Cstmt = ostmt;
			return (Pstmt)ee;

		case IF:
		{
			int ors = ret_seen;
			ret_seen = 0;
			ee = Pexpr(s->expand());
			if(
			    ee->tp->memptr() 
			    ||
			    ee->base==ASSIGN && 
			    ee->e1->tp && ee->e1->tp->base != PTR
			)
				ee = new expr(G_CM,ee,zero);
			Pexpr qq = new expr(QUEST,ee,zero);
			int ret1 = ret_seen;
			ret_seen = 0;

			qq->cond = e->expand();
			qq->e2 = else_stmt ? Pexpr(else_stmt->expand()) : zero;
			int ret2 = ret_seen; 
			if (ret1+ret2 && s_list) {
				error('s',"cannot expand inlineF%a with S after \"return\"",expand_fn);
				ret_seen = 0;
			}
			ret_seen += ret1;
			ret_seen += ret2;
			ret_seen += ors;

			// handle ``if (x) class_object_valued_expression;''

			Ptype t1 = qq->e1->tp;
			if (qq->e1->base == G_CM)
				t1=qq->e1->e1->tp;
			Ptype t2 = qq->e2->tp;

	if (t1 && t1->base==FCT)
		t1 = Pfct(t1)->s_returns ? Pfct(t1)->s_returns : Pfct(t1)->returns;

	if (t2 && t2->base==FCT)
		t2 = Pfct(t2)->s_returns ? Pfct(t2)->s_returns : Pfct(t2)->returns;

			Pname c1 = t1?t1->is_cl_obj():0;
			Pname c2 = t2?t2->is_cl_obj():0;

			int z1 = c1 && c1!=c2;
			int z2 = c2 && c1!=c2;

//error('d',"if()%k%t else%k%t",qq->e1->base,t1,qq->e2->base,t2);
//error('d',"c1%n c2%n %d %d",c1,c2,z1,z2);
//display_expr(qq);
			if (c1==0
			&& c2==0
			&& t1
			&& t2
			&& t2->check(t1,ASSIGN)
			&& t1->check(t2,ASSIGN)) {
				z1 = makeCM( t1 );
				z2 = makeCM( t2 );
				if ((z1 && ret1) || (z2 && ret2))
					error('s',"cannot expand inlineF with return in ifS");
			}

//error('d',"if()%k%t else%k%t",qq->e1->base,t1,qq->e2->base,t2);
//error('d',"c1%n c2%n %d %d",c1,c2,z1,z2);
//display_expr(qq);
			if (t1 && z1==0) {
				// since zero is acceptable to all pointer types
				// we need only ``fix'' z1 and z2 will take care
				// of itself
				z1 = t1->is_ptr_or_ref()
					&& t2
					&& t2->is_ptr_or_ref()
					&& t1->check(t2,0)
					&& !const_problem;
			}

//error('d',"if()%k%t else%k%t",qq->e1->base,t1,qq->e2->base,t2);
//error('d',"c1%n c2%n %d %d",c1,c2,z1,z2);
//display_expr(qq);
			if (z1) {
				Pexpr v = (z2==0 && (t2->is_ptr_or_ref())) ? new cast(t2,zero) : zero;
				qq->e1 = new expr(CM,qq->e1,v);
				qq->e1->tp = qq->e1->e1->tp;
			}

//error('d',"if()%k%t else%k%t",qq->e1->base,t1,qq->e2->base,t2);
//error('d',"c1%n c2%n %d %d",c1,c2,z1,z2);
//display_expr(qq);
			if (z2) {
				Pexpr v = (z1==0 && (t1->is_ptr_or_ref())) ? new cast(t1,zero) : zero;
				qq->e2 = new expr(CM,qq->e2,v);
				qq->e2->tp = qq->e2->e1->tp;
			}

//error('d',"if()%k%t else%k%t",qq->e1->base,t1,qq->e2->base,t2);
//error('d',"c1%n c2%n %d %d",c1,c2,z1,z2);
//display_expr(qq);
			if (s_list) {
				qq = new expr(CM,qq,Pexpr(s_list->expand()));
				qq->tp = qq->e2->tp;
			}
			else
				qq->tp = qq->e1->tp;
			PERM(qq);
			Cstmt = ostmt;
			return Pstmt(qq);
		}
		}
	}

	where.line = 0;

	switch (base) {
	default:
		if (e) e = e->expand();
		break;
	case PAIR:
		if (s2) s2 = s2->expand();
		break;
	case BLOCK:
		break;
	case FOR:
		if (for_init) for_init = for_init->expand();
		if (e2) e2 = e2->expand();
	case ASM:
		break;
	case LABEL: 
		{
		char *s=(char *)malloc(strlen(d->string)+1);
		strcpy(s,d->string);
		d->string=s;
		Ptable oscope=Scope;
		Pname n=scope->look(d->string,LABEL);
		Scope=n->n_realscope;
		d->string=temp(d->string,expand_fn);
		Scope=oscope;
		break;
		}
	case GOTO: {
		Ptable oscope=Scope;
		Pname n=scope->look(d->string,LABEL);
		Scope=n->n_realscope;
		d->string=temp(d->string,expand_fn);
		Scope=oscope;
		break;
		}
	case RETURN:
//	case BREAK:
//	case CONTINUE:
		error('s',"cannot expand inlineF%n with %kS",expand_fn,base);
	}

	if (s) s = s->expand();
	if (s_list) s_list = s_list->expand();
	PERM(this);
	Cstmt = ostmt;
	return this;
}

Pexpr expr::expand()
{
	if (this == 0) error('i',"E::expand(0)");

	switch (base) {
	case NAME:
		if ( expand_tbl
		&&   Pname(this)->n_scope == FCT 
		&&   Pname(this)->lex_level > expand_fn->lex_level ) {
			Pname n = Pname(this);
			char* s = n->string;
			if (s[0]=='_' && s[1]=='_' && (s[2]=='X'||s[2]=='K')) break;
			Pname cn = expand_fn->n_table->t_name;
		//	n->string = temp(s,expand_fn->string,(cn)?cn->string:0);
			n->string = temp(s,expand_fn);
		}
	case DUMMY: // null or error
	case DTOR:  // dummy type destructor
	case ICON:
	case FCON:
	case CCON:
	case IVAL:
	//case FVAL:
	//case LVAL:
	case STRING:
	case ZERO:
	case TEXT:
	case ANAME:
	case MDOT:
		break;
	case ICALL:
		if (expand_tbl && e1==0) {
			int ors = ret_seen;
			ret_seen = 0;
			Pname fn = il->fct_name;
			Pfct f = Pfct(fn->tp);
			if (f->f_expr==0
		// f->returns==void_type
		//	&& f->s_returns!=int_type
		//	&& fn->n_oper!=CTOR
			)
				error('s',&fn->where,"cannot expand value-returning inline%a with call of non-value-returning inline%a",expand_fn,fn);
		//	else
		//		error("inline%n called before defined",fn);
ret_seen = ors;
		}
		break;
	case SIZEOF:
	case CAST:
	case G_CAST:
		if (tp2) PERM(tp2);
		goto rrr;
	case QUEST:
		cond = cond->expand();
	default:
		if (e2) e2 = e2->expand();
	case REF:
	case DOT:
	rrr:
		if (e1) e1 = e1->expand();
		break;
	}

	switch (base) {
	case CM:
	case G_CM:
		if (tp==0) tp=e2->tp;
	}

	PERM(this);
	return this;
}

int nin;

static int watch_out;
bit expr::not_simple()
/*
	is a temporary variable needed to hold the value of this expression
	as an argument for an inline expansion?
	return 1; if side effect
	return 2; if modifies expression
*/
{
	int s;
//error('d',"not_simple%k",base);
	switch (base) {
	default:
		return 2;
	case NAME:
		if (nin==0 && Pname(this)->n_table==gtbl) return 2;	// unsafe: aliasing
	case ZERO:
	case IVAL:
	//case FVAL:
	case ICON:
	case CCON:
	case FCON:
		return 0;
	case STRING:
		::watch_out = 1;
		return 0;
	case MDOT:
		return mem->not_simple();
	case SIZEOF:
		return (e1==0 || e1==dummy) ? 0 : e1->not_simple();
	case G_ADDROF:
	case ADDROF:
		return e2->not_simple();
	case CAST:
	case G_CAST:
		return e1->not_simple();
	case DOT:
		::watch_out = 1;
		return e1->not_simple();
	case REF:
		::watch_out = 1;
		return e1->not_simple();
	case UMINUS:
	case NOT:
	case COMPL:
		return e2->not_simple();
	case DEREF:
		s = e1->not_simple();
		if (1<s) return 2;
		if (e2==0) return s;
		return s |= e2->not_simple();
	case MUL:
	case DIV:
	case MOD:
	case PLUS:
	case MINUS:
	case LS:
	case RS:
	case AND:
	case OR:
	case ER:
	case LT:
	case LE:
	case GT:
	case GE:
	case EQ:
	case NE:
	case ANDAND:
	case OROR:
	case CM:
		s = e1->not_simple();
		if (1<s) return 2;
		return s |= e2->not_simple();
	case QUEST:
		s = cond->not_simple();
		if (1<s) return 2;
		s |= e1->not_simple();
		if (1<s) return 2;
		return s |= e2->not_simple();
	case ANAME:
		if (curr_icall) {
			Pname n = (Pname)this;
			int argno = int(n->n_val);
			for (Pin il=curr_icall; il; il=il->i_next)
				if (n->n_table == il->i_table) goto aok;
			goto bok;
		aok:
			return (il->i_args[argno].local) ? 0 : il->i_args[argno].arg->not_simple();
		}
	bok:	error('i',"expand aname%n",this);
	case G_CM:
	case VALUE:
	case NEW:
	case GNEW:
	case CALL:
	case G_CALL:
	case ICALL:
	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:
		return 2;
	}
}

extern void uninline(Pname fn);
//extern Pname new_fct;	
//extern Pname del_fct;
extern Pstmt del_list;
extern Pstmt break_del_list;
extern Pstmt continue_del_list;
extern Pname curr_fct;
extern Pexpr init_list;

extern Pname make_default_ctor(Pclass cl);
void expand_itor(Pclass cl)
{
//error('d',"expand_itor(%t)",cl);
	Pexpr s2 = curr_expr;
	Pstmt s5 = del_list;
	Pstmt s6 = break_del_list;
	Pstmt s7 = continue_del_list;
	Pname s8 = curr_fct;
	Pexpr s9 = init_list;
	(void) cl->make_itor(1);
	curr_expr = s2;
	del_list = s5;
	break_del_list = s6;
	continue_del_list = s7;
	curr_fct = s8;
	init_list = s9;
}
/*
extern Pname make_default_dtor(Pclass cl);
void expand_dtor(Pclass cl)
{
//error('d',"expand_itor(%t)",cl);
	Pexpr s2 = curr_expr;
	Pstmt s5 = del_list;
	Pstmt s6 = break_del_list;
	Pstmt s7 = continue_del_list;
	Pname s8 = curr_fct;
	Pexpr s9 = init_list;
	(void) make_default_dtor(cl);
	curr_expr = s2;
	del_list = s5;
	break_del_list = s6;
	continue_del_list = s7;
	curr_fct = s8;
	init_list = s9;
}


void expand_ictor(Pclass cl)
{
//error('d',"expand_itor(%t)",cl);
	Pexpr s2 = curr_expr;
	Pstmt s5 = del_list;
	Pstmt s6 = break_del_list;
	Pstmt s7 = continue_del_list;
	Pname s8 = curr_fct;
	Pexpr s9 = init_list;
	(void) make_default_ctor(cl);
	curr_expr = s2;
	del_list = s5;
	break_del_list = s6;
	continue_del_list = s7;
	curr_fct = s8;
	init_list = s9;
}
*/
Pexpr fct::expand(Pname fn, Ptable scope, Pexpr ll)
/*
	expand call to (previously defined) inline function in "scope"
	with the argument list "ll"
	(1) declare variables in "scope"
	(2) initialize argument variables
	(3) link to body
*/
{
	Scope = scope;
//error('d',"expand%s() body %d f_expr %d inline %d",fn->string,body,f_expr,f_inline);
	if (f_inline == ITOR) {
		Pexpr s1 = last_expanded;
		expand_itor(memof);
		last_expanded = s1;
		last_stmt = stmtno;
		//this = Pfct(fn->tp);
		Pfct nf = Pfct(fn->tp);
		if (nf->f_inline==0 && nf->f_imeasure) {
			uninline(fn);
			return 0;
		}
		return nf->expand(fn,scope,ll);
	}

//error('d',"expand %n body %d f_expr %d defined %d",fn,body,f_expr,defined);
//error('d',"inline %d memtbl %d scope %d",f_inline,Pfct(fn->tp)->body->memtbl,scope);
//display_stmt(body);
	if ((body==0 && f_expr==0)			// before defined
	||  ((defined&SIMPLIFIED)==0)			// before simplified
	||  (Pfct(fn->tp)->body->memtbl==scope)		// while defining
	||  (f_inline>1)				// recursive call
	) {						// so don't expand
		if (warning_opt) error('w',"cannot inline%n in thisE",fn);
		// *** SBL: move def from template.h to cfront.h
		extern Pfct current_fct_instantiation; 
		if (fct_base == INSTANTIATED)
			current_fct_instantiation = this;
		if (fn->n_addr_taken++==0) fn->dcl_print(0);
		if (fct_base == INSTANTIATED)
			current_fct_instantiation = 0;
		return 0;
	}

	if (fn->n_oper==CTOR) {
		Pclass cl = Pclass(fn->n_table->t_name->tp);
		if (cl->c_body == 3) cl->print_all_vtbls(cl);
	}

	Pin il = new iline;
	Pexpr ic = new texpr(ICALL,0,0);
	int ns = 0;
	for (Pname an = f_args; an; an=an->n_list) ns++;
	il->fct_name = fn;
	il->i_args = new ia[il->i_slots = ns];
	ic->il = il;
	ic->tp = s_returns ? s_returns : returns;
//	Pname at = (f_this) ? f_this : (f_result) ? f_result : argtype;
	Pname at = f_args;

	f_inline++;

	if (at) il->i_table = at->n_table;
	int i = 0;
	int not_simple = 0;	/* is a temporary argument needed? */

	for (Pname n=at; n; n=n->n_list, i++) {
		/*	check formal/actual argument pairs
			and generate temporaries as necessary
		*/

		if (ll == 0) error('i',"F::expand(%n):AX",fn);
		Pexpr ee;

		if (ll->base == ELIST) {
			ee = ll->e1;
			ll = ll->e2;
		}
		else {
			ee = ll;
			ll = 0;
		}

		/* could be avoided when expanding into a block */
		il->i_args[i].local = 0;
		int s ;

 	// try to protect agains aliasing through pointers and references
	for (Pname m=at; m; m=m->n_list) {
		Pptr p;
		if (n!=m) {
		//	if ((p=m->tp->is_ptr()) || (p=m->tp->is_ref()))
			if (p=m->tp->is_ptr_or_ref())
				if (p->check(n->tp,0)==0 || p->typ->check(n->tp,0)==0) goto zxc;

		}
	}

		::watch_out = 0;
		int notsimple; notsimple = ee->not_simple();
		if ( notsimple==0 && ::watch_out && n->n_used > 1 && ee->base!=G_ADDROF) goto zxc;
		if (n->n_assigned_to==FUDGE111
		&& ee!=zero
		&& notsimple==0) {
			if (ee && ee->e1 && (ee->e1->base == NAME) &&
			       (! strcmp (ee->e1->string,"this"))) goto zxc;
		}
		else if (n->n_addr_taken || n->n_assigned_to) 
			goto zxc;
		else if (s=notsimple) {
			if (/*n->n_used==0	// n_used not set for ``this''
			|| */1<s
			|| 1<n->n_used ) {	// not safe
		zxc:
			if (last_expanded && last_expanded!=dummy && last_expanded==curr_expr && last_stmt==stmtno) {
				if (warning_opt)
					error('w',"%a not inlined, called twice in an expression",fn);
				f_inline--;
				delete il->i_args;
				delete il;
				// *** SBL: move to cfront.h from template.h
				extern Pfct current_fct_instantiation; 
				if (fct_base == INSTANTIATED)
					current_fct_instantiation = this;
				if (fn->n_addr_taken++==0) fn->dcl_print(0);
				if (fct_base == INSTANTIATED)
					current_fct_instantiation = 0;
				return 0;
			}
//error('d',"zxc %n %t ee %d %t",n,n->tp,ee->base,ee->tp);
			if (ee && ee->tp && (ee->tp->base==EOBJ) && ansi_opt) 
				; // do nothing
			else {
				Pname nn = dcl_local(scope,n,fn,1);
				nn->base = NAME;
				il->i_args[i].local = nn;
				++not_simple;
			//	if (nn->tp->is_ref()
			//	&& ee->tp
			//	&& ee->tp->is_ptr_or_ref()==0) ee = ee->address();
			}
			}
		}

		il->i_args[i].arg = ee;
		il->i_args[i].tp = n->tp;
	}
	
	Ptable tbl = body->memtbl;
	if (f_expr) {		// generate comma expression
		
		char loc_var = 0;

		/* look for local variables needing declaration: */
		for (n=tbl->get_mem(i=1); n; NEXT_NAME(tbl,n,i) ) {
//error('d',"n %n %d",n,n->base);
//error('d',"loc %n %d %d %d",n,n->n_used,n->n_assigned_to,n->n_addr_taken);
			if (n->base==NAME	// don't re-declare the args
			&& (n->tp->base!=FCT && n->tp->base!=OVERLOAD)
			&& (n->n_used || n->n_assigned_to || n->n_addr_taken)) {
if (last_expanded && last_expanded==curr_expr && last_stmt == stmtno) {
	if (warning_opt) error('w',"cannot inline%n in thisE",fn);
	f_inline--;
	delete il->i_args;
	delete il;
	if (fn->n_addr_taken++==0) fn->dcl_print(0);
	return 0;
}
				Pname nn = dcl_local(scope,n,fn);
				nn->base = NAME;
				n->string = nn->string;

				loc_var++;
			}
		}

		if (i /*not_simple*/ || loc_var) {
			if(!curr_expr) curr_expr = dummy;
			last_expanded = curr_expr;
			last_stmt = stmtno;
		}

		Pexpr ex;
		if (not_simple) {
			Pexpr etail = ex = new expr(CM,0,0);
			for (i=0; i<il->i_slots; i++) {
				Pname n = il->i_args[i].local;
				if (n == 0) continue;
				Pexpr e = il->i_args[i].arg;
					// if used assign
					// otherwise (e.g. unused argument)
					// simply evaluate for sideeffects
				if (n->n_used
				|| n->n_assigned_to
				|| n->n_addr_taken) {
				Pptr p1 = n->tp->is_ptr();
                                Pptr p2 = e->tp ? e->tp->is_ptr() : 0;
				if (p1 && p1->memof && !p2) {
					switch (e->base) {
					case CM:
					case G_CM:
						e->e2 = mptr_assign(n,e->e2);
						break;
					case ICALL:
						break;
					default:
						e = mptr_assign(n,e);
					}
					etail->e1 = e;
				}
				else
					etail->e1 = new expr(ASSIGN,n,e);
				}
				else
					etail->e1 = e;
				if (--not_simple)
					etail = etail->e2 = new expr(CM,0,0);
				else
					break;
			}
			etail->e2 = f_expr;
		}
		else
			ex = f_expr;
		ic->e1 = ex;
	}
	else {		// generate block:
		for (n=tbl->get_mem(i=1); n; NEXT_NAME(tbl,n,i) ) {
			// mangle local names
			if (n->base==NAME
			&& (n->n_used || n->n_assigned_to || n->n_addr_taken)) {
			//	Pname cn = fn->n_table->t_name;
			//	n->string = temp(n->string,fn->string,(cn)?cn->string:0);
				n->string = temp(n->string,fn);
			}
		}
		Pstmt ss;
		if (not_simple) {
			if(!curr_expr) curr_expr = dummy;
			last_expanded = curr_expr;
			last_stmt = stmtno;
			Pstmt st = new estmt(SM,curloc,0,0);
			st->where.line = 0;
			Pstmt stail = st;
			for (i=0; i<il->i_slots; i++) {
				Pname n = il->i_args[i].local;
				if (n == 0) continue;
				Pexpr e = il->i_args[i].arg;
				Pptr p1 = n->tp->is_ptr();
				Pptr p2 = e->tp->is_ptr();
				if (p1 && p1->memof && !p2) {
					switch (e->base) {
					case CM:
					case G_CM:
						e->e2 = mptr_assign(n,e->e2);
						break;
					default:
						e = mptr_assign(n,e);
					}
					stail->e = e;
				}
				else
					stail->e = new expr(ASSIGN,n,e);
				if (--not_simple) {
					stail = stail->s_list = new estmt(SM,curloc,0,0);
					stail->where.line = 0;
				}
				else
					break;
			}
			stail->s_list = body;
			ss = new block(curloc,0,st);
			ss->where.line = 0;
		}
		else
			ss = body;
		ic->e2 = Pexpr(ss);
	}

	f_inline--;
//error('d',"expand%n -- returning%k", fn, ic?ic->base:0);
//display_expr(ic);
	return ic;
}
