/*	Copyright  (c)	Günter Woigk 2010 - 2018
					mailto:kio@little-bat.de

	This file is free software

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	• Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.
	• Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
	OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
	OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
	ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <limits>
#include <cmath>
#include <cfloat>
#include "Type.h"
#include "IdfID.h"
#include "globals.h"
#include "Names.h"
//#include "Scope.h"
//#include "TypeSymbol.h"
//#include "Error.h"
//#include "Variable.h"
//#include "Const.h"
//#include "StructType.h"
//#include "Opcode.h"
//#include "ProcSymbol.h"



// ---------------------------------------------------------------
//			class Type
// ---------------------------------------------------------------

Type::Type (cType* basetype, TypeID type, cstr name, uint bits)
:
	basetype(basetype),
	name(newcopy(name)),
	type(type),
	bits(bits),
	subtypes(),
	ref_type(nullptr)
{
	if (basetype) basetype->subtypes.append(this);
}

Type::~Type ()
{
	for (uint i=0; i<subtypes.count(); i++) { delete subtypes[i]; }
}

void Type::print (FD& fd, cstr indent) const
{
	fd.write_fmt("%s%s\n", indent, name);
}

template<>
inline void Array<cType*>::serialize (FD& fd) const throws
{
	// serialize --specialization for T = Type*

	fd.write_uint16_z(MAGIC+3457);
	fd.write_uint32_z(cnt);
	for(uint i=0; i<cnt; i++)
		data[i]->serialize(fd);
}

void Type::serialize(FD& fd) const
{
	fd.write(type);
	fd.write(bits);
	subtypes.serialize(fd);
}

void Type::deserialize(FD&)
{
	TODO();
}

Type* Type::restore (FD&) throws
{
	TODO();
}


bool Type::isArrayWithInitialCount() const
{
	return isArray() && reinterpret_cast<const ArrayType*>(this)->initialcount != 0;
}


cPtrType* Type::refSubType() const
{
	if (!ref_type) ref_type = new PtrType(this, REF);
	return ref_type;
};

cEnumType* Type::enumSubType(cstr name) const
{
	assert(isBasicType());

	for (uint i = 0; i < subtypes.count(); i++)
	{
		cType* t = subtypes[i];
		if(t->isEnum() && eq(t->name,name)) return reinterpret_cast<cEnumType*>(t);
	}
	return nullptr;
}

cPtrType* Type::ptrSubType() const
{
	for (uint i = 0; i < subtypes.count(); i++)
	{
		cType* t = subtypes[i];
		if(t->isPtr()) return reinterpret_cast<cPtrType*>(t);
	}
	return new PtrType(this, PTR);
}

cArrayType* Type::arraySubType (uint initialcount) const
{
	for (uint i = 0; i < subtypes.count(); i++)
	{
		if (cArrayType* t = dynamic_cast<cArrayType*>(subtypes[i]))
			if (t->initialcount == initialcount) return t;
	}
	return new ArrayType(this, initialcount);
}

cProcType* Type::procSubType (Types const&) const
{
	TODO();
}

cRangeType* Type::rangeSubType () const
{
	TODO();
}


// ---------------------------------------------------------------
//			class BasicType
// ---------------------------------------------------------------

BasicType::BasicType (TypeID tid, cstr name, uint bits, float128 min, float128 max)
:
	Type(nullptr,tid,name,bits),
	precision(0),min_exp(0),max_exp(0),denormalizing(no),
	max(max),min(min)
{
	assert(isBasicType());
	assert(!isFloat());
}

BasicType::BasicType (TypeID tid, cstr name, uint bits, float128 min, float128 max, uint16 precision, int16 min_exp, int16 max_exp, bool denorm)
:
	Type(nullptr,tid,name,bits),
	precision(precision),
	min_exp(min_exp),
	max_exp(max_exp),
	denormalizing(denorm),
	max(max),min(min)
{
	assert(isBasicType());
}

bool BasicType::canRepresent(float128 value) const noexcept
{
	assert(isNumeric());

	if (isInteger()) { return value >= min && value <= max; }

	int exp;
	(void) frexp(value, &exp);
	return exp >= min_exp && exp <= max_exp;
}

bool BasicType::isNaN (float128 value) const noexcept
{
	return isnan(value);
}

bool BasicType::isInfinity (float128 value) const noexcept
{
	assert(isFloat());
	if (isinf(value)) return yes;

	int exp;
	(void) frexp(value, &exp);
	return exp > max_exp;
}

bool BasicType::isRoundToZero (float128 value) const noexcept
{
	assert(isFloat());

	if (value == 0.0l) return no;

	int exp;
	(void) frexp(value, &exp);
	return denormalizing ? exp <= min_exp-precision : exp < min_exp;
}

bool BasicType::isDenormalized (float128 value) const noexcept
{
	assert(isFloat());

	if (value == 0.0l) return no;

	int exp;
	(void) frexp(value, &exp);
	return exp < min_exp;
}

uint BasicType::numLostBits (float128 value) const noexcept	// due to denormalization / rounding to zero
{
	assert(isFloat());

	if (value == 0.0l) return 0;

	int exp;
	float128 mant = frexp(value, &exp);
	(void) mant;

	if (exp >= min_exp) return 0;
	// exp < min_exp
	if (!denormalizing) return precision;				// round to zero => all lost
	if (exp <= min_exp - precision) return precision;	// even denormaization can help
	// precision > min_exp - exp
	return uint(min_exp - exp);
}

PtrType::PtrType (cType* basetype, TypeID type)
:	Type(basetype, type, catstr(basetype->name,(type==PTR?"*":"&")), target->bpp)
{
	assert(type==PTR || type==REF);
}

AllocatedType::AllocatedType (cType* basetype, TypeID type, cstr name)
:	Type(basetype, type, name, target->bpp)
{}

ArrayType::ArrayType (cType* itemtype, uint initialcount)
:	AllocatedType(itemtype, ARRAY, catstr(itemtype->name,"[]")),
	initialcount(initialcount)
{}

EnumType::EnumType (cBasicType* basetype, cstr name)
:	Type(basetype, TypeID(ENUM|basetype->type), name, basetype->bits)
{
	#if XSAFE
	for (uint i = 0; i < subtypes.count(); i++)
	{
		cType* t = subtypes[i];
		assert(!t->isEnum() || ne(t->name,name) || t == this);
	}
	#endif
}


void PtrType::print (FD& , cstr ) const {TODO();}
void PtrType::serialize(FD&) const			{TODO();}
void PtrType::deserialize(FD&)				{TODO();}

void ArrayType::print (FD& , cstr ) const {TODO();}
void ArrayType::serialize(FD&) const			{TODO();}
void ArrayType::deserialize(FD&)				{TODO();}

void BasicType::print (FD& , cstr ) const {TODO();}
void BasicType::serialize(FD&) const			{TODO();}
void BasicType::deserialize(FD&)				{TODO();}

void EnumType::print (FD& , cstr ) const {TODO();}
void EnumType::serialize(FD&) const			{TODO();}
void EnumType::deserialize(FD&)				{TODO();}



BasicType* newFloatType (uint bits)
{
	if (bits == 32)
	{
		return new BasicType(Type::SFLOAT, "float32", 32,
				float128(std::numeric_limits<float32>().max()),
				float128(std::numeric_limits<float32>().min()),
				FLT_MANT_DIG,	// 24	 num digits for mantissa
				FLT_MIN_EXP,	// -125	 min. exponent for mantissa in range 0.5 ≤ mant < 1.0
				FLT_MAX_EXP,	// +128	 max. exponent for mantissa in range 0.5 ≤ mant < 1.0
				yes);			// yes	 denormalizes before tiny values are rounded to zero?
	}

	if (bits == 64)
	{
		return new BasicType(Type::FLOAT, "float", 64,
				 float128(std::numeric_limits<float64>().max()),
				 float128(std::numeric_limits<float64>().min()),
				 DBL_MANT_DIG,	// 53
				 DBL_MIN_EXP,	// -1021
				 DBL_MAX_EXP,	// +1024
				 yes);			// denormaizes
	}

	if (bits == 128)
	{
		return new BasicType(Type::LFLOAT, "float128", 128,
				 std::numeric_limits<float128>().max(),
				 std::numeric_limits<float128>().min(),
				 LDBL_MANT_DIG, // 64
				 LDBL_MIN_EXP,	// -16381
				 LDBL_MAX_EXP,	// +16384
				 yes);			// denormalizes
	}

	IERR();
}



#ifndef NDEBUG
#include "utilities.h"
#include <thread>
#include <cfloat>
#include <cmath>

static_assert(FLT_RADIX==2,"");
static_assert(FLT_MANT_DIG==24,"");
static_assert(DBL_MANT_DIG==53,"");
static_assert(LDBL_MANT_DIG==64,"");
ON_INIT([]
{
	xlogline("float32:  mant=%u, exp=[%i..%i]", FLT_MANT_DIG, FLT_MIN_EXP, FLT_MAX_EXP);  // 24, -125 .. +128
	xlogline("float64:  mant=%u, exp=[%i..%i]", DBL_MANT_DIG, DBL_MIN_EXP, DBL_MAX_EXP);  // 53, -1021 .. +1024
	xlogline("float128: mant=%u, exp=[%i..%i]", LDBL_MANT_DIG,LDBL_MIN_EXP,LDBL_MAX_EXP); // 64, -16381 .. +16384

	//float f;
	//f = ldexpf(0.5f,FLT_MIN_EXP);    logline("0.5f << -125 = %g", double(f)); //assert( f != 0.0f);
	//f = ldexpf(0.5f,FLT_MIN_EXP-2);  logline("0.5f << -127 = %g", double(f)); //assert( f == 0.0f);
	//f = ldexpf(0.5f,FLT_MIN_EXP-23); logline("0.5f >> (125+23) = %g", double(f)); //assert( f == 0.0f);
	//f = ldexpf(0.5f,FLT_MIN_EXP-24); logline("0.5f >> (125+24) = %g", double(f)); //assert( f == 0.0f);

	BasicType* t32 = newFloatType(32);
	BasicType* t64 = newFloatType(64);
	BasicType* t80 = newFloatType(128);

	t32->isNaN(nanl(""));
	t64->isNaN(0.0l/0.0l);
	t80->isNaN(sqrtl(-1.0l));

	assert(t64->isInfinity(+1e+350l));
	assert(t64->isInfinity(-1e+350l));
	assert(t64->isRoundToZero(+1e-350l));
	assert(t64->isRoundToZero(-1e-350l));
	assert(t64->isDenormalized(ldexp(0.55l,DBL_MIN_EXP-10)));
	assert(t64->numLostBits(ldexp(0.55l,DBL_MIN_EXP-10))==10);

	assert(t32->isInfinity(ldexp(0.50000l,FLT_MAX_EXP+1)));
	assert(t32->isInfinity(ldexp(0.99999l,FLT_MAX_EXP+1)));
	assert(t64->isInfinity(ldexp(0.50000l,DBL_MAX_EXP+1)));
	assert(t64->isInfinity(ldexp(0.99999l,DBL_MAX_EXP+1)));
	assert(t80->isInfinity(ldexp(0.50001l,LDBL_MAX_EXP+1)));
	assert(t80->isInfinity(ldexp(0.99999l,LDBL_MAX_EXP+1)));

	assert(!t32->isInfinity(ldexp(0.50000l,FLT_MAX_EXP)));
	assert(!t32->isInfinity(ldexp(0.99999l,FLT_MAX_EXP)));
	assert(!t64->isInfinity(ldexp(0.50000l,DBL_MAX_EXP)));
	assert(!t64->isInfinity(ldexp(0.99999l,DBL_MAX_EXP)));
	assert(!t80->isInfinity(ldexp(0.50000l,LDBL_MAX_EXP)));
	assert(!t80->isInfinity(ldexp(0.99999l,LDBL_MAX_EXP)));

	assert(t32->isRoundToZero(ldexp(0.5l,FLT_MIN_EXP-FLT_MANT_DIG)));
	assert(t64->isRoundToZero(ldexp(0.5l,DBL_MIN_EXP-DBL_MANT_DIG)));
	//assert(t80->isRoundToZero(ldexp(0.5l,LDBL_MIN_EXP-LDBL_MANT_DIG)));	if round to 0 then it already is 0
	assert(ldexp(0.5l,LDBL_MIN_EXP-LDBL_MANT_DIG) == 0.0l);
	assert(!t32->isRoundToZero(ldexp(0.5l,FLT_MIN_EXP-FLT_MANT_DIG+1)));
	assert(!t64->isRoundToZero(ldexp(0.5l,DBL_MIN_EXP-DBL_MANT_DIG+1)));
	assert(!t80->isRoundToZero(ldexp(0.5l,LDBL_MIN_EXP-LDBL_MANT_DIG+1)));

	long double l;
	l = ldexp(0.5l,DBL_MIN_EXP-DBL_MANT_DIG+1); xxlogline("0.5f >> (1021+53-1) = %g", double(l));
		assert(t64->numLostBits(l) == DBL_MANT_DIG-1);
		assert(t64->isDenormalized(l));
		assert(!t64->isRoundToZero(l));

	l = ldexp(0.5l,DBL_MIN_EXP-DBL_MANT_DIG);   xxlogline("0.5f >> (1021+53)   = %g", double(l));
		assert(t64->numLostBits(l) == DBL_MANT_DIG);
		assert(t64->isDenormalized(l));
		assert(t64->isRoundToZero(l));

	l = ldexp(0.5l, LDBL_MIN_EXP - LDBL_MANT_DIG + 1);
		assert(t80->numLostBits(l) == LDBL_MANT_DIG - 1);
		assert(t80->isDenormalized(l));
	l = ldexp(0.5l, LDBL_MIN_EXP - 1);
		assert(t80->numLostBits(l) == 1);
		assert(t80->isDenormalized(l));
	l = ldexp(0.9l, LDBL_MIN_EXP - 1);
		assert(t80->numLostBits(l) == 1);
		assert(t80->isDenormalized(l));
});
#endif


#if 0

inline uint offset_in_int (uint bits)
{
	return byteorder==byteorder_lohi || bits >= bpi ? 0u : bpi - bits;
}

inline uint stack_size (uint bits)
{
	uint m = (bpi>>b2b) -1;
	m = ((((bits+bpb-1)>>b2b)+m)|m)-m;
	return m << b2b;
}


void cTypes::remove(uint idx)
{
	if (idx < cnt)
	{
		TODO();//stacksize -= data[idx]->Stacksize();
		RCArray::remove(idx);
	}
}

void cTypes::drop()
{
	if(cnt)
	{
		TODO();//stacksize -= data[--cnt]->Stacksize();
	}
}








namespace cc { extern Scope* auto_symbols; }


Type* UintForBits(uint bits)
{
	switch(bits>>4)
	{
	case 0: if(Options::bpb<=8) return tuint8;	// 8>>4
	case 1:	return tuint16;	// 16>>4
	case 2:	return tuint32;	// 32>>4
	case 4:	return tuint64;	// 64>>4
	default: IERR();
	}
}

Type* IntForBits(uint bits)
{
	switch(bits>>4)
	{
	case 0: if(Options::bpb<=8) return tint8;	// 8>>4
	case 1:	return tint16;	// 16>>4
	case 2:	return tint32;	// 32>>4
	case 4:	return tint64;	// 64>>4
	default: IERR();
	}
}


Type* UintForBytes(uint bytes)
{
	switch((bytes<<Options::b2b)>>3)
	{
	case 1: return tuint8;
	case 2:	return tuint16;
	case 4:	return tuint32;
	case 8:	return tuint64;
	default: IERR();
	}
}

Type* IntForBytes(uint bytes)
{
	switch((bytes<<Options::b2b)>>3)
	{
	case 1: return tint8;
	case 2:	return tint16;
	case 4:	return tint32;
	case 8:	return tint64;
	default: IERR();
	}
}




idf_id Type::FQN() const
{
	return names.add(FQNStr());
}

cstr tostr(type_id n)
{
	if(n<=t_void) return basictypes[n]->NameStr();
	else return usingstr("type #%i",(int)n);
}


// ____________________ creators ____________________________






EnumType::EnumType( cType* dest )
:	Type(t_enum,dest)
{}

CentType::CentType( cType* dest )
:	Type(t_cent,dest)
{
	assert( dest->isAllocated() );
}

PtrType::PtrType( cType* dest, type_id tid )
:	Type(tid,dest)
{
	assert(basetype);
	assert(isPtr()||isRef());
}

ProcType::ProcType( cType* rtype, cTypes& args )
:	Type(t_proc,rtype),
	args(args,1/*copy*/)
{
	assert(basetype);
}

ArrayType::ArrayType( cType* items, uint cnt )
:	AllocatedType(t_array,items),
	initialcount(cnt)
{
	grow=
	shrink=resize=NULL;
	assert(basetype);
}

StructType::StructType( cStructType* basetype )
:	AllocatedType(t_struct,basetype)
{}

RangeType::RangeType( cType* itemtype )
:	Type(t_range,itemtype)
{}


// ----------------------------------------------------------------


idf_id Type::Idf() const
{
//	if(tid<=t_void) return tinfo[tid].idf;
	if(tid<=t_void) return basictypes[tid]->Idf();

	assert( mysymbol!=NULL && mysymbol->isaType());
	return mysymbol->Idf();
};

cstr Type::Name(bool fullname) const
{
	if(tid<=t_void) return tostr(tid);
	return mysymbol ? mysymbol->Name(fullname) : this->Definition(fullname);
}

cstr Type::Definition(bool fullname) const
{
	switch(tid)
	{
	default:	// BasicType:	t_int8 .. t_uint64, t_float, t_void
		assert(Tid()<=t_void);
		return tostr(tid);

	case t_enum:	return catstr( "enum ",basetype->Name(fullname));
	case t_cent:	return catstr( basetype->Name(fullname), "¢" );
	case t_ref:		return catstr( basetype->Name(fullname), "&" );
	case t_ptr:		return catstr( basetype->Name(fullname), "*" );
	case t_array:
		{
			ArrayType* p = ArrayTypePtr(this);
			cstr s = basetype->Name(fullname);
			return p->InitialCount() ? catstr(s,"[",numstr(p->InitialCount()),"]") : catstr(s,"[]");
		}
	case t_range:
		{
			return catstr( basetype->Name(fullname), "[to]" );
		}
	case t_proc:
		{
			ProcType* p = ProcTypePtr(this);
			cstr s = basetype->Name(fullname);
			if(p->ArgCnt()==0) return catstr(s,"()");
			s = catstr( s, "(", p->ArgType(0)->Name(fullname) );
			for(uint i=1;i<p->ArgCnt();i++) s = catstr(s,", ",p->ArgType(i)->Name(fullname));
			return catstr(s,")");
		}
	case t_signal:
		{
			SignalType* st = SignalTypePtr(this);
			cProcType*  pt = st->getProcType();
			assert(pt->basetype==tvoid);
			assert(pt->ArgCnt()>=1);
			cstr s = catstr( "signal(", pt->ArgType(0)->Name(fullname) );
			for(uint i=1;i<pt->ArgCnt();i++) s = catstr(s,", ",pt->ArgType(i)->Name(fullname));
			return catstr(s,")");
		}
	case t_struct:
		{
			StructType* p = StructTypePtr(this);
			cSymbols& symbols = p->GetMembers();
			if(symbols.count()==0) return BaseType() ? catstr(BaseType()->Name(fullname)," + {}") : "{}";
			cstr s = symbols[(uint)0]->Definition(fullname)+4;	// +4 => omit "var "
			for( uint i=1; i<symbols.count(); i++ ) { s = catstr( s, ", ", symbols[i]->Definition(fullname)+4 ); }
			s = catstr( "{ ", s, " }" );
			return BaseType() ? catstr(BaseType()->Name(fullname)," + ",s) : s;
		}
	}
}

void Type::print(FD& fd,cstr indent) const
{
	fd.write_str(usingstr("%s%s\n",indent,Definition(1)));
}


void Type::set_typesymbol( TypeSymbol* s ) const
{
	if(typesymbol&&verbose&&!isBasictype()) logline("typesymbol %s also referred as %s",typesymbol->Name(),s->Name());
	if(typesymbol==NULL) const_cast<TypeSymbol*&>(typesymbol) = s;
}

cstr Type::FQNStr() const
{
	switch(Tid())
	{
	default:	// BasicType:
		{
			assert(Tid()<=t_void);
//			return tinfo[Tid()].pnamestr;
			return basictypes[Tid()]->pNameStr();
		}
	case t_proc:
		{
			ProcType* p = ProcTypePtr(this);
			cstr s = basetype->FQNStr();
			if(p->ArgCnt()==0) return catstr(s,"BD");
			s = catstr( s, "B", p->ArgType(0)->FQNStr() );
			for(uint i=1;i<p->ArgCnt();i++) s = catstr(s,p->ArgType(i)->FQNStr());
			return catstr(s,"D");
		}
	case t_enum:
	case t_struct:
		{
			if(mysymbol)
			{
				cstr s = mysymbol->FQNStr();
				return catstr(numstr(strLen(s)),s);
			}
			IERR();		// local proc anon struct?
		}

	case t_array:
		{
			ArrayType* p = ArrayTypePtr(this);
			cstr s = BaseType()->FQNStr();
			if(p->InitialCount()) return usingstr("%sA%uE",s,p->InitialCount());
			else return catstr(s,"AE");
		}

	case t_range:
		{
			return catstr( BaseType()->FQNStr(), "ATOE" );
		}

	case t_ref:
	case t_ptr:   return catstr( BaseType()->FQNStr(), "P" );
	case t_cent:  return catstr( BaseType()->FQNStr(), "C" );
	}
}


CentType* Type::add_cent() const
{
	CentType* t;
	for(uint i=subtypes.count();i--;)
	{
		t = (CentType*)subtypes[i];
		if(t->isCent()) return t;
	}
	t = new CentType(this);
	subtypes.append(t); return t;
}

EnumType* Type::add_enum() const
{
	EnumType* t = new EnumType(this);
	subtypes.append(t); return t;
}

ArrayType* Type::ArraySubtype( uint cnt ) const
{
	union{ cType* t; ArrayType* at; };

	for(uint i=subtypes.count();i--;)
	{
		t = subtypes[i];
		if( at->isArray() && at->InitialCount()==cnt ) return at;
	}
	at = new ArrayType(this,cnt);
	subtypes.append(at); return at;
}

RangeType* Type::RangeSubtype() const
{
	RangeType* t;
	for(uint i=subtypes.count();i--;)
	{
		t = (RangeType*)subtypes[i];
		if(t->isRange()) return t;
	}
	t = new RangeType(this);
	subtypes.append(t); return t;
}

ProcType* Type::ProcSubtype(cTypes& args) const
{
	union{ cType* t; ProcType* pt; };

	for(uint i=subtypes.count();i--;)
	{
		t = subtypes[i];
		if( pt->isProcType() && pt->ArgTypes()==args ) return pt;
	}
	pt = new ProcType(this,args);
	subtypes.append(pt); return pt;
}

SignalType* Type::SignalSubtype(cTypes& args) const
{
	return ProcSubtype(args)->SignalSubtype();
}

SignalType*	ProcType::SignalSubtype	()	const
{
	for(uint i=subtypes.count();i--;) if(subtypes[i]->isSignal()) return SignalTypePtr(subtypes[i]);

	SignalType* st = new SignalType(this);
	subtypes.append(st);

	ProcSymbol* ps = cc::auto_symbols->AppendProcSymbol(tKILL,cTypes(this),this,0/*normal*/);
	ps->SetOpcode(tKILL);
	st->SetKill(ps);
	return st;
}

bool Type::isSubclassed() const
{
	if(isStruct())
	{
		for(uint i=subtypes.count();i--;) { if( subtypes[i]->isStruct()) return yes; }
	}
	return no;
}


void StructType::addSubtype(StructType* t )
{
	assert(t->basetype==NULL);

	t->basetype = this;
	t->GetTypeSymbol()->set_structsize( GetTypeSymbol()->StructSize() );
	subtypes.append(t);
}


/* virtual */
void cTypes::shrink( uint newcnt )
{
	for(uint i=newcnt;i<cnt;i++) size -= data[i]->Stacksize();
	Array<cType*>::shrink(newcnt);
}

/* virtual */
void cTypes::append( cType* t )
{
	Array<cType*>::append(t);
	size += t->Stacksize();
}


void cTypes::append( cTypes& q )
{
	Array<cType*>::append(q);
	size += q.size;
	q.size = 0;
}



/*	create argument list for printing:	"( str const, int32, int32 )"
*/
cstr cTypes::ArgsStr(bool fullname,bool reverted) const
{
	if(count()==0) return "()";
	cstr s = data[0]->Name(fullname);
	if(reverted) for(uint i=1;i<count();i++) { s = catstr(data[i]->Name(fullname),", ",s); }
	else		 for(uint i=1;i<count();i++) { s = catstr(s,", ",data[i]->Name(fullname)); }
	return catstr("( ",s," )");
}

#if 0
/*	create signature:	"int(str const,int,int)"
*/
cstr ProcType::Signature( bool fullname, bool /*reverted*/ ) const
{
	if(ArgCnt()==0) return catstr(RType()->Name(fullname),"()");
	cstr s = args[0]->Name(fullname);
//	if(reverted) for(uint i=1;i<ArgCnt();i++) { s = catstr(args[i]->Name(fullname),",",s); }
//	else
			 for(uint i=1;i<ArgCnt();i++) { s = catstr(s,",",args[i]->Name(fullname)); }
	return catstr(RType()->Name(fullname),"(",s,")");
}
#endif

cstr StripSpaces(cstr s)
{
	cptr p = strchr(s,' ');
	while(p!=NULL)
	{
		s = catstr(substr(s,p),p+1);
		p = strchr(s,' ');
	}
	return s;
}


/*	create argument list for assembler listing:	"( str, int, int -- int )"
*/
cstr ProcType::AsmArgsStr( bool fullname ) const
{
	if(ArgCnt()==0) return RType()==tvoid ? "()" : catstr("( -- ",StripSpaces(RType()->Name())," )");
	cstr s = StripSpaces(args[0]->Name(fullname));
	for(uint i=1;i<ArgCnt();i++) { s = catstr(s,", ",StripSpaces(args[i]->Name(fullname))); }
	return RType()==tvoid ? catstr("( ",s," )") : catstr("( ",s," -- ",StripSpaces(RType()->Name(fullname))," )");
}


bool Type::operator==( cType& q ) const
{
	return Tid() == q.Tid() && BaseType() == q.BaseType();
}

bool BasicType::operator==( cType& q ) const
{
	return this == &q;
}

bool SignalType::operator==( cType& q ) const
{
	return this == &q;
}

bool EnumType::operator==( cType& q ) const
{
	return this == &q;
}

bool ProcType::operator==( cType& q ) const
{
	return q.isProcType() && ProcTypePtr(&q)->RType()==RType() && args==ProcTypePtr(&q)->args;
}

bool ArrayType::operator==( cType& q ) const
{
	return q.isArray() && ItemType()==ArrayTypePtr(&q)->ItemType() && InitialCount()==ArrayTypePtr(&q)->InitialCount();
}

bool RangeType::operator==( cType& q ) const
{
	return q.isRange() && q.BaseType() == basetype;
}

#if 0
bool StructType::operator==( cType& q ) const
{
	if(&q==this)
		return yes;
	else if( q.IsStruct() && q.BaseType()==BaseType() )
	{
		cSymbols& S1 = type_symbol->GetSymbols();
		cSymbols& S2 = StructTypePtr(&q)->type_symbol->GetSymbols();

		for(uint i=0; i<S1.count(); i++ )
		{
			Symbol* s1 = S1[i]; if(s1->Name()==tEMPTYSTR) continue;
			Symbol* s2 = S2.FindSymbol(s1->Name());

			switch(s1->isa())
			{
			case isa_var:	if(  s2 && *s1==*s2 ) continue; else return no;
			case isa_const:	if( !s2 || *s1==*s2 ) continue; else return no;
			case isa_proc:	if( !s2 || s2->isaProc() ) continue; else return no;
			case isa_type:	if( !s2 || *s1==*s2 ) continue; else return no;
			case isa_scope:	IERR();
			default:		IERR();
			}
		}

		for(uint i=0; i<S2.count(); i++ )
		{
			Symbol* s1 = S2[i]; if(s1->Name()==tEMPTYSTR) continue;
			Symbol* s2 = S1.FindSymbol(s1->Name());

			switch(s1->isa())
			{
			case isa_var:	if(  s2 && *s1==*s2 ) continue; else return no;
			case isa_const:	if( !s2 || *s1==*s2 ) continue; else return no;
			case isa_proc:	if( !s2 || s2->isaProc() ) continue; else return no;
			case isa_type:	if( !s2 || *s1==*s2 ) continue; else return no;
			case isa_scope:	IERR();
			default:		IERR();
			}
		}

		return yes;
	}
	else return no;
}
#endif



/* ********************************************************
				DEFAULT INIT, COPY, KILL
******************************************************** */



// TODO: default kill,grow,shrink,resize are stored as member function in the class'es Symbol
// default kill,grow,shrink,resize for arrays are stored in scope globals.implicit == cc::auto_symbols


/*
void StructType::get_copycode(Opcodes& opcodes) const			// ( T -- T )
{
	if(BaseClass())	BaseClass()->get_copycode(opcodes);

	cSymbols& symbols = GetMembers();
	for(uint i=0;i<symbols.count();i++)			// deep copy allocated items:
	{
		Symbol* s = symbols[i];
		if(s->isaVar()&&s->IsMember()&&VariablePtr(s)->Type()->isAllocated())
		{
			opcodes.append_dup();						// T T¢
			opcodes.append_vref(VariablePtr(s));		// T T.item&
			opcodes.append_dup();						// T T.item& T.item&
			opcodes.append_peek();						// T T.item& item¢
			opcodes.append_retain();					// T T.item& item		null-safe!
			opcodes.append_poke();						// T
		}
	}
}
*/

bool StructType::needs_killcode(bool deep) const
{
	cSymbols& symbols = GetMembers();
	for(uint i=0;i<symbols.count();i++)
	{
		Symbol* s = symbols[i];
		if(s->isaVar() && s->isMember())
		{
			if(VariablePtr(s)->Type()->kill_is_set() || VariablePtr(s)->isAllocated()) return yes;
		}
	}
	return deep && BaseClass() && BaseClass()->needs_killcode(true);
}


void StructType::get_killcode(Opcodes& opcodes) const			// ( T -- T )
{
	cSymbols& symbols = GetMembers();
	for(uint i=0;i<symbols.count();i++)			// deep copy allocated items:
	{
		Symbol* s = symbols[i];
		if( s->isaVar() && s->isMember() &&
			(VariablePtr(s)->isAllocated()||VariablePtr(s)->Type()->kill_is_set()) )
		{
			opcodes.append_dup();							// T T¢
			opcodes.append_vref(VariablePtr(s));			// T T.item&
			opcodes.append_peek();							// T T.item¢
//			opcodes.cast(VariablePtr(s)->Type());			// T T.item
			opcodes.Vstack().last()=VariablePtr(s)->Type();	// T T.item			cast ( T¢ -- T )
			opcodes.append_dispose();						// T
		}
	}

	if(BaseClass())
	//	BaseClass()->get_killcode(opcodes);
	{
		opcodes.Vstack().last() = BaseType();				// cast ( T -- T.BaseType )
		opcodes.append_callproc(BaseClass()->GetKill());
	}
}


// get procedure kill(T) which deconstructs but not deallocates an object:
// returns NULL if kill(T) has no action.
// the kill code needs not to be null-safe.
// it is called with a special call_if_not_null_and_not_shared
//
ProcSymbol* Type::GetKill() const			// kill ( T -- T )
{
	if(!kill && isAllocated())
	{
		if(isStruct())
		{
			if(!StructTypePtr(this)->needs_killcode(true)) return NULL;

			kill = cc::auto_symbols->AppendProcSymbol(tKILL,cTypes(this),this,0/*normal*/);

			if(StructTypePtr(this)->needs_killcode(false))
			{
				Opcodes opcodes;
				opcodes.vpush(this);
				StructTypePtr(this)->get_killcode(opcodes);
				assert(opcodes.types_cnt()==1);

				kill->Objcode() = opcodes;
		//		kill->SetFlag(inline_proc);

			}
			else	// kill is only needed for a base type:
			{
				kill->SetAlias(this->BaseType()->GetKill());
			}
		}

		else if(isArray() && ItemType()->isAllocated())
		{
			Opcodes opcodes;
			opcodes.vpush(this);

			uint la = cc::nextLabel();
			uint le = cc::nextLabel();					// T[]
			opcodes.append_dup();						// T[] T[]¢
			opcodes.append_forallitems(la,le);			// T[] T&		note: implicitely handles NULL
			opcodes.append_peek();						// T[] T¢
//			opcodes.cast(ItemType());					// T[] T
			opcodes.Vstack().last() = ItemType();		// T[] T 		cast T¢ to T
			opcodes.append_dispose();					// T[]
			opcodes.append_mmnextitem(la,le,ArrayTypePtr(this));

			assert(opcodes.types_cnt()==1);
			kill = cc::auto_symbols->AppendProcSymbol(tKILL,cTypes(this),this,0/*normal*/);
			kill->Objcode() = opcodes;
	//		kill->SetFlag(inline_proc);
		}
	}

	return kill;
}



/*	void shrink(T[] s, n)
		if T is allocated
			if s is shared
				s.shrink(n)
				retain s[to]
			else
				dispose s[n to]
				s.shrink(n)
		else
			s = s[0,n]
*/
ProcSymbol* ArrayType::GetShrink() const	// objects_can_move:	shrink( T[]¢ uint -- )		shrink object
{
	if(shrink) return shrink;

	assert(objects_can_move);

	shrink = cc::auto_symbols->AppendProcSymbol(tSHRINK,cTypes(CentSubtype(),tuint),tvoid,0/*normal*//*+inline_proc*/);
	Opcodes& opcodes = shrink->Objcode();
	assert(opcodes.Vstack()[opcodes.Vstack().count()-2]->isCentToArray());

	// non-allocated Items?
	if( ! ItemType()->isAllocated() )
	{
		opcodes.append_shrink();
		return shrink;
	}

	ProcSymbol* ps = ItemType()->GetKill();

	if(!ps)	// kein Destructor ?
	{
		assert( !(ItemType()->isArray() && ItemType()->ItemType()->isAllocated()) );
		if(ItemType()->isStruct()) logline("shrink: note: %s hat kein kill()", ItemType()->Name());

		opcodes.append_resize(tSHRINKSTRARRAY);
		return shrink;
	}


/*	shrink( T[]¢ uint -- )
		dup_2			//	T[]¢ uint T[]¢ uint
		midstr2end		//	T[]¢ uint T[…]
		for_range		//	T[]¢ uint T[…] T&
			peek		//	T[]¢ uint T[…] T¢
			cast		//	T[]¢ uint T[…] T
			kill		//	T[]¢ uint T[…] T
			dealloc		// 	T[]¢ uint T[…]
		mmnext			//	T[]¢ uint
		shrink			//	-
*/

	IFDEBUG( uint v0 = opcodes.types_cnt(); )
	IFDEBUG( uint r0 = opcodes.rtypes_cnt(); )

	uint la = cc::nextLabel();
	uint le = cc::nextLabel();

	opcodes.append_dup2_midstr();		// dup_2 + midstr2end
	opcodes.append_forrange(la,le);		// T[]¢ n T&
//	opcodes.append_peek_clear();		// T[]¢ n T
	opcodes.append_peek();				// T[]¢ n T¢
	assert(opcodes.last_type()->isCent());
	opcodes.Vstack().last() = opcodes.Vstack().last()->BaseType();	// T[]¢ n T		cast
	opcodes.append_dispose();			// kill + dealloc
	opcodes.append_mmnextitem(la,le,this);
	opcodes.append_shrink();

	assert(opcodes.types_cnt()==v0-2);
	assert(opcodes.rtypes_cnt()==r0);
	return shrink;
}



ProcSymbol* ArrayType::GetResize() const			// void resize(T[]¢,uint)
{
	if(resize) return resize;

	assert(objects_can_move);

	resize = cc::auto_symbols->AppendProcSymbol(tRESIZE,cTypes(CentSubtype(),tuint),tvoid,inline_proc+0/*normal*/);
	Opcodes& opcodes = resize->Objcode();
	assert(opcodes.Vstack()[opcodes.Vstack().count()-2]->isCentToArray());

	// non-allocated Items?
	if( ! ItemType()->isAllocated() )
	{
		opcodes.append_shrink();
		return resize;
	}

/*	resize( T[]¢ uint -- )
		dup_2			//	T[]¢ uint T[]¢ uint
		midstr2end		//	T[]¢ uint T[…]
		for_range		//	T[]¢ uint T[…] T&
			peek		//	T[]¢ uint T[…] T¢
			cast		//	T[]¢ uint T[…] T
			kill		//	T[]¢ uint T[…] T
			dealloc		// 	T[]¢ uint T[…]
		mmnext			//	T[]¢ uint
		resize			//	-
*/

	IFDEBUG( uint v0 = opcodes.types_cnt(); )
	IFDEBUG( uint r0 = opcodes.rtypes_cnt(); )

	uint la = cc::nextLabel();
	uint le = cc::nextLabel();

	opcodes.append_dup2_midstr();		// dup2 + midstr2end	// note: dup2 geht nicht als opcode,
	opcodes.append_forrange(la,le);		// T[]¢ n T&			//		 weil opcodes nur 1 rval haben können
	opcodes.append_peek();				// T[]¢ n T¢
	assert(opcodes.last_type()->isCent());
	opcodes.Vstack().last() = opcodes.Vstack().last()->BaseType();		// T[]¢ n T		cast
	opcodes.append_dispose();			// kill + dealloc
	opcodes.append_mmnextitem(la,le,this);
	opcodes.append_resize();

	assert(opcodes.types_cnt()==v0-2);
	assert(opcodes.rtypes_cnt()==r0);
	return resize;
}

#endif







































