view dmd2/mtype.c @ 1650:40bd4a0d4870

Update to work with LLVM 2.7. Removed use of dyn_cast, llvm no compiles without exceptions and rtti by default. We do need exceptions for the libconfig stuff, but rtti isn't necessary (anymore). Debug info needs to be rewritten, as in LLVM 2.7 the format has completely changed. To have something to look at while rewriting, the old code has been wrapped inside #ifndef DISABLE_DEBUG_INFO , this means that you have to define this to compile at the moment. Updated tango 0.99.9 patch to include updated EH runtime code, which is needed for LLVM 2.7 as well.
author Tomas Lindquist Olsen
date Wed, 19 May 2010 12:42:32 +0200
parents e4f7b5d9c68a
children
line wrap: on
line source


// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#define __C99FEATURES__ 1	// Needed on Solaris for NaN and more
#define __USE_ISOC99 1		// so signbit() gets defined

#if (defined (__SVR4) && defined (__sun))
#include <alloca.h>
#endif

#ifdef __DMC__
#include <math.h>
#else
#include <cmath>
#endif

#include <stdio.h>
#include <assert.h>
#include <float.h>

#if _MSC_VER
#include <malloc.h>
#include <complex>
#include <limits>
#elif __DMC__
#include <complex.h>
#elif __MINGW32__
#include <malloc.h>
#endif

#include "rmem.h"
#include "port.h"

#include "dsymbol.h"
#include "mtype.h"
#include "scope.h"
#include "init.h"
#include "expression.h"
#include "attrib.h"
#include "declaration.h"
#include "template.h"
#include "id.h"
#include "enum.h"
#include "import.h"
#include "aggregate.h"
#include "hdrgen.h"

#if IN_LLVM
//#include "gen/tollvm.h"
Ir* Type::sir = NULL;
unsigned GetTypeAlignment(Ir* ir, Type* t);
#endif

FuncDeclaration *hasThis(Scope *sc);


#define LOGDOTEXP	0	// log ::dotExp()
#define LOGDEFAULTINIT	0	// log ::defaultInit()

// Allow implicit conversion of T[] to T*
#define IMPLICIT_ARRAY_TO_PTR	global.params.useDeprecated

/* These have default values for 32 bit code, they get
 * adjusted for 64 bit code.
 */

int PTRSIZE = 4;

/* REALSIZE = size a real consumes in memory
 * REALPAD = 'padding' added to the CPU real size to bring it up to REALSIZE
 * REALALIGNSIZE = alignment for reals
 */
#if TARGET_OSX
int REALSIZE = 16;
int REALPAD = 6;
int REALALIGNSIZE = 16;
#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS
int REALSIZE = 12;
int REALPAD = 2;
int REALALIGNSIZE = 4;
#else
int REALSIZE = 10;
int REALPAD = 0;
int REALALIGNSIZE = 2;
#endif

int Tsize_t = Tuns32;
int Tptrdiff_t = Tint32;

#if _WIN32 && !defined __MINGW32__
static double zero = 0;
double Port::nan = NAN;
double Port::infinity = 1/zero;
#endif

/***************************** Type *****************************/

ClassDeclaration *Type::typeinfo;
ClassDeclaration *Type::typeinfoclass;
ClassDeclaration *Type::typeinfointerface;
ClassDeclaration *Type::typeinfostruct;
ClassDeclaration *Type::typeinfotypedef;
ClassDeclaration *Type::typeinfopointer;
ClassDeclaration *Type::typeinfoarray;
ClassDeclaration *Type::typeinfostaticarray;
ClassDeclaration *Type::typeinfoassociativearray;
ClassDeclaration *Type::typeinfoenum;
ClassDeclaration *Type::typeinfofunction;
ClassDeclaration *Type::typeinfodelegate;
ClassDeclaration *Type::typeinfotypelist;
ClassDeclaration *Type::typeinfoconst;
ClassDeclaration *Type::typeinfoinvariant;
ClassDeclaration *Type::typeinfoshared;

Type *Type::tvoidptr;
Type *Type::basic[TMAX];
unsigned char Type::mangleChar[TMAX];
unsigned char Type::sizeTy[TMAX];
StringTable Type::stringtable;

#if IN_LLVM
StringTable Type::deco_stringtable;
#endif


Type::Type(TY ty/*, Type *next*/)
{
    this->ty = ty;
    this->mod = 0;
    //this->next = next;
    this->deco = NULL;
#if DMDV2
    this->cto = NULL;
    this->ito = NULL;
    this->sto = NULL;
    this->scto = NULL;
#endif
    this->pto = NULL;
    this->rto = NULL;
    this->arrayof = NULL;
    this->vtinfo = NULL;
#if IN_DMD
    this->ctype = NULL;
#endif

#if IN_LLVM
    this->irtype = NULL;
#endif
}

Type *Type::syntaxCopy()
{
    print();
    fprintf(stdmsg, "ty = %d\n", ty);
    assert(0);
    return this;
}

int Type::equals(Object *o)
{   Type *t;

    t = (Type *)o;
    //printf("Type::equals(%s, %s)\n", toChars(), t->toChars());
    if (this == o ||
	(t && deco == t->deco) &&		// deco strings are unique
	 deco != NULL)				// and semantic() has been run
    {
	//printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
	return 1;
    }
    //if (deco && t && t->deco) printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
    return 0;
}

char Type::needThisPrefix()
{
    return 'M';		// name mangling prefix for functions needing 'this'
}

#if IN_LLVM
void Type::init(Ir* _sir)
#else
void Type::init()
#endif
{   int i;
    int j;

    Lexer::initKeywords();

    for (i = 0; i < TMAX; i++)
	sizeTy[i] = sizeof(TypeBasic);
    sizeTy[Tsarray] = sizeof(TypeSArray);
    sizeTy[Tarray] = sizeof(TypeDArray);
    //sizeTy[Tnarray] = sizeof(TypeNArray);
    sizeTy[Taarray] = sizeof(TypeAArray);
    sizeTy[Tpointer] = sizeof(TypePointer);
    sizeTy[Treference] = sizeof(TypeReference);
    sizeTy[Tfunction] = sizeof(TypeFunction);
    sizeTy[Tdelegate] = sizeof(TypeDelegate);
    sizeTy[Tident] = sizeof(TypeIdentifier);
    sizeTy[Tinstance] = sizeof(TypeInstance);
    sizeTy[Ttypeof] = sizeof(TypeTypeof);
    sizeTy[Tenum] = sizeof(TypeEnum);
    sizeTy[Ttypedef] = sizeof(TypeTypedef);
    sizeTy[Tstruct] = sizeof(TypeStruct);
    sizeTy[Tclass] = sizeof(TypeClass);
    sizeTy[Ttuple] = sizeof(TypeTuple);
    sizeTy[Tslice] = sizeof(TypeSlice);
    sizeTy[Treturn] = sizeof(TypeReturn);

    mangleChar[Tarray] = 'A';
    mangleChar[Tsarray] = 'G';
    mangleChar[Tnarray] = '@';
    mangleChar[Taarray] = 'H';
    mangleChar[Tpointer] = 'P';
    mangleChar[Treference] = 'R';
    mangleChar[Tfunction] = 'F';
    mangleChar[Tident] = 'I';
    mangleChar[Tclass] = 'C';
    mangleChar[Tstruct] = 'S';
    mangleChar[Tenum] = 'E';
    mangleChar[Ttypedef] = 'T';
    mangleChar[Tdelegate] = 'D';

    mangleChar[Tnone] = 'n';
    mangleChar[Tvoid] = 'v';
    mangleChar[Tint8] = 'g';
    mangleChar[Tuns8] = 'h';
    mangleChar[Tint16] = 's';
    mangleChar[Tuns16] = 't';
    mangleChar[Tint32] = 'i';
    mangleChar[Tuns32] = 'k';
    mangleChar[Tint64] = 'l';
    mangleChar[Tuns64] = 'm';
    mangleChar[Tfloat32] = 'f';
    mangleChar[Tfloat64] = 'd';
    mangleChar[Tfloat80] = 'e';

    mangleChar[Timaginary32] = 'o';
    mangleChar[Timaginary64] = 'p';
    mangleChar[Timaginary80] = 'j';
    mangleChar[Tcomplex32] = 'q';
    mangleChar[Tcomplex64] = 'r';
    mangleChar[Tcomplex80] = 'c';

    mangleChar[Tbool] = 'b';
    mangleChar[Tascii] = 'a';
    mangleChar[Twchar] = 'u';
    mangleChar[Tdchar] = 'w';

    mangleChar[Tbit] = '@';
    mangleChar[Tinstance] = '@';
    mangleChar[Terror] = '@';
    mangleChar[Ttypeof] = '@';
    mangleChar[Ttuple] = 'B';
    mangleChar[Tslice] = '@';
    mangleChar[Treturn] = '@';

    for (i = 0; i < TMAX; i++)
    {	if (!mangleChar[i])
	    fprintf(stdmsg, "ty = %d\n", i);
	assert(mangleChar[i]);
    }

    // Set basic types
    static TY basetab[] =
	{ Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64,
	  Tfloat32, Tfloat64, Tfloat80,
	  Timaginary32, Timaginary64, Timaginary80,
	  Tcomplex32, Tcomplex64, Tcomplex80,
	  Tbool,
	  Tascii, Twchar, Tdchar };

    for (i = 0; i < sizeof(basetab) / sizeof(basetab[0]); i++)
    {	Type *t = new TypeBasic(basetab[i]);
	t = t->merge();
	basic[basetab[i]] = t;
    }
    basic[Terror] = basic[Tint32];

    tvoidptr = tvoid->pointerTo();

    // LDC
    sir = _sir;

    // set size_t / ptrdiff_t types and pointer size
    if (global.params.is64bit)
    {
	Tsize_t = Tuns64;
	Tptrdiff_t = Tint64;
	PTRSIZE = 8;
    }
    else
    {
	PTRSIZE = 4;
#if TARGET_OSX
	REALSIZE = 16;
	REALPAD = 6;
#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS
	REALSIZE = 12;
	REALPAD = 2;
#else
	REALSIZE = 10;
	REALPAD = 0;
#endif
	Tsize_t = Tuns32;
	Tptrdiff_t = Tint32;
	PTRSIZE = 4;
    }

    // set real size and padding
    if (global.params.cpu == ARCHx86)
    {
	REALSIZE = 12;
	REALPAD = 2;
    }
    else if (global.params.cpu == ARCHx86_64)
    {
	REALSIZE = 16;
	REALPAD = 6;
    }
    else
    {
	REALSIZE = 8;
	REALPAD = 0;
    }
}

d_uns64 Type::size()
{
    return size(0);
}

d_uns64 Type::size(Loc loc)
{
    error(loc, "no size for type %s", toChars());
    return 1;
}

unsigned Type::alignsize()
{
    return size(0);
}

Type *Type::semantic(Loc loc, Scope *sc)
{
    return merge();
}

Type *Type::trySemantic(Loc loc, Scope *sc)
{
    unsigned errors = global.errors;
    global.gag++;			// suppress printing of error messages
    Type *t = semantic(loc, sc);
    global.gag--;
    if (errors != global.errors)	// if any errors happened
    {
	global.errors = errors;
	t = NULL;
    }
    return t;
}

/*******************************
 * Determine if converting 'this' to 'to' is an identity operation,
 * a conversion to const operation, or the types aren't the same.
 * Returns:
 *	MATCHequal	'this' == 'to'
 *	MATCHconst	'to' is const
 *	MATCHnomatch	conversion to mutable or invariant
 */

MATCH Type::constConv(Type *to)
{
    if (equals(to))
	return MATCHexact;
    if (ty == to->ty && to->mod == MODconst)
	return MATCHconst;
    return MATCHnomatch;
}

/********************************
 * Convert to 'const'.
 */

Type *Type::constOf()
{
#if 0
    //printf("Type::constOf() %p %s\n", this, toChars());
    if (isConst())
	return this;
    if (cto)
	return cto;
    Type *t = makeConst();
    t = t->merge();
    cto = t;
    if (ito)
	ito->cto = t;
    //if (t->nextOf()) assert(t->nextOf()->isConst());
    //printf("-Type::constOf() %p %s\n", t, toChars());
    return t;
#else
    //printf("Type::constOf() %p %s\n", this, toChars());
    if (mod == MODconst)
	return this;
    if (cto)
    {	assert(cto->mod == MODconst);
	return cto;
    }
    Type *t = makeConst();
    t = t->merge();
    t->fixTo(this);
    //printf("-Type::constOf() %p %s\n", t, toChars());
    return t;
#endif
}

/********************************
 * Convert to 'immutable'.
 */

Type *Type::invariantOf()
{
#if 0
    //printf("Type::invariantOf() %p %s\n", this, toChars());
    if (isInvariant())
    {
	return this;
    }
    if (ito)
    {
	//if (!ito->isInvariant()) printf("\tito is %p %s\n", ito, ito->toChars());
	assert(ito->isInvariant());
	return ito;
    }
    Type *t = makeInvariant();
    t = t->merge();
    ito = t;
    if (cto)
	cto->ito = t;
#if 0 // fails for function types
    if (t->nextOf() && !t->nextOf()->isInvariant())
    {
	assert(0);
    }
#endif
    //printf("\t%p\n", t);
    return t;
#else
    //printf("Type::invariantOf() %p %s\n", this, toChars());
    if (isInvariant())
    {
	return this;
    }
    if (ito)
    {
	assert(ito->isInvariant());
	return ito;
    }
    Type *t = makeInvariant();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p\n", t);
    return t;
#endif
}

/********************************
 * Make type mutable.
 */

Type *Type::mutableOf()
{
#if 0
    //printf("Type::mutableOf() %p, %s\n", this, toChars());
    Type *t = this;
    if (isConst())
    {	t = cto;
	assert(!t || t->isMutable());
    }
    else if (isInvariant())
    {	t = ito;
	assert(!t || t->isMutable());
    }
    if (!t)
    {
	unsigned sz = sizeTy[ty];
	t = (Type *)mem.malloc(sz);
	memcpy(t, this, sz);
	t->mod = 0;
	t->deco = NULL;
	t->arrayof = NULL;
	t->pto = NULL;
	t->rto = NULL;
	t->cto = NULL;
	t->ito = NULL;
	t->sto = NULL;
	t->scto = NULL;
	t->vtinfo = NULL;
	if (ty == Tsarray)
	{   TypeSArray *ta = (TypeSArray *)t;
	    //ta->next = ta->next->mutableOf();
	}
	t = t->merge();
	if (isConst())
	{   cto = t;
	    t->cto = this;
	    if (ito)
		ito->cto = this;
	}
	else if (isInvariant())
	{   ito = t;
	    t->ito = this;
	    if (cto)
		cto->ito = this;
	}
    }
    return t;
#else
    //printf("Type::mutableOf() %p, %s\n", this, toChars());
    Type *t = this;
    if (isConst())
    {	if (isShared())
	    t = sto;		// shared const => shared
	else
	    t = cto;
	assert(!t || t->isMutable());
    }
    else if (isInvariant())
    {	t = ito;
	assert(!t || (t->isMutable() && !t->isShared()));
    }
    if (!t)
    {
	unsigned sz = sizeTy[ty];
	t = (Type *)mem.malloc(sz);
	memcpy(t, this, sz);
	t->mod = 0;
	t->deco = NULL;
	t->arrayof = NULL;
	t->pto = NULL;
	t->rto = NULL;
	t->cto = NULL;
	t->ito = NULL;
	t->sto = NULL;
	t->scto = NULL;
	t->vtinfo = NULL;
	t = t->merge();

	t->fixTo(this);

	switch (mod)
	{
	    case MODconst:
		t->cto = this;
		break;

	    case MODinvariant:
		t->ito = this;
		break;

	    case MODshared:
		t->sto = this;
		break;

	    case MODshared | MODconst:
		t->scto = this;
		break;

	    default:
		assert(0);
	}
    }
    return t;
#endif
}

Type *Type::sharedOf()
{
    //printf("Type::sharedOf() %p, %s\n", this, toChars());
    if (mod == MODshared)
    {
	return this;
    }
    if (sto)
    {
	assert(sto->isShared());
	return sto;
    }
    Type *t = makeShared();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p\n", t);
    return t;
}

Type *Type::sharedConstOf()
{
    //printf("Type::sharedConstOf() %p, %s\n", this, toChars());
    if (mod == (MODshared | MODconst))
    {
	return this;
    }
    if (scto)
    {
	assert(scto->mod == (MODshared | MODconst));
	return scto;
    }
    Type *t = makeSharedConst();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p\n", t);
    return t;
}


/**********************************
 * For our new type 'this', which is type-constructed from t,
 * fill in the cto, ito, sto, scto shortcuts.
 */

void Type::fixTo(Type *t)
{
    ito = t->ito;
#if 0
    /* Cannot do these because these are not fully transitive:
     * there can be a shared ptr to immutable, for example.
     * Immutable subtypes are always immutable, though.
     */
    cto = t->cto;
    sto = t->sto;
    scto = t->scto;
#endif

    assert(mod != t->mod);
#define X(m, n) (((m) << 3) | (n))
    switch (X(mod, t->mod))
    {
	case X(0, MODconst):
	    cto = t;
	    break;

	case X(0, MODinvariant):
	    ito = t;
	    break;

	case X(0, MODshared):
	    sto = t;
	    break;

	case X(0, MODshared | MODconst):
	    scto = t;
	    break;


	case X(MODconst, 0):
	    cto = NULL;
	    goto L2;

	case X(MODconst, MODinvariant):
	    ito = t;
	    goto L2;

	case X(MODconst, MODshared):
	    sto = t;
	    goto L2;

	case X(MODconst, MODshared | MODconst):
	    scto = t;
	L2:
	    t->cto = this;
	    break;


	case X(MODinvariant, 0):
	    ito = NULL;
	    goto L3;

	case X(MODinvariant, MODconst):
	    cto = t;
	    goto L3;

	case X(MODinvariant, MODshared):
	    sto = t;
	    goto L3;

	case X(MODinvariant, MODshared | MODconst):
	    scto = t;
	L3:
	    t->ito = this;
	    if (t->cto) t->cto->ito = this;
	    if (t->sto) t->sto->ito = this;
	    if (t->scto) t->scto->ito = this;
	    break;


	case X(MODshared, 0):
	    sto = NULL;
	    goto L4;

	case X(MODshared, MODconst):
	    cto = t;
	    goto L4;

	case X(MODshared, MODinvariant):
	    ito = t;
	    goto L4;

	case X(MODshared, MODshared | MODconst):
	    scto = t;
	L4:
	    t->sto = this;
	    break;


	case X(MODshared | MODconst, 0):
	    scto = NULL;
	    break;

	case X(MODshared | MODconst, MODconst):
	    cto = t;
	    break;

	case X(MODshared | MODconst, MODinvariant):
	    ito = t;
	    break;

	case X(MODshared | MODconst, MODshared):
	    sto = t;
	L5:
	    t->scto = this;
	    break;

	default:
	    assert(0);
    }
#undef X

    check();
    t->check();
    //printf("fixTo: %s, %s\n", toChars(), t->toChars());
}

/***************************
 * Look for bugs in constructing types.
 */

void Type::check()
{
    switch (mod)
    {
	case 0:
	    if (cto) assert(cto->mod == MODconst);
	    if (ito) assert(ito->mod == MODinvariant);
	    if (sto) assert(sto->mod == MODshared);
	    if (scto) assert(scto->mod == (MODshared | MODconst));
	    break;

	case MODconst:
	    if (cto) assert(cto->mod == 0);
	    if (ito) assert(ito->mod == MODinvariant);
	    if (sto) assert(sto->mod == MODshared);
	    if (scto) assert(scto->mod == (MODshared | MODconst));
	    break;

	case MODinvariant:
	    if (cto) assert(cto->mod == MODconst);
	    if (ito) assert(ito->mod == 0);
	    if (sto) assert(sto->mod == MODshared);
	    if (scto) assert(scto->mod == (MODshared | MODconst));
	    break;

	case MODshared:
	    if (cto) assert(cto->mod == MODconst);
	    if (ito) assert(ito->mod == MODinvariant);
	    if (sto) assert(sto->mod == 0);
	    if (scto) assert(scto->mod == (MODshared | MODconst));
	    break;

	case MODshared | MODconst:
	    if (cto) assert(cto->mod == MODconst);
	    if (ito) assert(ito->mod == MODinvariant);
	    if (sto) assert(sto->mod == MODshared);
	    if (scto) assert(scto->mod == 0);
	    break;

	default:
	    assert(0);
    }

    Type *tn = nextOf();
    if (tn && ty != Tfunction && ty != Tdelegate)
    {	// Verify transitivity
	switch (mod)
	{
	    case 0:
		break;

	    case MODconst:
		assert(tn->mod & MODinvariant || tn->mod & MODconst);
		break;

	    case MODinvariant:
		assert(tn->mod == MODinvariant);
		break;

	    case MODshared:
		assert(tn->mod & MODinvariant || tn->mod & MODshared);
		break;

	    case MODshared | MODconst:
		assert(tn->mod & MODinvariant || tn->mod & (MODshared | MODconst));
		break;

	    default:
		assert(0);
	}
	tn->check();
    }
}

Type *Type::makeConst()
{
    //printf("Type::makeConst() %p, %s\n", this, toChars());
    if (cto)
	return cto;
    unsigned sz = sizeTy[ty];
    Type *t = (Type *)mem.malloc(sz);
    memcpy(t, this, sz);
    t->mod = MODconst;
    t->deco = NULL;
    t->arrayof = NULL;
    t->pto = NULL;
    t->rto = NULL;
    t->cto = NULL;
    t->ito = NULL;
    t->sto = NULL;
    t->scto = NULL;
    t->vtinfo = NULL;
    //printf("-Type::makeConst() %p, %s\n", t, toChars());
    return t;
}

Type *Type::makeInvariant()
{
    if (ito)
	return ito;
    unsigned sz = sizeTy[ty];
    Type *t = (Type *)mem.malloc(sz);
    memcpy(t, this, sz);
    t->mod = MODinvariant;
    t->deco = NULL;
    t->arrayof = NULL;
    t->pto = NULL;
    t->rto = NULL;
    t->cto = NULL;
    t->ito = NULL;
    t->sto = NULL;
    t->scto = NULL;
    t->vtinfo = NULL;
    return t;
}

Type *Type::makeShared()
{
    if (sto)
	return sto;
    unsigned sz = sizeTy[ty];
    Type *t = (Type *)mem.malloc(sz);
    memcpy(t, this, sz);
    t->mod = MODshared;
    t->deco = NULL;
    t->arrayof = NULL;
    t->pto = NULL;
    t->rto = NULL;
    t->cto = NULL;
    t->ito = NULL;
    t->sto = NULL;
    t->scto = NULL;
    t->vtinfo = NULL;
    return t;
}

Type *Type::makeSharedConst()
{
    if (scto)
	return scto;
    unsigned sz = sizeTy[ty];
    Type *t = (Type *)mem.malloc(sz);
    memcpy(t, this, sz);
    t->mod = MODshared | MODconst;
    t->deco = NULL;
    t->arrayof = NULL;
    t->pto = NULL;
    t->rto = NULL;
    t->cto = NULL;
    t->ito = NULL;
    t->sto = NULL;
    t->scto = NULL;
    t->vtinfo = NULL;
    return t;
}

/************************************
 * Apply MODxxxx bits to existing type.
 */

Type *Type::castMod(unsigned mod)
{   Type *t;

    switch (mod)
    {
	case 0:
	    t = mutableOf();
	    break;

	case MODconst:
	    t = constOf();
	    break;

	case MODinvariant:
	    t = invariantOf();
	    break;

	case MODshared:
	    t = sharedOf();
	    break;

	case MODshared | MODconst:
	    t = sharedConstOf();
	    break;

	default:
	    assert(0);
    }
    return t;
}

/************************************
 * Add MODxxxx bits to existing type.
 * We're adding, not replacing, so adding const to
 * a shared type => "shared const"
 */

Type *Type::addMod(unsigned mod)
{   Type *t = this;

    /* Add anything to immutable, and it remains immutable
     */
    if (!t->isInvariant())
    {
	switch (mod)
	{
	    case 0:
		break;

	    case MODconst:
		if (isShared())
		    t = sharedConstOf();
		else
		    t = constOf();
		break;

	    case MODinvariant:
		t = invariantOf();
		break;

	    case MODshared:
		if (isConst())
		    t = sharedConstOf();
		else
		    t = sharedOf();
		break;

	    case MODshared | MODconst:
		t = sharedConstOf();
		break;

	    default:
		assert(0);
	}
    }
    return t;
}

/************************************
 * Add storage class modifiers to type.
 */

Type *Type::addStorageClass(unsigned stc)
{
    /* Just translate to MOD bits and let addMod() do the work
     */
    unsigned mod = 0;

    if (stc & STCimmutable)
	mod = MODinvariant;
    else
    {	if (stc & (STCconst | STCin))
	    mod = MODconst;
	if (stc & STCshared)
	    mod |= MODshared;
    }
    return addMod(mod);
}

/**************************
 * Return type with the top level of it being mutable.
 */
Type *Type::toHeadMutable()
{
    if (!mod)
	return this;
    return mutableOf();
}

Type *Type::pointerTo()
{
    if (!pto)
    {	Type *t;

	t = new TypePointer(this);
	pto = t->merge();
    }
    return pto;
}

Type *Type::referenceTo()
{
    if (!rto)
    {	Type *t;

	t = new TypeReference(this);
	rto = t->merge();
    }
    return rto;
}

Type *Type::arrayOf()
{
    if (!arrayof)
    {	Type *t;

	t = new TypeDArray(this);
	arrayof = t->merge();
    }
    return arrayof;
}

Dsymbol *Type::toDsymbol(Scope *sc)
{
    return NULL;
}

/*******************************
 * If this is a shell around another type,
 * get that other type.
 */

Type *Type::toBasetype()
{
    return this;
}

/********************************
 * Name mangling.
 * Input:
 *	flag	0x100	do not do const/invariant
 */

void Type::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) // Possible conflict from merge
{
    if (flag != mod && flag != 0x100)
    {
	if (mod & MODshared)
	    buf->writeByte('O');

	if (mod & MODconst)
	    buf->writeByte('x');
	else if (mod & MODinvariant)
	    buf->writeByte('y');

	// Cannot be both const and invariant
	assert((mod & (MODconst | MODinvariant)) != (MODconst | MODinvariant));
    }
    buf->writeByte(mangleChar[ty]);
}

/********************************
 * For pretty-printing a type.
 */

char *Type::toChars()
{   OutBuffer *buf;
    HdrGenState hgs;

    buf = new OutBuffer();
    toCBuffer(buf, NULL, &hgs);
    return buf->toChars();
}

void Type::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
{
    toCBuffer2(buf, hgs, 0);
    if (ident)
    {	buf->writeByte(' ');
	buf->writestring(ident->toChars());
    }
}

void Type::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring(toChars());
}

void Type::toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	const char *p;

	if (this->mod & MODshared)
	    buf->writestring("shared(");
	switch (this->mod & (MODconst | MODinvariant))
	{
	    case 0:
		toCBuffer2(buf, hgs, this->mod);
		break;
	    case MODconst:
		p = "const(";
		goto L1;
	    case MODinvariant:
		p = "immutable(";
	    L1:	buf->writestring(p);
		toCBuffer2(buf, hgs, this->mod);
		buf->writeByte(')');
		break;
	    default:
		assert(0);
	}
	if (this->mod & MODshared)
	    buf->writeByte(')');
    }
}

void Type::modToBuffer(OutBuffer *buf)
{
    if (mod & MODshared)
        buf->writestring(" shared");
    if (mod & MODconst)
        buf->writestring(" const");
    if (mod & MODinvariant)
        buf->writestring(" immutable");
}

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

Type *Type::merge()
{   Type *t;

    //printf("merge(%s)\n", toChars());
    t = this;
    assert(t);
    if (!deco)
    {
	OutBuffer buf;
	StringValue *sv;

	//if (next)
	    //next = next->merge();
	toDecoBuffer(&buf, false);
	sv = stringtable.update((char *)buf.data, buf.offset);
	if (sv->ptrvalue)
	{   t = (Type *) sv->ptrvalue;
#ifdef DEBUG
	    if (!t->deco)
		printf("t = %s\n", t->toChars());
#endif
	    assert(t->deco);
	    //printf("old value, deco = '%s' %p\n", t->deco, t->deco);
	}
	else
	{
	    sv->ptrvalue = this;

            // we still need deco strings to be unique
            // or Type::equals fails, which breaks a bunch of stuff,
            // like covariant member function overloads.
	    OutBuffer mangle;
	    toDecoBuffer(&mangle, true);
	    StringValue* sv2 = deco_stringtable.update((char *)mangle.data, mangle.offset);
	    if (sv2->ptrvalue)
	    {  Type* t2 = (Type *) sv2->ptrvalue;
	       assert(t2->deco);
	       deco = t2->deco;
	    }
	    else
	    {
	       sv2->ptrvalue = this;
	       deco = (char *)sv2->lstring.string;
	    }
	    //printf("new value, deco = '%s' %p\n", t->deco, t->deco);
	}
    }
    return t;
}

/*************************************
 * This version does a merge even if the deco is already computed.
 * Necessary for types that have a deco, but are not merged.
 */
Type *Type::merge2()
{
    //printf("merge2(%s)\n", toChars());
    Type *t = this;
    assert(t);
    if (!t->deco)
	return t->merge();

    StringValue *sv = deco_stringtable.lookup((char *)t->deco, strlen(t->deco));
    if (sv && sv->ptrvalue)
    {   t = (Type *) sv->ptrvalue;
	assert(t->deco);
    }
    else
	assert(0);
    return t;
}

int Type::isintegral()
{
    return FALSE;
}

int Type::isfloating()
{
    return FALSE;
}

int Type::isreal()
{
    return FALSE;
}

int Type::isimaginary()
{
    return FALSE;
}

int Type::iscomplex()
{
    return FALSE;
}

int Type::isscalar()
{
    return FALSE;
}

int Type::isunsigned()
{
    return FALSE;
}

ClassDeclaration *Type::isClassHandle()
{
    return NULL;
}

int Type::isauto()
{
    return FALSE;
}

int Type::isString()
{
    return FALSE;
}

/**************************
 * Given:
 *	T a, b;
 * Can we assign:
 *	a = b;
 * ?
 */
int Type::isAssignable()
{
    return TRUE;
}

int Type::checkBoolean()
{
    return isscalar();
}

/*********************************
 * Check type to see if it is based on a deprecated symbol.
 */

void Type::checkDeprecated(Loc loc, Scope *sc)
{
    Dsymbol *s = toDsymbol(sc);

    if (s)
	s->checkDeprecated(loc, sc);
}


Expression *Type::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("Type::defaultInit() '%s'\n", toChars());
#endif
    return NULL;
}

int Type::isZeroInit(Loc loc)
{
    return 0;		// assume not
}

int Type::isBaseOf(Type *t, int *poffset)
{
    return 0;		// assume not
}

/********************************
 * Determine if 'this' can be implicitly converted
 * to type 'to'.
 * Returns:
 *	MATCHnomatch, MATCHconvert, MATCHconst, MATCHexact
 */

MATCH Type::implicitConvTo(Type *to)
{
    //printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to);
    //printf("from: %s\n", toChars());
    //printf("to  : %s\n", to->toChars());
    if (this == to)
	return MATCHexact;
    return MATCHnomatch;
}

Expression *Type::getProperty(Loc loc, Identifier *ident)
{   Expression *e;

#if LOGDOTEXP
    printf("Type::getProperty(type = '%s', ident = '%s')\n", toChars(), ident->toChars());
#endif
    if (ident == Id::__sizeof)
    {
	e = new IntegerExp(loc, size(loc), Type::tsize_t);
    }
    else if (ident == Id::size)
    {
	error(loc, ".size property should be replaced with .sizeof");
	e = new ErrorExp();
    }
    else if (ident == Id::alignof)
    {
	e = new IntegerExp(loc, alignsize(), Type::tsize_t);
    }
    else if (ident == Id::typeinfo)
    {
	if (!global.params.useDeprecated)
	    error(loc, ".typeinfo deprecated, use typeid(type)");
	e = getTypeInfo(NULL);
    }
    else if (ident == Id::init)
    {
	if (ty == Tvoid)
	    error(loc, "void does not have an initializer");
	e = defaultInit(loc);
    }
    else if (ident == Id::mangleof)
    {	const char *s;
	if (!deco)
	{   s = toChars();
	    error(loc, "forward reference of type %s.mangleof", s);
	}
	else
	    s = deco;
	e = new StringExp(loc, (char *)s, strlen(s), 'c');
	Scope sc;
	e = e->semantic(&sc);
    }
    else if (ident == Id::stringof)
    {	char *s = toChars();
	e = new StringExp(loc, s, strlen(s), 'c');
	Scope sc;
	e = e->semantic(&sc);
    }
    else
    {
	error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
	e = new ErrorExp();
    }
    return e;
}

Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident)
{   VarDeclaration *v = NULL;

#if LOGDOTEXP
    printf("Type::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (e->op == TOKdotvar)
    {
	DotVarExp *dv = (DotVarExp *)e;
	v = dv->var->isVarDeclaration();
    }
    else if (e->op == TOKvar)
    {
	VarExp *ve = (VarExp *)e;
	v = ve->var->isVarDeclaration();
    }
    if (v)
    {
	if (ident == Id::offset)
	{
	    if (!global.params.useDeprecated)
		error(e->loc, ".offset deprecated, use .offsetof");
	    goto Loffset;
	}
	else if (ident == Id::offsetof)
	{
	  Loffset:
	    if (v->storage_class & STCfield)
	    {
		e = new IntegerExp(e->loc, v->offset, Type::tsize_t);
		return e;
	    }
	}
	else if (ident == Id::init)
	{
#if 0
	    if (v->init)
	    {
		if (v->init->isVoidInitializer())
		    error(e->loc, "%s.init is void", v->toChars());
		else
		{   Loc loc = e->loc;
		    e = v->init->toExpression();
		    if (e->op == TOKassign || e->op == TOKconstruct || e->op == TOKblit)
		    {
			e = ((AssignExp *)e)->e2;

			/* Take care of case where we used a 0
			 * to initialize the struct.
			 */
			if (e->type == Type::tint32 &&
			    e->isBool(0) &&
			    v->type->toBasetype()->ty == Tstruct)
			{
			    e = v->type->defaultInit(e->loc);
			}
		    }
		    e = e->optimize(WANTvalue | WANTinterpret);
//		    if (!e->isConst())
//			error(loc, ".init cannot be evaluated at compile time");
		}
		return e;
	    }
#endif
	    Expression *ex = defaultInit(e->loc);
	    return ex;
	}
    }
    if (ident == Id::typeinfo)
    {
	if (!global.params.useDeprecated)
	    error(e->loc, ".typeinfo deprecated, use typeid(type)");
	e = getTypeInfo(sc);
	return e;
    }
    if (ident == Id::stringof)
    {	char *s = e->toChars();
	e = new StringExp(e->loc, s, strlen(s), 'c');
	Scope sc;
	e = e->semantic(&sc);
	return e;
    }
    return getProperty(e->loc, ident);
}

unsigned Type::memalign(unsigned salign)
{
    return salign;
}

void Type::error(Loc loc, const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    ::verror(loc, format, ap);
    va_end( ap );
}

void Type::warning(Loc loc, const char *format, ...)
{
    if (global.params.warnings && !global.gag)
    {
	va_list ap;
	va_start(ap, format);
	::vwarning(loc, format, ap);
	va_end( ap );
    }
}

Identifier *Type::getTypeInfoIdent(int internal)
{
    // _init_10TypeInfo_%s
    OutBuffer buf;
    Identifier *id;
    char *name;
    int len;

    if (internal)
    {	buf.writeByte(mangleChar[ty]);
	if (ty == Tarray)
	    buf.writeByte(mangleChar[((TypeArray *)this)->next->ty]);
    }
    else
	toDecoBuffer(&buf, true);
    len = buf.offset;
    name = (char *)alloca(19 + sizeof(len) * 3 + len + 1);
    buf.writeByte(0);
#if TARGET_OSX
    // The LINKc will prepend the _
    sprintf(name, "D%dTypeInfo_%s6__initZ", 9 + len, buf.data);
#else
    sprintf(name, "_D%dTypeInfo_%s6__initZ", 9 + len, buf.data);
#endif
// LDC
// it is not clear where the underscore that's stripped here is added back in
//    if (global.params.isWindows)
//	name++;			// C mangling will add it back in
    //printf("name = %s\n", name);
    id = Lexer::idPool(name);
    return id;
}

TypeBasic *Type::isTypeBasic()
{
    return NULL;
}


void Type::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
    //printf("Type::resolve() %s, %d\n", toChars(), ty);
    Type *t = semantic(loc, sc);
    *pt = t;
    *pe = NULL;
    *ps = NULL;
}

/*******************************
 * If one of the subtypes of this type is a TypeIdentifier,
 * i.e. it's an unresolved type, return that type.
 */

Type *Type::reliesOnTident()
{
    return NULL;
}

/********************************
 * We've mistakenly parsed this as a type.
 * Redo it as an Expression.
 * NULL if cannot.
 */

Expression *Type::toExpression()
{
    return NULL;
}

/***************************************
 * Return !=0 if type has pointers that need to
 * be scanned by the GC during a collection cycle.
 */

int Type::hasPointers()
{
    return FALSE;
}

/*************************************
 * If this is a type of something, return that something.
 */

Type *Type::nextOf()
{
    return NULL;
}

/****************************************
 * Return the mask that an integral type will
 * fit into.
 */
uinteger_t Type::sizemask()
{   uinteger_t m;

    switch (toBasetype()->ty)
    {
	case Tbool:	m = 1;				break;
	case Tchar:
	case Tint8:
	case Tuns8:	m = 0xFF;			break;
	case Twchar:
	case Tint16:
	case Tuns16:	m = 0xFFFFUL;			break;
	case Tdchar:
	case Tint32:
	case Tuns32:	m = 0xFFFFFFFFUL;		break;
	case Tint64:
	case Tuns64:	m = 0xFFFFFFFFFFFFFFFFULL;	break;
	default:
		assert(0);
    }
    return m;
}

/* ============================= TypeNext =========================== */

TypeNext::TypeNext(TY ty, Type *next)
	: Type(ty)
{
    this->next = next;
}

void TypeNext::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    Type::toDecoBuffer(buf, flag, mangle);
    assert(next != this);
    //printf("this = %p, ty = %d, next = %p, ty = %d\n", this, this->ty, next, next->ty);
    next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}

void TypeNext::checkDeprecated(Loc loc, Scope *sc)
{
    Type::checkDeprecated(loc, sc);
    if (next)	// next can be NULL if TypeFunction and auto return type
	next->checkDeprecated(loc, sc);
}


Type *TypeNext::reliesOnTident()
{
    return next->reliesOnTident();
}

/*******************************
 * For TypeFunction, nextOf() can return NULL if the function return
 * type is meant to be inferred, and semantic() hasn't yet ben run
 * on the function. After semantic(), it must no longer be NULL.
 */

Type *TypeNext::nextOf()
{
    return next;
}

Type *TypeNext::makeConst()
{
    //printf("TypeNext::makeConst() %p, %s\n", this, toChars());
    if (cto)
    {	assert(cto->mod == MODconst);
	return cto;
    }    
    TypeNext *t = (TypeNext *)Type::makeConst();
    if (ty != Tfunction && ty != Tdelegate &&
	(next->deco || next->ty == Tfunction) &&
        !next->isInvariant() && !next->isConst())
    {	if (next->isShared())
	    t->next = next->sharedConstOf();
	else
	    t->next = next->constOf();
    }
    //printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeInvariant()
{
    //printf("TypeNext::makeInvariant() %s\n", toChars());
    if (ito)
    {	assert(ito->isInvariant());
	return ito;
    }
    TypeNext *t = (TypeNext *)Type::makeInvariant();
    if (ty != Tfunction && ty != Tdelegate &&
	(next->deco || next->ty == Tfunction) &&
	!next->isInvariant())
    {	t->next = next->invariantOf();
    }
    return t;
}

Type *TypeNext::makeShared()
{
    //printf("TypeNext::makeShared() %s\n", toChars());
    if (sto)
    {	assert(sto->mod == MODshared);
	return sto;
    }    
    TypeNext *t = (TypeNext *)Type::makeShared();
    if (ty != Tfunction && ty != Tdelegate &&
	(next->deco || next->ty == Tfunction) &&
        !next->isInvariant() && !next->isShared())
    {
	if (next->isConst())
	    t->next = next->sharedConstOf();
	else
	    t->next = next->sharedOf();
    }
    //printf("TypeNext::makeShared() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeSharedConst()
{
    //printf("TypeNext::makeSharedConst() %s\n", toChars());
    if (scto)
    {	assert(scto->mod == (MODshared | MODconst));
	return scto;
    }    
    TypeNext *t = (TypeNext *)Type::makeSharedConst();
    if (ty != Tfunction && ty != Tdelegate &&
	(next->deco || next->ty == Tfunction) &&
        !next->isInvariant() && !next->isSharedConst())
    {
	t->next = next->sharedConstOf();
    }
    //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars());
    return t;
}

MATCH TypeNext::constConv(Type *to)
{   MATCH m = Type::constConv(to);

    if (m == MATCHconst &&
        next->constConv(((TypeNext *)to)->next) == MATCHnomatch)
	m = MATCHnomatch;
    return m;
}


void TypeNext::transitive()
{
    /* Invoke transitivity of type attributes
     */
    next = next->addMod(mod);
}

/* ============================= TypeBasic =========================== */

TypeBasic::TypeBasic(TY ty)
	: Type(ty)
{   const char *d;
    unsigned flags;

#define TFLAGSintegral	1
#define TFLAGSfloating	2
#define TFLAGSunsigned	4
#define TFLAGSreal	8
#define TFLAGSimaginary	0x10
#define TFLAGScomplex	0x20

    flags = 0;
    switch (ty)
    {
	case Tvoid:	d = Token::toChars(TOKvoid);
			break;

	case Tint8:	d = Token::toChars(TOKint8);
			flags |= TFLAGSintegral;
			break;

	case Tuns8:	d = Token::toChars(TOKuns8);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Tint16:	d = Token::toChars(TOKint16);
			flags |= TFLAGSintegral;
			break;

	case Tuns16:	d = Token::toChars(TOKuns16);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Tint32:	d = Token::toChars(TOKint32);
			flags |= TFLAGSintegral;
			break;

	case Tuns32:	d = Token::toChars(TOKuns32);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Tfloat32:	d = Token::toChars(TOKfloat32);
			flags |= TFLAGSfloating | TFLAGSreal;
			break;

	case Tint64:	d = Token::toChars(TOKint64);
			flags |= TFLAGSintegral;
			break;

	case Tuns64:	d = Token::toChars(TOKuns64);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Tfloat64:	d = Token::toChars(TOKfloat64);
			flags |= TFLAGSfloating | TFLAGSreal;
			break;

	case Tfloat80:	d = Token::toChars(TOKfloat80);
			flags |= TFLAGSfloating | TFLAGSreal;
			break;

	case Timaginary32: d = Token::toChars(TOKimaginary32);
			flags |= TFLAGSfloating | TFLAGSimaginary;
			break;

	case Timaginary64: d = Token::toChars(TOKimaginary64);
			flags |= TFLAGSfloating | TFLAGSimaginary;
			break;

	case Timaginary80: d = Token::toChars(TOKimaginary80);
			flags |= TFLAGSfloating | TFLAGSimaginary;
			break;

	case Tcomplex32: d = Token::toChars(TOKcomplex32);
			flags |= TFLAGSfloating | TFLAGScomplex;
			break;

	case Tcomplex64: d = Token::toChars(TOKcomplex64);
			flags |= TFLAGSfloating | TFLAGScomplex;
			break;

	case Tcomplex80: d = Token::toChars(TOKcomplex80);
			flags |= TFLAGSfloating | TFLAGScomplex;
			break;

	case Tbool:	d = "bool";
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Tascii:	d = Token::toChars(TOKchar);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Twchar:	d = Token::toChars(TOKwchar);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	case Tdchar:	d = Token::toChars(TOKdchar);
			flags |= TFLAGSintegral | TFLAGSunsigned;
			break;

	default:	assert(0);
    }
    this->dstring = d;
    this->flags = flags;
    merge();
}

Type *TypeBasic::syntaxCopy()
{
    // No semantic analysis done on basic types, no need to copy
    return this;
}


char *TypeBasic::toChars()
{
    return Type::toChars();
}

void TypeBasic::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    //printf("TypeBasic::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod);
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring(dstring);
}

d_uns64 TypeBasic::size(Loc loc)
{   unsigned size;

    //printf("TypeBasic::size()\n");
    switch (ty)
    {
	case Tint8:
	case Tuns8:	size = 1;	break;
	case Tint16:
	case Tuns16:	size = 2;	break;
	case Tint32:
	case Tuns32:
	case Tfloat32:
	case Timaginary32:
			size = 4;	break;
	case Tint64:
	case Tuns64:
	case Tfloat64:
	case Timaginary64:
			size = 8;	break;
	case Tfloat80:
	case Timaginary80:
			size = REALSIZE;	break;
	case Tcomplex32:
			size = 8;		break;
	case Tcomplex64:
			size = 16;		break;
	case Tcomplex80:
			size = REALSIZE * 2;	break;

	case Tvoid:
	    //size = Type::size();	// error message
	    size = 1;
	    break;

	case Tbool:	size = 1;		break;
	case Tascii:	size = 1;		break;
	case Twchar:	size = 2;		break;
	case Tdchar:	size = 4;		break;

	default:
	    assert(0);
	    break;
    }
    //printf("TypeBasic::size() = %d\n", size);
    return size;
}

unsigned TypeBasic::alignsize()
{
    if (ty == Tvoid)
        return 1;
    return GetTypeAlignment(sir, this);
#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS
	case Tint64:
	case Tuns64:
	case Tfloat64:
	case Timaginary64:
	case Tcomplex32:
	case Tcomplex64:
	    sz = 4;
	    break;
#endif
#if IN_DMD
	default:
	    sz = size(0);
	    break;
    }
    return sz;
#endif
}


Expression *TypeBasic::getProperty(Loc loc, Identifier *ident)
{
    Expression *e;
    d_int64 ivalue;
#ifdef IN_GCC
    real_t    fvalue;
#else
    d_float80 fvalue;
#endif

    //printf("TypeBasic::getProperty('%s')\n", ident->toChars());
    if (ident == Id::max)
    {
	switch (ty)
	{
	    case Tint8:		ivalue = 0x7F;		goto Livalue;
	    case Tuns8:		ivalue = 0xFF;		goto Livalue;
	    case Tint16:	ivalue = 0x7FFFUL;	goto Livalue;
	    case Tuns16:	ivalue = 0xFFFFUL;	goto Livalue;
	    case Tint32:	ivalue = 0x7FFFFFFFUL;	goto Livalue;
	    case Tuns32:	ivalue = 0xFFFFFFFFUL;	goto Livalue;
	    case Tint64:	ivalue = 0x7FFFFFFFFFFFFFFFLL;	goto Livalue;
	    case Tuns64:	ivalue = 0xFFFFFFFFFFFFFFFFULL;	goto Livalue;
	    case Tbool:		ivalue = 1;		goto Livalue;
	    case Tchar:		ivalue = 0xFF;		goto Livalue;
	    case Twchar:	ivalue = 0xFFFFUL;	goto Livalue;
	    case Tdchar:	ivalue = 0x10FFFFUL;	goto Livalue;

	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	fvalue = FLT_MAX;	goto Lfvalue;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	fvalue = DBL_MAX;	goto Lfvalue;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	fvalue = LDBL_MAX;	goto Lfvalue;
	}
    }
    else if (ident == Id::min)
    {
	switch (ty)
	{
	    case Tint8:		ivalue = -128;		goto Livalue;
	    case Tuns8:		ivalue = 0;		goto Livalue;
	    case Tint16:	ivalue = -32768;	goto Livalue;
	    case Tuns16:	ivalue = 0;		goto Livalue;
	    case Tint32:	ivalue = -2147483647L - 1;	goto Livalue;
	    case Tuns32:	ivalue = 0;			goto Livalue;
	    case Tint64:	ivalue = (-9223372036854775807LL-1LL);	goto Livalue;
	    case Tuns64:	ivalue = 0;		goto Livalue;
	    case Tbool:		ivalue = 0;		goto Livalue;
	    case Tchar:		ivalue = 0;		goto Livalue;
	    case Twchar:	ivalue = 0;		goto Livalue;
	    case Tdchar:	ivalue = 0;		goto Livalue;

	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	fvalue = FLT_MIN;	goto Lfvalue;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	fvalue = DBL_MIN;	goto Lfvalue;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	fvalue = LDBL_MIN;	goto Lfvalue;
	}
    }
    else if (ident == Id::nan)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Tcomplex64:
	    case Tcomplex80:
	    case Timaginary32:
	    case Timaginary64:
	    case Timaginary80:
	    case Tfloat32:
	    case Tfloat64:
	    case Tfloat80:
	    {
		fvalue = Port::nan;
		goto Lfvalue;
	    }
	}
    }
    else if (ident == Id::infinity)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Tcomplex64:
	    case Tcomplex80:
	    case Timaginary32:
	    case Timaginary64:
	    case Timaginary80:
	    case Tfloat32:
	    case Tfloat64:
	    case Tfloat80:
		fvalue = Port::infinity;
		goto Lfvalue;
	}
    }
    else if (ident == Id::dig)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	ivalue = FLT_DIG;	goto Lint;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	ivalue = DBL_DIG;	goto Lint;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	ivalue = LDBL_DIG;	goto Lint;
	}
    }
    else if (ident == Id::epsilon)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	fvalue = FLT_EPSILON;	goto Lfvalue;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	fvalue = DBL_EPSILON;	goto Lfvalue;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	fvalue = LDBL_EPSILON;	goto Lfvalue;
	}
    }
    else if (ident == Id::mant_dig)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	ivalue = FLT_MANT_DIG;	goto Lint;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	ivalue = DBL_MANT_DIG;	goto Lint;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	ivalue = LDBL_MANT_DIG; goto Lint;
	}
    }
    else if (ident == Id::max_10_exp)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	ivalue = FLT_MAX_10_EXP;	goto Lint;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	ivalue = DBL_MAX_10_EXP;	goto Lint;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	ivalue = LDBL_MAX_10_EXP;	goto Lint;
	}
    }
    else if (ident == Id::max_exp)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	ivalue = FLT_MAX_EXP;	goto Lint;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	ivalue = DBL_MAX_EXP;	goto Lint;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	ivalue = LDBL_MAX_EXP;	goto Lint;
	}
    }
    else if (ident == Id::min_10_exp)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	ivalue = FLT_MIN_10_EXP;	goto Lint;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	ivalue = DBL_MIN_10_EXP;	goto Lint;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	ivalue = LDBL_MIN_10_EXP;	goto Lint;
	}
    }
    else if (ident == Id::min_exp)
    {
	switch (ty)
	{
	    case Tcomplex32:
	    case Timaginary32:
	    case Tfloat32:	ivalue = FLT_MIN_EXP;	goto Lint;
	    case Tcomplex64:
	    case Timaginary64:
	    case Tfloat64:	ivalue = DBL_MIN_EXP;	goto Lint;
	    case Tcomplex80:
	    case Timaginary80:
	    case Tfloat80:	ivalue = LDBL_MIN_EXP;	goto Lint;
	}
    }

Ldefault:
    return Type::getProperty(loc, ident);

Livalue:
    e = new IntegerExp(loc, ivalue, this);
    return e;

Lfvalue:
    if (isreal() || isimaginary())
	e = new RealExp(loc, fvalue, this);
    else
    {
	complex_t cvalue;

#if __DMC__
	//((real_t *)&cvalue)[0] = fvalue;
	//((real_t *)&cvalue)[1] = fvalue;
	cvalue = fvalue + fvalue * I;
#else
	cvalue.re = fvalue;
	cvalue.im = fvalue;
#endif
	//for (int i = 0; i < 20; i++)
	//    printf("%02x ", ((unsigned char *)&cvalue)[i]);
	//printf("\n");
	e = new ComplexExp(loc, cvalue, this);
    }
    return e;

Lint:
    e = new IntegerExp(loc, ivalue, Type::tint32);
    return e;
}

Expression *TypeBasic::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    Type *t;

    if (ident == Id::re)
    {
	switch (ty)
	{
	    case Tcomplex32:	t = tfloat32;		goto L1;
	    case Tcomplex64:	t = tfloat64;		goto L1;
	    case Tcomplex80:	t = tfloat80;		goto L1;
	    L1:
		e = e->castTo(sc, t);
		break;

	    case Tfloat32:
	    case Tfloat64:
	    case Tfloat80:
		break;

	    case Timaginary32:	t = tfloat32;		goto L2;
	    case Timaginary64:	t = tfloat64;		goto L2;
	    case Timaginary80:	t = tfloat80;		goto L2;
	    L2:
		e = new RealExp(0, 0.0, t);
		break;

	    default:
		return Type::getProperty(e->loc, ident);
	}
    }
    else if (ident == Id::im)
    {	Type *t2;

	switch (ty)
	{
	    case Tcomplex32:	t = timaginary32;	t2 = tfloat32;	goto L3;
	    case Tcomplex64:	t = timaginary64;	t2 = tfloat64;	goto L3;
	    case Tcomplex80:	t = timaginary80;	t2 = tfloat80;	goto L3;
	    L3:
		e = e->castTo(sc, t);
		e->type = t2;
		break;

	    case Timaginary32:	t = tfloat32;	goto L4;
	    case Timaginary64:	t = tfloat64;	goto L4;
	    case Timaginary80:	t = tfloat80;	goto L4;
	    L4:
		e = e->copy();
		e->type = t;
		break;

	    case Tfloat32:
	    case Tfloat64:
	    case Tfloat80:
		e = new RealExp(0, 0.0, this);
		break;

	    default:
		return Type::getProperty(e->loc, ident);
	}
    }
    else
    {
	return Type::dotExp(sc, e, ident);
    }
    return e;
}

Expression *TypeBasic::defaultInit(Loc loc)
{   dinteger_t value = 0;

#if SNAN_DEFAULT_INIT
    /*
     * Use a payload which is different from the machine NaN,
     * so that uninitialised variables can be
     * detected even if exceptions are disabled.
     */
    unsigned short snan[8] = { 0, 0, 0, 0xA000, 0x7FFF };
    /*
     * Although long doubles are 10 bytes long, some
     * C ABIs pad them out to 12 or even 16 bytes, so
     * leave enough space in the snan array.
     */
    assert(REALSIZE <= sizeof(snan));
    d_float80 fvalue = *(long double*)snan;
#endif

#if LOGDEFAULTINIT
    printf("TypeBasic::defaultInit() '%s'\n", toChars());
#endif
    switch (ty)
    {
	case Tchar:
	    value = 0xFF;
	    break;

	case Twchar:
	case Tdchar:
	    value = 0xFFFF;
	    break;

	case Timaginary32:
	case Timaginary64:
	case Timaginary80:
	case Tfloat32:
	case Tfloat64:
	case Tfloat80:
#if SNAN_DEFAULT_INIT
	    return new RealExp(loc, fvalue, this);
#else
	    return getProperty(loc, Id::nan);
#endif

	case Tcomplex32:
	case Tcomplex64:
	case Tcomplex80:
#if SNAN_DEFAULT_INIT
	{   // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
	    complex_t cvalue;
	    ((real_t *)&cvalue)[0] = fvalue;
	    ((real_t *)&cvalue)[1] = fvalue;
	    return new ComplexExp(loc, cvalue, this);
	}
#else
	    return getProperty(loc, Id::nan);
#endif

	case Tvoid:
	    error(loc, "void does not have a default initializer");
    }
    return new IntegerExp(loc, value, this);
}

int TypeBasic::isZeroInit(Loc loc)
{
    switch (ty)
    {
	case Tchar:
	case Twchar:
	case Tdchar:
	case Timaginary32:
	case Timaginary64:
	case Timaginary80:
	case Tfloat32:
	case Tfloat64:
	case Tfloat80:
	case Tcomplex32:
	case Tcomplex64:
	case Tcomplex80:
	    return 0;		// no
    }
    return 1;			// yes
}

int TypeBasic::isintegral()
{
    //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags);
    return flags & TFLAGSintegral;
}

int TypeBasic::isfloating()
{
    return flags & TFLAGSfloating;
}

int TypeBasic::isreal()
{
    return flags & TFLAGSreal;
}

int TypeBasic::isimaginary()
{
    return flags & TFLAGSimaginary;
}

int TypeBasic::iscomplex()
{
    return flags & TFLAGScomplex;
}

int TypeBasic::isunsigned()
{
    return flags & TFLAGSunsigned;
}

int TypeBasic::isscalar()
{
    return flags & (TFLAGSintegral | TFLAGSfloating);
}

MATCH TypeBasic::implicitConvTo(Type *to)
{
    //printf("TypeBasic::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
    if (this == to)
	return MATCHexact;

#if DMDV2
    if (ty == to->ty)
    {
	return (mod == to->mod) ? MATCHexact : MATCHconst;
    }
#endif

    if (ty == Tvoid || to->ty == Tvoid)
	return MATCHnomatch;
    if (to->ty == Tbool)
	return MATCHnomatch;
    if (!to->isTypeBasic())
	return MATCHnomatch;

    TypeBasic *tob = (TypeBasic *)to;
    if (flags & TFLAGSintegral)
    {
	// Disallow implicit conversion of integers to imaginary or complex
	if (tob->flags & (TFLAGSimaginary | TFLAGScomplex))
	    return MATCHnomatch;

#if DMDV2
	// If converting from integral to integral
	if (1 && tob->flags & TFLAGSintegral)
	{   d_uns64 sz = size(0);
	    d_uns64 tosz = tob->size(0);

	    /* Can't convert to smaller size
	     */
	    if (sz > tosz)
		return MATCHnomatch;

	    /* Can't change sign if same size
	     */
	    /*if (sz == tosz && (flags ^ tob->flags) & TFLAGSunsigned)
		return MATCHnomatch;*/
	}
#endif
    }
    else if (flags & TFLAGSfloating)
    {
	// Disallow implicit conversion of floating point to integer
	if (tob->flags & TFLAGSintegral)
	    return MATCHnomatch;

	assert(tob->flags & TFLAGSfloating);

	// Disallow implicit conversion from complex to non-complex
	if (flags & TFLAGScomplex && !(tob->flags & TFLAGScomplex))
	    return MATCHnomatch;

	// Disallow implicit conversion of real or imaginary to complex
	if (flags & (TFLAGSreal | TFLAGSimaginary) &&
	    tob->flags & TFLAGScomplex)
	    return MATCHnomatch;

	// Disallow implicit conversion to-from real and imaginary
	if ((flags & (TFLAGSreal | TFLAGSimaginary)) !=
	    (tob->flags & (TFLAGSreal | TFLAGSimaginary)))
	    return MATCHnomatch;
    }
    return MATCHconvert;
}

TypeBasic *TypeBasic::isTypeBasic()
{
    return (TypeBasic *)this;
}

/***************************** TypeArray *****************************/

TypeArray::TypeArray(TY ty, Type *next)
    : TypeNext(ty, next)
{
}

Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
    Type *n = this->next->toBasetype();		// uncover any typedef's

#if LOGDOTEXP
    printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (ident == Id::reverse && (n->ty == Tchar || n->ty == Twchar))
    {
	Expression *ec;
	Expressions *arguments;

	//LDC: Build arguments.
	static FuncDeclaration *adReverseChar_fd = NULL;
	if(!adReverseChar_fd) {
	    Arguments* args = new Arguments;
	    Type* arrty = Type::tchar->arrayOf();
	    args->push(new Argument(STCin, arrty, NULL, NULL));
	    adReverseChar_fd = FuncDeclaration::genCfunc(args, arrty, "_adReverseChar");
	}
	static FuncDeclaration *adReverseWchar_fd = NULL;
	if(!adReverseWchar_fd) {
	    Arguments* args = new Arguments;
	    Type* arrty = Type::twchar->arrayOf();
	    args->push(new Argument(STCin, arrty, NULL, NULL));
	    adReverseWchar_fd = FuncDeclaration::genCfunc(args, arrty, "_adReverseWchar");
	}

	if(n->ty == Twchar)
	    ec = new VarExp(0, adReverseWchar_fd);
	else
	    ec = new VarExp(0, adReverseChar_fd);
	e = e->castTo(sc, n->arrayOf());	// convert to dynamic array
	arguments = new Expressions();
	arguments->push(e);
	e = new CallExp(e->loc, ec, arguments);
	e->type = next->arrayOf();
    }
    else if (ident == Id::sort && (n->ty == Tchar || n->ty == Twchar))
    {
	Expression *ec;
	Expressions *arguments;

	//LDC: Build arguments.
	static FuncDeclaration *adSortChar_fd = NULL;
	if(!adSortChar_fd) {
	    Arguments* args = new Arguments;
	    Type* arrty = Type::tchar->arrayOf();
	    args->push(new Argument(STCin, arrty, NULL, NULL));
	    adSortChar_fd = FuncDeclaration::genCfunc(args, arrty, "_adSortChar");
	}
	static FuncDeclaration *adSortWchar_fd = NULL;
	if(!adSortWchar_fd) {
	    Arguments* args = new Arguments;
	    Type* arrty = Type::twchar->arrayOf();
	    args->push(new Argument(STCin, arrty, NULL, NULL));
	    adSortWchar_fd = FuncDeclaration::genCfunc(args, arrty, "_adSortWchar");
	}

	if(n->ty == Twchar)
	    ec = new VarExp(0, adSortWchar_fd);
	else
	    ec = new VarExp(0, adSortChar_fd);
	e = e->castTo(sc, n->arrayOf());	// convert to dynamic array
	arguments = new Expressions();
	arguments->push(e);
	e = new CallExp(e->loc, ec, arguments);
	e->type = next->arrayOf();
    }
    else if (ident == Id::reverse || ident == Id::dup || ident == Id::idup)
    {
	Expression *ec;
	Expressions *arguments;
	int size = next->size(e->loc);
	int dup;

	assert(size);
	dup = (ident == Id::dup || ident == Id::idup);
	//LDC: Build arguments.
	static FuncDeclaration *adDup_fd = NULL;
	if(!adDup_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
	    args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
	    adDup_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adDup);
	}
	static FuncDeclaration *adReverse_fd = NULL;
	if(!adReverse_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
	    args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
	    adReverse_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adReverse);
	}

	if(dup)
	    ec = new VarExp(0, adDup_fd);
	else
	    ec = new VarExp(0, adReverse_fd);
	e = e->castTo(sc, n->arrayOf());	// convert to dynamic array
	arguments = new Expressions();
	if (dup)
	    arguments->push(getTypeInfo(sc));

    // LDC repaint array type to void[]
    if (n->ty != Tvoid) {
        e = new CastExp(e->loc, e, e->type);
        e->type = Type::tvoid->arrayOf();
    }
    arguments->push(e);

	if (!dup)
	    arguments->push(new IntegerExp(0, size, Type::tsize_t));
	e = new CallExp(e->loc, ec, arguments);
	if (ident == Id::idup)
	{   Type *einv = next->invariantOf();
	    if (next->implicitConvTo(einv) < MATCHconst)
		error(e->loc, "cannot implicitly convert element type %s to immutable", next->toChars());
	    e->type = einv->arrayOf();
	}
	else
	    e->type = next->mutableOf()->arrayOf();
    }
    else if (ident == Id::sort)
    {
	Expression *ec;
	Expressions *arguments;
	bool isBit = (n->ty == Tbit);

	//LDC: Build arguments.
	static FuncDeclaration *adSort_fd = NULL;
	if(!adSort_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
	    args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
	    adSort_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSort");
	}
	static FuncDeclaration *adSortBit_fd = NULL;
	if(!adSortBit_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
	    args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
	    adSortBit_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSortBit");
	}

	if(isBit)
	    ec = new VarExp(0, adSortBit_fd);
	else
	    ec = new VarExp(0, adSort_fd);
	e = e->castTo(sc, n->arrayOf());	// convert to dynamic array
	arguments = new Expressions();

    // LDC repaint array type to void[]
    if (n->ty != Tvoid) {
        e = new CastExp(e->loc, e, e->type);
        e->type = Type::tvoid->arrayOf();
    }
	arguments->push(e);

    if (next->ty != Tbit)
        arguments->push(n->getTypeInfo(sc));   // LDC, we don't support the getInternalTypeInfo
                                               // optimization arbitrarily, not yet at least...
	e = new CallExp(e->loc, ec, arguments);
	e->type = next->arrayOf();
    }
    else
    {
	e = Type::dotExp(sc, e, ident);
    }
    return e;
}


/***************************** TypeSArray *****************************/

TypeSArray::TypeSArray(Type *t, Expression *dim)
    : TypeArray(Tsarray, t)
{
    //printf("TypeSArray(%s)\n", dim->toChars());
    this->dim = dim;
}

Type *TypeSArray::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    Expression *e = dim->syntaxCopy();
    t = new TypeSArray(t, e);
    t->mod = mod;
    return t;
}

d_uns64 TypeSArray::size(Loc loc)
{   dinteger_t sz;

    if (!dim)
	return Type::size(loc);
    sz = dim->toInteger();

    {	dinteger_t n, n2;

	n = next->size();
	n2 = n * sz;
	if (n && (n2 / n) != sz)
	    goto Loverflow;
	sz = n2;
    }
    return sz;

Loverflow:
    error(loc, "index %jd overflow for static array", sz);
    return 1;
}

unsigned TypeSArray::alignsize()
{
    return next->alignsize();
}

/**************************
 * This evaluates exp while setting length to be the number
 * of elements in the tuple t.
 */
Expression *semanticLength(Scope *sc, Type *t, Expression *exp)
{
    if (t->ty == Ttuple)
    {	ScopeDsymbol *sym = new ArrayScopeSymbol(sc, (TypeTuple *)t);
	sym->parent = sc->scopesym;
	sc = sc->push(sym);

	exp = exp->semantic(sc);

	sc->pop();
    }
    else
	exp = exp->semantic(sc);
    return exp;
}

Expression *semanticLength(Scope *sc, TupleDeclaration *s, Expression *exp)
{
    ScopeDsymbol *sym = new ArrayScopeSymbol(sc, s);
    sym->parent = sc->scopesym;
    sc = sc->push(sym);

    exp = exp->semantic(sc);

    sc->pop();
    return exp;
}

void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
    //printf("TypeSArray::resolve() %s\n", toChars());
    next->resolve(loc, sc, pe, pt, ps);
    //printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
    if (*pe)
    {	// It's really an index expression
	Expression *e = new IndexExp(loc, *pe, dim);
	*pe = e;
    }
    else if (*ps)
    {	Dsymbol *s = *ps;
	TupleDeclaration *td = s->isTupleDeclaration();
	if (td)
	{
	    ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td);
	    sym->parent = sc->scopesym;
	    sc = sc->push(sym);

	    dim = dim->semantic(sc);
	    dim = dim->optimize(WANTvalue | WANTinterpret);
	    uinteger_t d = dim->toUInteger();

	    sc = sc->pop();

	    if (d >= td->objects->dim)
	    {	error(loc, "tuple index %ju exceeds %u", d, td->objects->dim);
		goto Ldefault;
	    }
	    Object *o = (Object *)td->objects->data[(size_t)d];
	    if (o->dyncast() == DYNCAST_DSYMBOL)
	    {
		*ps = (Dsymbol *)o;
		return;
	    }
	    if (o->dyncast() == DYNCAST_EXPRESSION)
	    {
		*ps = NULL;
		*pe = (Expression *)o;
		return;
	    }

	    /* Create a new TupleDeclaration which
	     * is a slice [d..d+1] out of the old one.
	     * Do it this way because TemplateInstance::semanticTiargs()
	     * can handle unresolved Objects this way.
	     */
	    Objects *objects = new Objects;
	    objects->setDim(1);
	    objects->data[0] = o;

	    TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
	    *ps = tds;
	}
	else
	    goto Ldefault;
    }
    else
    {
     Ldefault:
	Type::resolve(loc, sc, pe, pt, ps);
    }
}

Type *TypeSArray::semantic(Loc loc, Scope *sc)
{
    //printf("TypeSArray::semantic() %s\n", toChars());

    Type *t;
    Expression *e;
    Dsymbol *s;
    next->resolve(loc, sc, &e, &t, &s);
    if (dim && s && s->isTupleDeclaration())
    {	TupleDeclaration *sd = s->isTupleDeclaration();

	dim = semanticLength(sc, sd, dim);
	dim = dim->optimize(WANTvalue | WANTinterpret);
	uinteger_t d = dim->toUInteger();

	if (d >= sd->objects->dim)
	{   error(loc, "tuple index %ju exceeds %u", d, sd->objects->dim);
	    return Type::terror;
	}
	Object *o = (Object *)sd->objects->data[(size_t)d];
	if (o->dyncast() != DYNCAST_TYPE)
	{   error(loc, "%s is not a type", toChars());
	    return Type::terror;
	}
	t = (Type *)o;
	return t;
    }

    next = next->semantic(loc,sc);
    transitive();

    Type *tbn = next->toBasetype();

    if (dim)
    {	dinteger_t n, n2;

	dim = semanticLength(sc, tbn, dim);

	dim = dim->optimize(WANTvalue | WANTinterpret);
	if (sc && sc->parameterSpecialization && dim->op == TOKvar &&
	    ((VarExp *)dim)->var->storage_class & STCtemplateparameter)
	{
	    /* It could be a template parameter N which has no value yet:
	     *   template Foo(T : T[N], size_t N);
	     */
	    return this;
	}
	dinteger_t d1 = dim->toInteger();
	dim = dim->castTo(sc, tsize_t);
	dim = dim->optimize(WANTvalue);
	dinteger_t d2 = dim->toInteger();

	if (d1 != d2)
	    goto Loverflow;

	if (tbn->isintegral() ||
		 tbn->isfloating() ||
		 tbn->ty == Tpointer ||
		 tbn->ty == Tarray ||
		 tbn->ty == Tsarray ||
		 tbn->ty == Taarray ||
		 tbn->ty == Tclass)
	{
	    /* Only do this for types that don't need to have semantic()
	     * run on them for the size, since they may be forward referenced.
	     */
	    n = tbn->size(loc);
	    n2 = n * d2;
	    if ((int)n2 < 0)
		goto Loverflow;
	    if (n2 >= 0x1000000)	// put a 'reasonable' limit on it
		goto Loverflow;
	    if (n && n2 / n != d2)
	    {
	      Loverflow:
		error(loc, "index %jd overflow for static array", d1);
		dim = new IntegerExp(0, 1, tsize_t);
	    }
	}
    }
    switch (tbn->ty)
    {
	case Ttuple:
	{   // Index the tuple to get the type
	    assert(dim);
	    TypeTuple *tt = (TypeTuple *)tbn;
	    uinteger_t d = dim->toUInteger();

	    if (d >= tt->arguments->dim)
	    {	error(loc, "tuple index %ju exceeds %u", d, tt->arguments->dim);
		return Type::terror;
	    }
	    Argument *arg = (Argument *)tt->arguments->data[(size_t)d];
	    return arg->type;
	}
	case Tstruct:
	{   TypeStruct *ts = (TypeStruct *)tbn;
	    if (ts->sym->isnested)
		error(loc, "cannot have array of inner structs %s", ts->toChars());
	    break;
	}
	case Tfunction:
	case Tnone:
	    error(loc, "can't have array of %s", tbn->toChars());
	    tbn = next = tint32;
	    break;
    }
    if (tbn->isauto())
	error(loc, "cannot have array of auto %s", tbn->toChars());
    return merge();
}

void TypeSArray::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    Type::toDecoBuffer(buf, flag, mangle);
    if (dim)
	buf->printf("%ju", dim->toInteger());
    if (next)
	/* Note that static arrays are value types, so
	 * for a parameter, propagate the 0x100 to the next
	 * level, since for T[4][3], any const should apply to the T,
	 * not the [4].
	 */
	next->toDecoBuffer(buf,  (flag & 0x100) ? flag : mod, mangle);
}

void TypeSArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);
    buf->printf("[%s]", dim->toChars());
}

Expression *TypeSArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (ident == Id::length)
    {
	e = dim;
    }
    else if (ident == Id::ptr)
    {
	e = e->castTo(sc, next->pointerTo());
    }
    else
    {
	e = TypeArray::dotExp(sc, e, ident);
    }
    return e;
}

int TypeSArray::isString()
{
    TY nty = next->toBasetype()->ty;
    return nty == Tchar || nty == Twchar || nty == Tdchar;
}

unsigned TypeSArray::memalign(unsigned salign)
{
    return next->memalign(salign);
}

MATCH TypeSArray::constConv(Type *to)
{
    if (to->ty == Tsarray)
    {
	TypeSArray *tsa = (TypeSArray *)to;
	if (!dim->equals(tsa->dim))
	    return MATCHnomatch;
    }
    return TypeNext::constConv(to);
}

MATCH TypeSArray::implicitConvTo(Type *to)
{
    //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());

    // Allow implicit conversion of static array to pointer or dynamic array
    if (IMPLICIT_ARRAY_TO_PTR && to->ty == Tpointer)
    {
	TypePointer *tp = (TypePointer *)to;

	if (next->mod != tp->next->mod && tp->next->mod != MODconst)
	    return MATCHnomatch;

	if (tp->next->ty == Tvoid || next->constConv(tp->next) != MATCHnomatch)
	{
	    return MATCHconvert;
	}
	return MATCHnomatch;
    }
    if (to->ty == Tarray)
    {	int offset = 0;
	TypeDArray *ta = (TypeDArray *)to;

	if (next->mod != ta->next->mod && ta->next->mod != MODconst)
	    return MATCHnomatch;

	if (next->equals(ta->next) ||
	    next->implicitConvTo(ta->next) >= MATCHconst ||
	    (ta->next->isBaseOf(next, &offset) && offset == 0) ||
	    ta->next->ty == Tvoid)
	    return MATCHconvert;
	return MATCHnomatch;
    }
    if (to->ty == Tsarray)
    {
	if (this == to)
	    return MATCHexact;

	TypeSArray *tsa = (TypeSArray *)to;

	if (dim->equals(tsa->dim))
	{
	    /* Since static arrays are value types, allow
	     * conversions from const elements to non-const
	     * ones, just like we allow conversion from const int
	     * to int.
	     */
	    MATCH m = next->implicitConvTo(tsa->next);
	    if (m >= MATCHconst)
	    {
		if (mod != to->mod)
		    m = MATCHconst;
		return m;
	    }
	}
    }
    return MATCHnomatch;
}

Expression *TypeSArray::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeSArray::defaultInit() '%s'\n", toChars());
#endif
    return next->defaultInit(loc);
}

int TypeSArray::isZeroInit(Loc loc)
{
    return next->isZeroInit(loc);
}


Expression *TypeSArray::toExpression()
{
    Expression *e = next->toExpression();
    if (e)
    {	Expressions *arguments = new Expressions();
	arguments->push(dim);
	e = new ArrayExp(dim->loc, e, arguments);
    }
    return e;
}

int TypeSArray::hasPointers()
{
    return next->hasPointers();
}

/***************************** TypeDArray *****************************/

TypeDArray::TypeDArray(Type *t)
    : TypeArray(Tarray, t)
{
    //printf("TypeDArray(t = %p)\n", t);
}

Type *TypeDArray::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    if (t == next)
	t = this;
    else
    {	t = new TypeDArray(t);
	t->mod = mod;
    }
    return t;
}

d_uns64 TypeDArray::size(Loc loc)
{
    //printf("TypeDArray::size()\n");
    return PTRSIZE * 2;
}

unsigned TypeDArray::alignsize()
{
    // A DArray consists of two ptr-sized values, so align it on pointer size
    // boundary
    return PTRSIZE;
}

Type *TypeDArray::semantic(Loc loc, Scope *sc)
{   Type *tn = next;

    tn = next->semantic(loc,sc);
    Type *tbn = tn->toBasetype();
    switch (tbn->ty)
    {
	case Tfunction:
	case Tnone:
	case Ttuple:
	    error(loc, "can't have array of %s", tbn->toChars());
	    tn = next = tint32;
	    break;
	case Tstruct:
	{   TypeStruct *ts = (TypeStruct *)tbn;
	    if (ts->sym->isnested)
		error(loc, "cannot have array of inner structs %s", ts->toChars());
	    break;
	}
    }
    if (tn->isauto())
	error(loc, "cannot have array of auto %s", tn->toChars());

    next = tn;
    transitive();
    return merge();
}

void TypeDArray::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    Type::toDecoBuffer(buf, flag, mangle);
    if (next)
	next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}

void TypeDArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);
    buf->writestring("[]");
}

Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (ident == Id::length)
    {
	if (e->op == TOKstring)
	{   StringExp *se = (StringExp *)e;

	    return new IntegerExp(se->loc, se->len, Type::tindex);
	}
	e = new ArrayLengthExp(e->loc, e);
	e->type = Type::tsize_t;
	return e;
    }
    else if (ident == Id::ptr)
    {
	e = e->castTo(sc, next->pointerTo());
	return e;
    }
    else
    {
	e = TypeArray::dotExp(sc, e, ident);
    }
    return e;
}

int TypeDArray::isString()
{
    TY nty = next->toBasetype()->ty;
    return nty == Tchar || nty == Twchar || nty == Tdchar;
}

MATCH TypeDArray::implicitConvTo(Type *to)
{
    //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
    if (equals(to))
	return MATCHexact;

    // Allow implicit conversion of array to pointer
    if (IMPLICIT_ARRAY_TO_PTR && to->ty == Tpointer)
    {
	TypePointer *tp = (TypePointer *)to;

	/* Allow conversion to void*
	 */
	if (tp->next->ty == Tvoid &&
	    (next->mod == tp->next->mod || tp->next->mod == MODconst))
	{
	    return MATCHconvert;
	}

	return next->constConv(to);
    }

    if (to->ty == Tarray)
    {	int offset = 0;
	TypeDArray *ta = (TypeDArray *)to;

	if (!(next->mod == ta->next->mod || ta->next->mod == MODconst))
	    return MATCHnomatch;	// not const-compatible

	/* Allow conversion to void[]
	 */
	if (next->ty != Tvoid && ta->next->ty == Tvoid)
	{
	    return MATCHconvert;
	}

	MATCH m = next->constConv(ta->next);
	if (m != MATCHnomatch)
	{
	    if (m == MATCHexact && mod != to->mod)
		m = MATCHconst;
	    return m;
	}

	/* Allow conversions of T[][] to const(T)[][]
	 */
	if (mod == ta->mod && next->ty == Tarray && ta->next->ty == Tarray)
	{
	    m = next->implicitConvTo(ta->next);
	    if (m == MATCHconst)
		return m;
	}

	/* Conversion of array of derived to array of base
	 */
	if (ta->next->isBaseOf(next, &offset) && offset == 0)
	    return MATCHconvert;
    }
    return Type::implicitConvTo(to);
}

Expression *TypeDArray::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeDArray::defaultInit() '%s'\n", toChars());
#endif
    Expression *e;
    e = new NullExp(loc);
    e->type = this;
    return e;
}

int TypeDArray::isZeroInit(Loc loc)
{
    return 1;
}

int TypeDArray::checkBoolean()
{
    return TRUE;
}

int TypeDArray::hasPointers()
{
    return TRUE;
}


/***************************** TypeNewArray *****************************/

#if 0

TypeNewArray::TypeNewArray(Type *telement)
	: TypeArray(Tnewarray, telement)
{
    sym = NULL;
}

Type *TypeNewArray::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    if (t == next)
	t = this;
    else
    {	t = new TypeNewArray(t);
	t->mod = mod;
    }
    return t;
}

d_uns64 TypeNewArray::size(Loc loc)
{
    //printf("TypeNewArray::size()\n");
    return PTRSIZE;
}

unsigned TypeNewArray::alignsize()
{
    return PTRSIZE;
}

Type *TypeNewArray::semantic(Loc loc, Scope *sc)
{   Type *tn = next;

    tn = next->semantic(loc,sc);
    Type *tbn = tn->toBasetype();
    switch (tbn->ty)
    {
	case Tfunction:
	case Tnone:
	case Ttuple:
	    error(loc, "can't have array of %s", tbn->toChars());
	    tn = next = tint32;
	    break;
	case Tstruct:
	{   TypeStruct *ts = (TypeStruct *)tbn;
	    if (ts->sym->isnested)
		error(loc, "cannot have array of inner structs %s", ts->toChars());
	    break;
	}
    }
    if (tn->isauto())
	error(loc, "cannot have array of auto %s", tn->toChars());

    next = tn;
    transitive();
    return merge();
}

void TypeNewArray::toDecoBuffer(OutBuffer *buf, int flag)
{
    Type::toDecoBuffer(buf, flag);
    buf->writeByte('e');
    if (next)
	next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod);
}

void TypeNewArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);
    buf->writestring("[new]");
}

#endif

/***************************** TypeAArray *****************************/

TypeAArray::TypeAArray(Type *t, Type *index)
    : TypeArray(Taarray, t)
{
    this->index = index;
}

Type *TypeAArray::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    Type *ti = index->syntaxCopy();
    if (t == next && ti == index)
	t = this;
    else
    {	t = new TypeAArray(t, ti);
	t->mod = mod;
    }
    return t;
}

d_uns64 TypeAArray::size(Loc loc)
{
    return PTRSIZE /* * 2*/;
}


Type *TypeAArray::semantic(Loc loc, Scope *sc)
{
    //printf("TypeAArray::semantic() %s index->ty = %d\n", toChars(), index->ty);

    // Deal with the case where we thought the index was a type, but
    // in reality it was an expression.
    if (index->ty == Tident || index->ty == Tinstance || index->ty == Tsarray)
    {
	Expression *e;
	Type *t;
	Dsymbol *s;

	index->resolve(loc, sc, &e, &t, &s);
	if (e)
	{   // It was an expression -
	    // Rewrite as a static array
	    TypeSArray *tsa;

	    tsa = new TypeSArray(next, e);
	    return tsa->semantic(loc,sc);
	}
	else if (t)
	    index = t;
	else
	    index->error(loc, "index is not a type or an expression");
    }
    else
	index = index->semantic(loc,sc);

    if (index->nextOf() && !index->nextOf()->isInvariant())
    {
	index = index->constOf()->mutableOf();
#if 0
printf("index is %p %s\n", index, index->toChars());
index->check();
printf("index->mod = x%x\n", index->mod);
printf("index->ito = x%x\n", index->ito);
if (index->ito) {
printf("index->ito->mod = x%x\n", index->ito->mod);
printf("index->ito->ito = x%x\n", index->ito->ito);
}
#endif
    }

    switch (index->toBasetype()->ty)
    {
	case Tbool:
	case Tfunction:
	case Tvoid:
	case Tnone:
	    error(loc, "can't have associative array key of %s", index->toBasetype()->toChars());
	    break;
    }
    next = next->semantic(loc,sc);
    transitive();

    switch (next->toBasetype()->ty)
    {
	case Tfunction:
	case Tnone:
	    error(loc, "can't have associative array of %s", next->toChars());
	    break;
    }
    if (next->isauto())
	error(loc, "cannot have array of auto %s", next->toChars());

    return merge();
}

void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
    //printf("TypeAArray::resolve() %s\n", toChars());

    // Deal with the case where we thought the index was a type, but
    // in reality it was an expression.
    if (index->ty == Tident || index->ty == Tinstance || index->ty == Tsarray)
    {
	Expression *e;
	Type *t;
	Dsymbol *s;

	index->resolve(loc, sc, &e, &t, &s);
	if (e)
	{   // It was an expression -
	    // Rewrite as a static array

	    TypeSArray *tsa = new TypeSArray(next, e);
	    return tsa->resolve(loc, sc, pe, pt, ps);
	}
	else if (t)
	    index = t;
	else
	    index->error(loc, "index is not a type or an expression");
    }
    Type::resolve(loc, sc, pe, pt, ps);
}


Expression *TypeAArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (ident == Id::length)
    {
	Expression *ec;
	Expressions *arguments;

	//LDC: Build arguments.
	static FuncDeclaration *aaLen_fd = NULL;
	if(!aaLen_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
	    aaLen_fd = FuncDeclaration::genCfunc(args, Type::tsize_t, Id::aaLen);
	}

	ec = new VarExp(0, aaLen_fd);
	arguments = new Expressions();
	arguments->push(e);
	e = new CallExp(e->loc, ec, arguments);
	e->type = aaLen_fd->type->nextOf();
    }
    else if (ident == Id::keys)
    {
	Expression *ec;
	Expressions *arguments;
	int size = index->size(e->loc);

	assert(size);
	//LDC: Build arguments.
	static FuncDeclaration *aaKeys_fd = NULL;
	if(!aaKeys_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
	    args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
	    aaKeys_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::aaKeys);
	}

	ec = new VarExp(0, aaKeys_fd);
	arguments = new Expressions();
	arguments->push(e);
	arguments->push(new IntegerExp(0, size, Type::tsize_t));
	e = new CallExp(e->loc, ec, arguments);
	e->type = index->arrayOf();
    }
    else if (ident == Id::values)
    {
	Expression *ec;
	Expressions *arguments;

	//LDC: Build arguments.
	static FuncDeclaration *aaValues_fd = NULL;
	if(!aaValues_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
	    args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
	    args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
	    aaValues_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::aaValues);
	}

	ec = new VarExp(0, aaValues_fd);
	arguments = new Expressions();
	arguments->push(e);
	size_t keysize = index->size(e->loc);
	keysize = (keysize + PTRSIZE - 1) & ~(PTRSIZE - 1);
	arguments->push(new IntegerExp(0, keysize, Type::tsize_t));
	arguments->push(new IntegerExp(0, next->size(e->loc), Type::tsize_t));
	e = new CallExp(e->loc, ec, arguments);
	e->type = next->arrayOf();
    }
    else if (ident == Id::rehash)
    {
	Expression *ec;
	Expressions *arguments;

	//LDC: Build arguments.
	static FuncDeclaration *aaRehash_fd = NULL;
	if(!aaRehash_fd) {
	    Arguments* args = new Arguments;
	    args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
	    args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
	    aaRehash_fd = FuncDeclaration::genCfunc(args, Type::tvoidptr, Id::aaRehash);
	}

	ec = new VarExp(0, aaRehash_fd);
	arguments = new Expressions();
	arguments->push(e->addressOf(sc));
	arguments->push(index->getInternalTypeInfo(sc)); // LDC doesn't support getInternalTypeInfo, see above
	e = new CallExp(e->loc, ec, arguments);
	e->type = this;
    }
    else
    {
	e = Type::dotExp(sc, e, ident);
    }
    return e;
}

void TypeAArray::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    Type::toDecoBuffer(buf, flag, mangle);
    index->toDecoBuffer(buf, mangle);
    next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}

void TypeAArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);
    buf->writeByte('[');
    index->toCBuffer2(buf, hgs, 0);
    buf->writeByte(']');
}

Expression *TypeAArray::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeAArray::defaultInit() '%s'\n", toChars());
#endif
    Expression *e;
    e = new NullExp(loc);
    e->type = this;
    return e;
}

int TypeAArray::isZeroInit(Loc loc)
{
    return TRUE;
}

int TypeAArray::checkBoolean()
{
    return TRUE;
}

int TypeAArray::hasPointers()
{
    return TRUE;
}

MATCH TypeAArray::implicitConvTo(Type *to)
{
    //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
    if (equals(to))
	return MATCHexact;

    if (to->ty == Taarray)
    {	TypeAArray *ta = (TypeAArray *)to;

	if (!(next->mod == ta->next->mod || ta->next->mod == MODconst))
	    return MATCHnomatch;	// not const-compatible

	if (!(index->mod == ta->index->mod || ta->index->mod == MODconst))
	    return MATCHnomatch;	// not const-compatible

	MATCH m = next->constConv(ta->next);
	MATCH mi = index->constConv(ta->index);
	if (m != MATCHnomatch && mi != MATCHnomatch)
	{
	    if (m == MATCHexact && mod != to->mod)
		m = MATCHconst;
	    if (mi < m)
		m = mi;
	    return m;
	}
    }
    return Type::implicitConvTo(to);
}

MATCH TypeAArray::constConv(Type *to)
{
    if (to->ty == Taarray)
    {
	TypeAArray *taa = (TypeAArray *)to;
	MATCH mindex = index->constConv(taa->index);
	MATCH mkey = next->constConv(taa->next);
	// Pick the worst match
	return mkey < mindex ? mkey : mindex;
    }
    else
	return Type::constConv(to);
}

/***************************** TypePointer *****************************/

TypePointer::TypePointer(Type *t)
    : TypeNext(Tpointer, t)
{
}

Type *TypePointer::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    if (t == next)
	t = this;
    else
    {	t = new TypePointer(t);
	t->mod = mod;
    }
    return t;
}

Type *TypePointer::semantic(Loc loc, Scope *sc)
{
    //printf("TypePointer::semantic()\n");
    if (deco)
	return this;
    Type *n = next->semantic(loc, sc);
    switch (n->toBasetype()->ty)
    {
	case Ttuple:
	    error(loc, "can't have pointer to %s", n->toChars());
	    n = tint32;
	    break;
    }
    if (n != next)
    {
	deco = NULL;
    }
    next = n;
    transitive();
    return merge();
}


d_uns64 TypePointer::size(Loc loc)
{
    return PTRSIZE;
}

void TypePointer::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    //printf("TypePointer::toCBuffer2() next = %d\n", next->ty);
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);
    if (next->ty != Tfunction)
	buf->writeByte('*');
}

MATCH TypePointer::implicitConvTo(Type *to)
{
    //printf("TypePointer::implicitConvTo(to = %s) %s\n", to->toChars(), toChars());

    if (equals(to))
	return MATCHexact;
    if (to->ty == Tpointer)
    {	TypePointer *tp = (TypePointer *)to;
	assert(tp->next);

        if (!(next->mod == tp->next->mod || tp->next->mod == MODconst))
            return MATCHnomatch;        // not const-compatible

        /* Alloc conversion to void[]
         */
        if (next->ty != Tvoid && tp->next->ty == Tvoid)
        {
            return MATCHconvert;
        }

        MATCH m = next->constConv(tp->next);
        if (m != MATCHnomatch)
	{
	    if (m == MATCHexact && mod != to->mod)
		m = MATCHconst;
            return m;
	}

        /* Conversion of ptr to derived to ptr to base
         */
	int offset = 0;
        if (tp->next->isBaseOf(next, &offset) && offset == 0)
            return MATCHconvert;
    }
    return MATCHnomatch;
}

int TypePointer::isscalar()
{
    return TRUE;
}

Expression *TypePointer::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypePointer::defaultInit() '%s'\n", toChars());
#endif
    Expression *e;
    e = new NullExp(loc);
    e->type = this;
    return e;
}

int TypePointer::isZeroInit(Loc loc)
{
    return 1;
}

int TypePointer::hasPointers()
{
    return TRUE;
}


/***************************** TypeReference *****************************/

TypeReference::TypeReference(Type *t)
    : TypeNext(Treference, t)
{
    // BUG: what about references to static arrays?
}

Type *TypeReference::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    if (t == next)
	t = this;
    else
    {	t = new TypeReference(t);
	t->mod = mod;
    }
    return t;
}

Type *TypeReference::semantic(Loc loc, Scope *sc)
{
    //printf("TypeReference::semantic()\n");
    Type *n = next->semantic(loc, sc);
    if (n != next)
	deco = NULL;
    next = n;
    transitive();
    return merge();
}


d_uns64 TypeReference::size(Loc loc)
{
    return PTRSIZE;
}

void TypeReference::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);
    buf->writeByte('&');
}

Expression *TypeReference::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif

    // References just forward things along
    return next->dotExp(sc, e, ident);
}

Expression *TypeReference::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeReference::defaultInit() '%s'\n", toChars());
#endif
    Expression *e = new NullExp(loc);
    e->type = this;
    return e;
}

int TypeReference::isZeroInit(Loc loc)
{
    return 1;
}


/***************************** TypeFunction *****************************/

TypeFunction::TypeFunction(Arguments *parameters, Type *treturn, int varargs, enum LINK linkage)
    : TypeNext(Tfunction, treturn)
{
//if (!treturn) *(char*)0=0;
//    assert(treturn);
    assert(0 <= varargs && varargs <= 2);
    this->parameters = parameters;
    this->varargs = varargs;
    this->linkage = linkage;
    this->inuse = 0;
    this->isnothrow = false;
    this->ispure = false;
    this->isproperty = false;
    this->isref = false;

#if IN_LLVM
    this->funcdecl = NULL;
#endif
}

Type *TypeFunction::syntaxCopy()
{
    Type *treturn = next ? next->syntaxCopy() : NULL;
    Arguments *params = Argument::arraySyntaxCopy(parameters);
    TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage);
    t->mod = mod;
    t->isnothrow = isnothrow;
    t->ispure = ispure;
    t->isproperty = isproperty;
    t->isref = isref;
    return t;
}

/*******************************
 * Returns:
 *	0	types are distinct
 *	1	this is covariant with t
 *	2	arguments match as far as overloading goes,
 *		but types are not covariant
 *	3	cannot determine covariance because of forward references
 */

int Type::covariant(Type *t)
{
#if 0
    printf("Type::covariant(t = %s) %s\n", t->toChars(), toChars());
    printf("deco = %p, %p\n", deco, t->deco);
//    printf("ty = %d\n", next->ty);
    printf("mod = %x, %x\n", mod, t->mod);
#endif

    int inoutmismatch = 0;

    TypeFunction *t1;
    TypeFunction *t2;

    if (equals(t))
	return 1;			// covariant

    if (ty != Tfunction || t->ty != Tfunction)
	goto Ldistinct;

    t1 = (TypeFunction *)this;
    t2 = (TypeFunction *)t;

    if (t1->varargs != t2->varargs)
	goto Ldistinct;

    if (t1->parameters && t2->parameters)
    {
	size_t dim = Argument::dim(t1->parameters);
	if (dim != Argument::dim(t2->parameters))
	    goto Ldistinct;

	for (size_t i = 0; i < dim; i++)
	{   Argument *arg1 = Argument::getNth(t1->parameters, i);
	    Argument *arg2 = Argument::getNth(t2->parameters, i);

	    if (!arg1->type->equals(arg2->type))
	    {
#if 0 // turn on this for contravariant argument types, see bugzilla 3075
		// We can add const, but not subtract it
		if (arg2->type->implicitConvTo(arg1->type) < MATCHconst)
#endif
		    goto Ldistinct;
	    }
	    if ((arg1->storageClass & ~STCscope) != (arg2->storageClass & ~STCscope))
		inoutmismatch = 1;
	    // We can add scope, but not subtract it
	    if (!(arg1->storageClass & STCscope) && (arg2->storageClass & STCscope))
		inoutmismatch = 1;
	}
    }
    else if (t1->parameters != t2->parameters)
	goto Ldistinct;

    // The argument lists match
    if (inoutmismatch)
	goto Lnotcovariant;
    if (t1->linkage != t2->linkage)
	goto Lnotcovariant;

  {
    // Return types
    Type *t1n = t1->next;
    Type *t2n = t2->next;

    if (t1n->equals(t2n))
	goto Lcovariant;
    if (t1n->ty == Tclass && t2n->ty == Tclass)
    {
	/* If same class type, but t2n is const, then it's
	 * covariant. Do this test first because it can work on
	 * forward references.
	 */
	if (((TypeClass *)t1n)->sym == ((TypeClass *)t2n)->sym &&
	    t2n->mod == MODconst)
	    goto Lcovariant;

	// If t1n is forward referenced:
	ClassDeclaration *cd = ((TypeClass *)t1n)->sym;
	if (!cd->baseClass && cd->baseclasses.dim && !cd->isInterfaceDeclaration())
	{
	    return 3;
	}
    }
    if (t1n->implicitConvTo(t2n))
	goto Lcovariant;
  }
    goto Lnotcovariant;

Lcovariant:
    /* Can convert mutable to const
     */
    if (t1->mod != t2->mod)
    {
	if (!(t1->mod & MODconst) && (t2->mod & MODconst))
	    goto Lnotcovariant;
	if (!(t1->mod & MODshared) && (t2->mod & MODshared))
	    goto Lnotcovariant;
    }

    /* Can convert pure to impure, and nothrow to throw
     */
    if (!t1->ispure && t2->ispure)
	goto Lnotcovariant;

    if (!t1->isnothrow && t2->isnothrow)
	goto Lnotcovariant;

    if (t1->isref != t2->isref)
	goto Lnotcovariant;

    //printf("\tcovaraint: 1\n");
    return 1;

Ldistinct:
    //printf("\tcovaraint: 0\n");
    return 0;

Lnotcovariant:
    //printf("\tcovaraint: 2\n");
    return 2;
}

void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{   unsigned char mc;

    //printf("TypeFunction::toDecoBuffer() this = %p %s\n", this, toChars());
    //static int nest; if (++nest == 50) *(char*)0=0;
    if (inuse)
    {	inuse = 2;		// flag error to caller
	return;
    }
    inuse++;
#if 1
    if (mod & MODshared)
	buf->writeByte('O');
    if (mod & MODconst)
	buf->writeByte('x');
    else if (mod & MODinvariant)
	buf->writeByte('y');
#endif
    switch (linkage)
    {
	case LINKd:		mc = 'F';	break;
	case LINKc:		mc = 'U';	break;
	case LINKwindows:	mc = 'W';	break;
	case LINKpascal:	mc = 'V';	break;
	case LINKcpp:		mc = 'R';	break;

    // LDC
    case LINKintrinsic: mc = 'Q';   break;

	default:
	    assert(0);
    }
    buf->writeByte(mc);
    if (ispure || isnothrow || isproperty || isref)
    {
	if (ispure)
	    buf->writestring("Na");
	if (isnothrow)
	    buf->writestring("Nb");
	if (isref)
	    buf->writestring("Nc");
	if (isproperty)
	    buf->writestring("Nd");
    }

    // LDC: if we're not producing a mangle string, add the this
    // type to prevent merging different member function
    if (!mangle && funcdecl)
    {
        if (funcdecl->needThis())
        {
            AggregateDeclaration* ad = funcdecl->isMember2();
            buf->writeByte('M');
            ad->type->toDecoBuffer(buf, false);
        }
        /* BUG This causes problems with delegate types
           On the other hand, the llvm type for nested functions *is* different
           so not doing anything here may be lead to bugs!
           A sane solution would be DtoType(Dsymbol)...
        if (funcdecl->isNested())
        {
            buf->writeByte('M');
            if (funcdecl->toParent2() && funcdecl->toParent2()->isFuncDeclaration())
            {
                FuncDeclaration* fd = funcdecl->toParent2()->isFuncDeclaration();
                fd->type->toDecoBuffer(buf, false);
            }
        }*/
    }

    // Write argument types
    Argument::argsToDecoBuffer(buf, parameters, mangle);
    //if (buf->data[buf->offset - 1] == '@') halt();
    buf->writeByte('Z' - varargs);	// mark end of arg list
    next->toDecoBuffer(buf, mangle);
    inuse--;
}

void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
{
    //printf("TypeFunction::toCBuffer() this = %p\n", this);
    const char *p = NULL;

    if (inuse)
    {	inuse = 2;		// flag error to caller
	return;
    }
    inuse++;

    /* Use 'storage class' style for attributes
     */
    if (mod & MODconst)
	buf->writestring("const ");
    if (mod & MODinvariant)
	buf->writestring("immutable ");
    if (mod & MODshared)
	buf->writestring("shared ");

    if (ispure)
	buf->writestring("pure ");
    if (isnothrow)
	buf->writestring("nothrow ");
    if (isproperty)
	buf->writestring("@property ");
    if (isref)
	buf->writestring("ref ");

    if (next && (!ident || ident->toHChars2() == ident->toChars()))
	next->toCBuffer2(buf, hgs, 0);
    if (hgs->ddoc != 1)
    {
	switch (linkage)
	{
	    case LINKd:		p = NULL;	break;
	    case LINKc:		p = "C ";	break;
	    case LINKwindows:	p = "Windows ";	break;
	    case LINKpascal:	p = "Pascal ";	break;
	    case LINKcpp:	p = "C++ ";	break;

        // LDC
        case LINKintrinsic: p = "Intrinsic"; break;

	    default:
		assert(0);
	}
    }

    if (!hgs->hdrgen && p)
	buf->writestring(p);
    if (ident)
    {   buf->writeByte(' ');
	buf->writestring(ident->toHChars2());
    }
    Argument::argsToCBuffer(buf, hgs, parameters, varargs);
    inuse--;
}

void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    //printf("TypeFunction::toCBuffer2() this = %p, ref = %d\n", this, isref);
    const char *p = NULL;

    if (inuse)
    {	inuse = 2;		// flag error to caller
	return;
    }
    inuse++;
    if (next)
	next->toCBuffer2(buf, hgs, 0);
    if (hgs->ddoc != 1)
    {
	switch (linkage)
	{
	    case LINKd:		p = NULL;	break;
	    case LINKc:		p = "C ";	break;
	    case LINKwindows:	p = "Windows ";	break;
	    case LINKpascal:	p = "Pascal ";	break;
	    case LINKcpp:	p = "C++ ";	break;

        // LDC
        case LINKintrinsic: p = "Intrinsic"; break;

	    default:
		assert(0);
	}
    }

    if (!hgs->hdrgen && p)
	buf->writestring(p);
    buf->writestring(" function");
    Argument::argsToCBuffer(buf, hgs, parameters, varargs);

    /* Use postfix style for attributes
     */
    if (mod != this->mod)
    {
	modToBuffer(buf);
    }
    if (ispure)
	buf->writestring(" pure");
    if (isnothrow)
	buf->writestring(" nothrow");
    if (isproperty)
	buf->writestring(" @property");
    if (isref)
	buf->writestring(" ref");

    inuse--;
}

Type *TypeFunction::semantic(Loc loc, Scope *sc)
{
    if (deco)			// if semantic() already run
    {
	//printf("already done\n");
	return this;
    }
    //printf("TypeFunction::semantic() this = %p\n", this);
    //printf("TypeFunction::semantic() %s, sc->stc = %x\n", toChars(), sc->stc);

    /* Copy in order to not mess up original.
     * This can produce redundant copies if inferring return type,
     * as semantic() will get called again on this.
     */
    TypeFunction *tf = (TypeFunction *)mem.malloc(sizeof(TypeFunction));
    memcpy(tf, this, sizeof(TypeFunction));
    if (parameters)
    {	tf->parameters = (Arguments *)parameters->copy();
	for (size_t i = 0; i < parameters->dim; i++)
	{   Argument *arg = (Argument *)parameters->data[i];
	    Argument *cpy = (Argument *)mem.malloc(sizeof(Argument));
	    memcpy(cpy, arg, sizeof(Argument));
	    tf->parameters->data[i] = (void *)cpy;
	}
    }

    if (sc->stc & STCpure)
	tf->ispure = TRUE;
    if (sc->stc & STCnothrow)
	tf->isnothrow = TRUE;
    if (sc->stc & STCref)
	tf->isref = TRUE;

    tf->linkage = sc->linkage;
    if (tf->next)
    {
	tf->next = tf->next->semantic(loc,sc);
	if (tf->next->toBasetype()->ty == Tsarray)
	{   error(loc, "functions cannot return static array %s", tf->next->toChars());
	    tf->next = Type::terror;
	}
	if (tf->next->toBasetype()->ty == Tfunction)
	{   error(loc, "functions cannot return a function");
	    tf->next = Type::terror;
	}
	if (tf->next->toBasetype()->ty == Ttuple)
	{   error(loc, "functions cannot return a tuple");
	    tf->next = Type::terror;
	}
	if (tf->next->isauto() && !(sc->flags & SCOPEctor))
	    error(loc, "functions cannot return scope %s", tf->next->toChars());
    }

    if (tf->parameters)
    {	size_t dim = Argument::dim(tf->parameters);

	for (size_t i = 0; i < dim; i++)
	{   Argument *arg = Argument::getNth(tf->parameters, i);

	    tf->inuse++;
	    arg->type = arg->type->semantic(loc,sc);
	    if (tf->inuse == 1) tf->inuse--;

	    arg->type = arg->type->addStorageClass(arg->storageClass);

	    if (arg->storageClass & (STCauto | STCalias | STCstatic))
	    {
		if (!arg->type)
		    continue;
	    }
// Possible merge conflict
	    Type *t = arg->type->toBasetype();

	    if (arg->storageClass & (STCout | STCref | STClazy))
	    {
		if (t->ty == Tsarray)
		    error(loc, "cannot have out or ref parameter of type %s", t->toChars());
		if (arg->storageClass & STCout && arg->type->mod)
		    error(loc, "cannot have const/invariant out parameter of type %s", t->toChars());
	    }
	    if (!(arg->storageClass & STClazy) && t->ty == Tvoid)
		error(loc, "cannot have parameter of type %s", arg->type->toChars());

	    if (arg->defaultArg)
	    {
		arg->defaultArg = arg->defaultArg->semantic(sc);
		arg->defaultArg = resolveProperties(sc, arg->defaultArg);
		arg->defaultArg = arg->defaultArg->implicitCastTo(sc, arg->type);
	    }

	    /* If arg turns out to be a tuple, the number of parameters may
	     * change.
	     */
	    if (t->ty == Ttuple)
	    {	dim = Argument::dim(tf->parameters);
		i--;
	    }
	}
    }
    if (tf->next)
	tf->deco = tf->merge()->deco;

    if (tf->inuse)
    {	error(loc, "recursive type");
	tf->inuse = 0;
	return terror;
    }

    if (tf->varargs == 1 && tf->linkage != LINKd && Argument::dim(tf->parameters) == 0)
	error(loc, "variadic functions with non-D linkage must have at least one parameter");

    /* Don't return merge(), because arg identifiers and default args
     * can be different
     * even though the types match
     */
    return tf;
}

/********************************
 * 'args' are being matched to function 'this'
 * Determine match level.
 * Returns:
 *	MATCHxxxx
 */

int TypeFunction::callMatch(Expression *ethis, Expressions *args)
{
    //printf("TypeFunction::callMatch() %s\n", toChars());
    MATCH match = MATCHexact;		// assume exact match

    if (ethis)
    {	Type *t = ethis->type;
	if (t->toBasetype()->ty == Tpointer)
	    t = t->toBasetype()->nextOf();	// change struct* to struct
	if (t->mod != mod)
	{
	    if (mod == MODconst)
		match = MATCHconst;
	    else
		return MATCHnomatch;
	}
    }

    size_t nparams = Argument::dim(parameters);
    size_t nargs = args ? args->dim : 0;
    if (nparams == nargs)
	;
    else if (nargs > nparams)
    {
	if (varargs == 0)
	    goto Nomatch;		// too many args; no match
	match = MATCHconvert;		// match ... with a "conversion" match level
    }

    for (size_t u = 0; u < nparams; u++)
    {	MATCH m;
	Expression *arg;

	// BUG: what about out and ref?

	Argument *p = Argument::getNth(parameters, u);
	assert(p);
	if (u >= nargs)
	{
	    if (p->defaultArg)
		continue;
	    if (varargs == 2 && u + 1 == nparams)
		goto L1;
	    goto Nomatch;		// not enough arguments
	}
	arg = (Expression *)args->data[u];
	assert(arg);

	// Non-lvalues do not match ref or out parameters
	if (p->storageClass & (STCref | STCout) && !arg->isLvalue())
	    goto Nomatch;

	if (p->storageClass & STClazy && p->type->ty == Tvoid &&
		arg->type->ty != Tvoid)
	    m = MATCHconvert;
	else
	    m = arg->implicitConvTo(p->type);
	//printf("\tm = %d\n", m);
	if (m == MATCHnomatch)			// if no match
	{
	  L1:
	    if (varargs == 2 && u + 1 == nparams)	// if last varargs param
	    {	Type *tb = p->type->toBasetype();
		TypeSArray *tsa;
		dinteger_t sz;

		switch (tb->ty)
		{
		    case Tsarray:
			tsa = (TypeSArray *)tb;
			sz = tsa->dim->toInteger();
			if (sz != nargs - u)
			    goto Nomatch;
		    case Tarray:
		    {	TypeArray *ta = (TypeArray *)tb;
			for (; u < nargs; u++)
			{
			    arg = (Expression *)args->data[u];
			    assert(arg);
#if 1
			    /* If lazy array of delegates,
			     * convert arg(s) to delegate(s)
			     */
			    Type *tret = p->isLazyArray();
			    if (tret)
			    {
				if (ta->next->equals(arg->type))
				{   m = MATCHexact;
				}
				else
				{
				    m = arg->implicitConvTo(tret);
				    if (m == MATCHnomatch)
				    {
					if (tret->toBasetype()->ty == Tvoid)
					    m = MATCHconvert;
				    }
				}
			    }
			    else
				m = arg->implicitConvTo(ta->next);
#else
			    m = arg->implicitConvTo(ta->next);
#endif
			    if (m == MATCHnomatch)
				goto Nomatch;
			    if (m < match)
				match = m;
			}
			goto Ldone;
		    }
		    case Tclass:
			// Should see if there's a constructor match?
			// Or just leave it ambiguous?
			goto Ldone;

		    default:
			goto Nomatch;
		}
	    }
	    goto Nomatch;
	}
	if (m < match)
	    match = m;			// pick worst match
    }

Ldone:
    //printf("match = %d\n", match);
    return match;

Nomatch:
    //printf("no match\n");
    return MATCHnomatch;
}

Type *TypeFunction::reliesOnTident()
{
    if (parameters)
    {
	for (size_t i = 0; i < parameters->dim; i++)
	{   Argument *arg = (Argument *)parameters->data[i];
	    Type *t = arg->type->reliesOnTident();
	    if (t)
		return t;
	}
    }
    return next->reliesOnTident();
}

/***************************
 * Examine function signature for parameter p and see if
 * p can 'escape' the scope of the function.
 */

bool TypeFunction::parameterEscapes(Argument *p)
{

    /* Scope parameters do not escape.
     * Allow 'lazy' to imply 'scope' -
     * lazy parameters can be passed along
     * as lazy parameters to the next function, but that isn't
     * escaping.
     */
    if (p->storageClass & (STCscope | STClazy))
	return FALSE;

    if (ispure)
    {	/* With pure functions, we need only be concerned if p escapes
	 * via any return statement.
	 */
	Type* tret = nextOf()->toBasetype();
	if (!isref && !tret->hasPointers())
	{   /* The result has no references, so p could not be escaping
	     * that way.
	     */
	    return FALSE;
	}
    }

    /* Assume it escapes in the absence of better information.
     */
    return TRUE;
}

/***************************** TypeDelegate *****************************/

TypeDelegate::TypeDelegate(Type *t)
    : TypeNext(Tfunction, t)
{
    ty = Tdelegate;
}

Type *TypeDelegate::syntaxCopy()
{
    Type *t = next->syntaxCopy();
    if (t == next)
	t = this;
    else
    {	t = new TypeDelegate(t);
	t->mod = mod;
    }
    return t;
}

Type *TypeDelegate::semantic(Loc loc, Scope *sc)
{
    if (deco)			// if semantic() already run
    {
	//printf("already done\n");
	return this;
    }
    next = next->semantic(loc,sc);
    return merge();
}

d_uns64 TypeDelegate::size(Loc loc)
{
    return PTRSIZE * 2;
}

// LDC added, no reason to align to 2*PTRSIZE
unsigned TypeDelegate::alignsize()
{
    // A Delegate consists of two ptr values, so align it on pointer size
    // boundary
    return PTRSIZE;
}

void TypeDelegate::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    TypeFunction *tf = (TypeFunction *)next;

    tf->next->toCBuffer2(buf, hgs, 0);
    buf->writestring(" delegate");
    Argument::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs);
}

Expression *TypeDelegate::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeDelegate::defaultInit() '%s'\n", toChars());
#endif
    Expression *e;
    e = new NullExp(loc);
    e->type = this;
    return e;
}

int TypeDelegate::isZeroInit(Loc loc)
{
    return 1;
}

int TypeDelegate::checkBoolean()
{
    return TRUE;
}

Expression *TypeDelegate::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (ident == Id::ptr)
    {
    e = new GEPExp(e->loc, e, ident, 0);
	e->type = tvoidptr;
	return e;
    }
    else if (ident == Id::funcptr)
    {
    e = new GEPExp(e->loc, e, ident, 1);
    e->type = tvoidptr;
	return e;
    }
    else
    {
	e = Type::dotExp(sc, e, ident);
    }
    return e;
}

int TypeDelegate::hasPointers()
{
    return TRUE;
}



/***************************** TypeQualified *****************************/

TypeQualified::TypeQualified(TY ty, Loc loc)
    : Type(ty)
{
    this->loc = loc;
}

void TypeQualified::syntaxCopyHelper(TypeQualified *t)
{
    //printf("TypeQualified::syntaxCopyHelper(%s) %s\n", t->toChars(), toChars());
    idents.setDim(t->idents.dim);
    for (int i = 0; i < idents.dim; i++)
    {
	Identifier *id = (Identifier *)t->idents.data[i];
	if (id->dyncast() == DYNCAST_DSYMBOL)
	{
	    TemplateInstance *ti = (TemplateInstance *)id;

	    ti = (TemplateInstance *)ti->syntaxCopy(NULL);
	    id = (Identifier *)ti;
	}
	idents.data[i] = id;
    }
}


void TypeQualified::addIdent(Identifier *ident)
{
    idents.push(ident);
}

void TypeQualified::toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs)
{
    int i;

    for (i = 0; i < idents.dim; i++)
    {	Identifier *id = (Identifier *)idents.data[i];

	buf->writeByte('.');

	if (id->dyncast() == DYNCAST_DSYMBOL)
	{
	    TemplateInstance *ti = (TemplateInstance *)id;
	    ti->toCBuffer(buf, hgs);
	}
	else
	    buf->writestring(id->toChars());
    }
}

d_uns64 TypeQualified::size(Loc loc)
{
    error(this->loc, "size of type %s is not known", toChars());
    return 1;
}

/*************************************
 * Takes an array of Identifiers and figures out if
 * it represents a Type or an Expression.
 * Output:
 *	if expression, *pe is set
 *	if type, *pt is set
 */

void TypeQualified::resolveHelper(Loc loc, Scope *sc,
	Dsymbol *s, Dsymbol *scopesym,
	Expression **pe, Type **pt, Dsymbol **ps)
{
    VarDeclaration *v;
    EnumMember *em;
    TupleDeclaration *td;
    Expression *e;

#if 0
    printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, toChars());
    if (scopesym)
	printf("\tscopesym = '%s'\n", scopesym->toChars());
#endif
    *pe = NULL;
    *pt = NULL;
    *ps = NULL;
    if (s)
    {
	//printf("\t1: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
	s->checkDeprecated(loc, sc);		// check for deprecated aliases
	s = s->toAlias();
	//printf("\t2: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
	for (int i = 0; i < idents.dim; i++)
	{
	    Identifier *id = (Identifier *)idents.data[i];
	    Dsymbol *sm = s->searchX(loc, sc, id);
	    //printf("\t3: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
	    //printf("\tgetType = '%s'\n", s->getType()->toChars());
	    if (!sm)
	    {	Type *t;

		v = s->isVarDeclaration();
		if (v && id == Id::length)
		{
		    e = v->getConstInitializer();
		    if (!e)
			e = new VarExp(loc, v);
		    t = e->type;
		    if (!t)
			goto Lerror;
		    goto L3;
		}
		t = s->getType();
		if (!t && s->isDeclaration())
		    t = s->isDeclaration()->type;
		if (t)
		{
		    sm = t->toDsymbol(sc);
		    if (sm)
		    {	sm = sm->search(loc, id, 0);
			if (sm)
			    goto L2;
		    }
		    //e = t->getProperty(loc, id);
		    e = new TypeExp(loc, t);
		    e = t->dotExp(sc, e, id);
		    i++;
		L3:
		    for (; i < idents.dim; i++)
		    {
			id = (Identifier *)idents.data[i];
			//printf("e: '%s', id: '%s', type = %p\n", e->toChars(), id->toChars(), e->type);
			if (id == Id::offsetof)
			{   e = new DotIdExp(e->loc, e, id);
			    e = e->semantic(sc);
			}
			else
			    e = e->type->dotExp(sc, e, id);
		    }
		    *pe = e;
		}
		else
	          Lerror:
		    error(loc, "identifier '%s' of '%s' is not defined", id->toChars(), toChars());
		return;
	    }
	L2:
	    s = sm->toAlias();
	}

	v = s->isVarDeclaration();
	if (v)
	{
#if 0
	    // It's not a type, it's an expression
	    Expression *e = v->getConstInitializer();
	    if (e)
	    {
		*pe = e->copy();	// make copy so we can change loc
		(*pe)->loc = loc;
	    }
	    else
#endif
	    {
#if 0
		WithScopeSymbol *withsym;
		if (scopesym && (withsym = scopesym->isWithScopeSymbol()) != NULL)
		{
		    // Same as wthis.ident
		    e = new VarExp(loc, withsym->withstate->wthis);
		    e = new DotIdExp(loc, e, ident);
		    //assert(0);	// BUG: should handle this
		}
		else
#endif
		    *pe = new VarExp(loc, v);
	    }
	    return;
	}
	em = s->isEnumMember();
	if (em)
	{
	    // It's not a type, it's an expression
	    *pe = em->value->copy();
	    return;
	}

L1:
	Type *t = s->getType();
	if (!t)
	{
	    // If the symbol is an import, try looking inside the import
	    Import *si;

	    si = s->isImport();
	    if (si)
	    {
		s = si->search(loc, s->ident, 0);
		if (s && s != si)
		    goto L1;
		s = si;
	    }
	    *ps = s;
	    return;
	}
	if (t->ty == Tinstance && t != this && !t->deco)
	{   error(loc, "forward reference to '%s'", t->toChars());
	    return;
	}

	if (t != this)
	{
	    if (t->reliesOnTident())
	    {
		Scope *scx;

		for (scx = sc; 1; scx = scx->enclosing)
		{
		    if (!scx)
		    {   error(loc, "forward reference to '%s'", t->toChars());
			return;
		    }
		    if (scx->scopesym == scopesym)
			break;
		}
		t = t->semantic(loc, scx);
		//((TypeIdentifier *)t)->resolve(loc, scx, pe, &t, ps);
	    }
	}
	if (t->ty == Ttuple)
	    *pt = t;
	else
	    *pt = t->merge();
    }
    if (!s)
    {
	error(loc, "identifier '%s' is not defined", toChars());
    }
}

/***************************** TypeIdentifier *****************************/

TypeIdentifier::TypeIdentifier(Loc loc, Identifier *ident)
    : TypeQualified(Tident, loc)
{
    this->ident = ident;
}


Type *TypeIdentifier::syntaxCopy()
{
    TypeIdentifier *t;

    t = new TypeIdentifier(loc, ident);
    t->syntaxCopyHelper(this);
    t->mod = mod;
    return t;
}

void TypeIdentifier::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{   unsigned len;
    char *name;

    Type::toDecoBuffer(buf, flag, mangle);
    name = ident->toChars();
    len = strlen(name);
    buf->printf("%d%s", len, name);
}

void TypeIdentifier::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring(this->ident->toChars());
    toCBuffer2Helper(buf, hgs);
}

/*************************************
 * Takes an array of Identifiers and figures out if
 * it represents a Type or an Expression.
 * Output:
 *	if expression, *pe is set
 *	if type, *pt is set
 */

void TypeIdentifier::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
    Dsymbol *scopesym;

    //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, toChars());
    Dsymbol *s = sc->search(loc, ident, &scopesym);
    resolveHelper(loc, sc, s, scopesym, pe, pt, ps);
    if (*pt)
	(*pt) = (*pt)->addMod(mod);
}

/*****************************************
 * See if type resolves to a symbol, if so,
 * return that symbol.
 */

Dsymbol *TypeIdentifier::toDsymbol(Scope *sc)
{
    //printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
    if (!sc)
	return NULL;
    //printf("ident = '%s'\n", ident->toChars());

    Dsymbol *scopesym;
    Dsymbol *s = sc->search(loc, ident, &scopesym);
    if (s)
    {
	for (int i = 0; i < idents.dim; i++)
	{
	    Identifier *id = (Identifier *)idents.data[i];
	    s = s->searchX(loc, sc, id);
	    if (!s)                 // failed to find a symbol
	    {	//printf("\tdidn't find a symbol\n");
		break;
	    }
	}
    }
    return s;
}

Type *TypeIdentifier::semantic(Loc loc, Scope *sc)
{
    Type *t;
    Expression *e;
    Dsymbol *s;

    //printf("TypeIdentifier::semantic(%s)\n", toChars());
    resolve(loc, sc, &e, &t, &s);
    if (t)
    {
	//printf("\tit's a type %d, %s, %s\n", t->ty, t->toChars(), t->deco);

	if (t->ty == Ttypedef)
	{   TypeTypedef *tt = (TypeTypedef *)t;

	    if (tt->sym->sem == 1)
		error(loc, "circular reference of typedef %s", tt->toChars());
	}
	t = t->addMod(mod);
    }
    else
    {
#ifdef DEBUG
	if (!global.gag)
	    printf("1: ");
#endif
	if (s)
	{
	    s->error(loc, "is used as a type");
	    //halt();
	}
	else
	    error(loc, "%s is used as a type", toChars());
	t = tvoid;
    }
    //t->print();
    return t;
}

Type *TypeIdentifier::reliesOnTident()
{
    return this;
}

Expression *TypeIdentifier::toExpression()
{
    Expression *e = new IdentifierExp(loc, ident);
    for (int i = 0; i < idents.dim; i++)
    {
	Identifier *id = (Identifier *)idents.data[i];
	e = new DotIdExp(loc, e, id);
    }

    return e;
}

/***************************** TypeInstance *****************************/

TypeInstance::TypeInstance(Loc loc, TemplateInstance *tempinst)
    : TypeQualified(Tinstance, loc)
{
    this->tempinst = tempinst;
}

Type *TypeInstance::syntaxCopy()
{
    //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim);
    TypeInstance *t;

    t = new TypeInstance(loc, (TemplateInstance *)tempinst->syntaxCopy(NULL));
    t->syntaxCopyHelper(this);
    t->mod = mod;
    return t;
}


void TypeInstance::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    tempinst->toCBuffer(buf, hgs);
    toCBuffer2Helper(buf, hgs);
}

void TypeInstance::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
    // Note close similarity to TypeIdentifier::resolve()

    Dsymbol *s;

    *pe = NULL;
    *pt = NULL;
    *ps = NULL;

#if 0
    if (!idents.dim)
    {
	error(loc, "template instance '%s' has no identifier", toChars());
	return;
    }
#endif
    //id = (Identifier *)idents.data[0];
    //printf("TypeInstance::resolve(sc = %p, idents = '%s')\n", sc, id->toChars());
    s = tempinst;
    if (s)
	s->semantic(sc);
    resolveHelper(loc, sc, s, NULL, pe, pt, ps);
    if (*pt)
	*pt = (*pt)->addMod(mod);
    //printf("pt = '%s'\n", (*pt)->toChars());
}

Type *TypeInstance::semantic(Loc loc, Scope *sc)
{
    Type *t;
    Expression *e;
    Dsymbol *s;

    //printf("TypeInstance::semantic(%s)\n", toChars());

    if (sc->parameterSpecialization)
    {
	unsigned errors = global.errors;
	global.gag++;

	resolve(loc, sc, &e, &t, &s);

	global.gag--;
	if (errors != global.errors)
	{   if (global.gag == 0)
		global.errors = errors;
	    return this;
	}
    }
    else
	resolve(loc, sc, &e, &t, &s);

    if (!t)
    {
#ifdef DEBUG
	printf("2: ");
#endif
	error(loc, "%s is used as a type", toChars());
	t = tvoid;
    }
    return t;
}

Dsymbol *TypeInstance::toDsymbol(Scope *sc)
{
    Type *t;
    Expression *e;
    Dsymbol *s;

    //printf("TypeInstance::semantic(%s)\n", toChars());

    if (sc->parameterSpecialization)
    {
	unsigned errors = global.errors;
	global.gag++;

	resolve(loc, sc, &e, &t, &s);

	global.gag--;
	if (errors != global.errors)
	{   if (global.gag == 0)
		global.errors = errors;
	    return NULL;
	}
    }
    else
	resolve(loc, sc, &e, &t, &s);

    return s;
}


/***************************** TypeTypeof *****************************/

TypeTypeof::TypeTypeof(Loc loc, Expression *exp)
	: TypeQualified(Ttypeof, loc)
{
    this->exp = exp;
}

Type *TypeTypeof::syntaxCopy()
{
    //printf("TypeTypeof::syntaxCopy() %s\n", toChars());
    TypeTypeof *t;

    t = new TypeTypeof(loc, exp->syntaxCopy());
    t->syntaxCopyHelper(this);
    t->mod = mod;
    return t;
}

Dsymbol *TypeTypeof::toDsymbol(Scope *sc)
{
    Type *t;

    t = semantic(loc, sc);
    if (t == this)
	return NULL;
    return t->toDsymbol(sc);
}

void TypeTypeof::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring("typeof(");
    exp->toCBuffer(buf, hgs);
    buf->writeByte(')');
    toCBuffer2Helper(buf, hgs);
}

Type *TypeTypeof::semantic(Loc loc, Scope *sc)
{   Expression *e;
    Type *t;

    //printf("TypeTypeof::semantic() %p\n", this);

    //static int nest; if (++nest == 50) *(char*)0=0;

#if 0
    /* Special case for typeof(this) and typeof(super) since both
     * should work even if they are not inside a non-static member function
     */
    if (exp->op == TOKthis || exp->op == TOKsuper)
    {
	// Find enclosing struct or class
	for (Dsymbol *s = sc->parent; 1; s = s->parent)
	{
	    ClassDeclaration *cd;
	    StructDeclaration *sd;

	    if (!s)
	    {
		error(loc, "%s is not in a struct or class scope", exp->toChars());
		goto Lerr;
	    }
	    cd = s->isClassDeclaration();
	    if (cd)
	    {
		if (exp->op == TOKsuper)
		{
		    cd = cd->baseClass;
		    if (!cd)
		    {	error(loc, "class %s has no 'super'", s->toChars());
			goto Lerr;
		    }
		}
		t = cd->type;
		break;
	    }
	    sd = s->isStructDeclaration();
	    if (sd)
	    {
		if (exp->op == TOKsuper)
		{
		    error(loc, "struct %s has no 'super'", sd->toChars());
		    goto Lerr;
		}
		t = sd->type->pointerTo();
		break;
	    }
	}
    }
    else
#endif
    {
	sc->intypeof++;
	exp = exp->semantic(sc);
	sc->intypeof--;
	if (exp->op == TOKtype)
	{
	    error(loc, "argument %s to typeof is not an expression", exp->toChars());
	}
	t = exp->type;
	if (!t)
	{
	    error(loc, "expression (%s) has no type", exp->toChars());
	    goto Lerr;
	}
	if (t->ty == Ttypeof)
	    error(loc, "forward reference to %s", toChars());

	/* typeof should reflect the true type,
	 * not what 'auto' would have gotten us.
	 */
	//t = t->toHeadMutable();
    }
    if (idents.dim)
    {
	Dsymbol *s = t->toDsymbol(sc);
	for (size_t i = 0; i < idents.dim; i++)
	{
	    if (!s)
		break;
	    Identifier *id = (Identifier *)idents.data[i];
	    s = s->searchX(loc, sc, id);
	}

	if (s)
	{
	    t = s->getType();
	    if (!t)
	    {	error(loc, "%s is not a type", s->toChars());
		goto Lerr;
	    }
	}
	else
	{   error(loc, "cannot resolve .property for %s", toChars());
	    goto Lerr;
	}
    }
    return t;

Lerr:
    return tvoid;
}

d_uns64 TypeTypeof::size(Loc loc)
{
    if (exp->type)
	return exp->type->size(loc);
    else
	return TypeQualified::size(loc);
}



/***************************** TypeReturn *****************************/

TypeReturn::TypeReturn(Loc loc)
	: TypeQualified(Treturn, loc)
{
}

Type *TypeReturn::syntaxCopy()
{
    TypeReturn *t = new TypeReturn(loc);
    t->syntaxCopyHelper(this);
    t->mod = mod;
    return t;
}

Dsymbol *TypeReturn::toDsymbol(Scope *sc)
{
    Type *t = semantic(0, sc);
    if (t == this)
	return NULL;
    return t->toDsymbol(sc);
}

Type *TypeReturn::semantic(Loc loc, Scope *sc)
{
    Type *t;
    if (!sc->func)
    {	error(loc, "typeof(return) must be inside function");
	goto Lerr;
    }
    t = sc->func->type->nextOf();
    t = t->addMod(mod);

    if (idents.dim)
    {
	Dsymbol *s = t->toDsymbol(sc);
	for (size_t i = 0; i < idents.dim; i++)
	{
	    if (!s)
		break;
	    Identifier *id = (Identifier *)idents.data[i];
	    s = s->searchX(loc, sc, id);
	}
	if (s)
	{
	    t = s->getType();
	    if (!t)
	    {	error(loc, "%s is not a type", s->toChars());
		goto Lerr;
	    }
	}
	else
	{   error(loc, "cannot resolve .property for %s", toChars());
	    goto Lerr;
	}
    }
    return t;

Lerr:
    return terror;
}

void TypeReturn::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring("typeof(return)");
    toCBuffer2Helper(buf, hgs);
}


/***************************** TypeEnum *****************************/

TypeEnum::TypeEnum(EnumDeclaration *sym)
	: Type(Tenum)
{
    this->sym = sym;
}

char *TypeEnum::toChars()
{
    if (mod)
	return Type::toChars();
    return sym->toChars();
}

Type *TypeEnum::syntaxCopy()
{
    return this;
}

Type *TypeEnum::semantic(Loc loc, Scope *sc)
{
    //printf("TypeEnum::semantic() %s\n", toChars());
    //sym->semantic(sc);
    return merge();
}

d_uns64 TypeEnum::size(Loc loc)
{
    if (!sym->memtype)
    {
	error(loc, "enum %s is forward referenced", sym->toChars());
	return 4;
    }
    return sym->memtype->size(loc);
}

unsigned TypeEnum::alignsize()
{
    if (!sym->memtype)
    {
#ifdef DEBUG
	printf("1: ");
#endif
	error(0, "enum %s is forward referenced", sym->toChars());
	return 4;
    }
    return sym->memtype->alignsize();
}

Dsymbol *TypeEnum::toDsymbol(Scope *sc)
{
    return sym;
}

Type *TypeEnum::toBasetype()
{
    if (!sym->memtype)
    {
#ifdef DEBUG
	printf("2: ");
#endif
	error(sym->loc, "enum %s is forward referenced", sym->toChars());
	return tint32;
    }
    return sym->memtype->toBasetype();
}

void TypeEnum::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    const char *name = sym->mangle();
    Type::toDecoBuffer(buf, flag, mangle);
    buf->printf("%s", name);
}

void TypeEnum::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring(sym->toChars());
}

Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars());
#endif
    Dsymbol *s = sym->search(e->loc, ident, 0);
    if (!s)
    {
	if (ident == Id::max ||
	    ident == Id::min ||
	    ident == Id::init ||
	    ident == Id::stringof ||
	    !sym->memtype
	   )
	{
	    return getProperty(e->loc, ident);
	}
	return sym->memtype->dotExp(sc, e, ident);
    }
    EnumMember *m = s->isEnumMember();
    Expression *em = m->value->copy();
    em->loc = e->loc;
    return em;
}

Expression *TypeEnum::getProperty(Loc loc, Identifier *ident)
{   Expression *e;

    if (ident == Id::max)
    {
	if (!sym->maxval)
	    goto Lfwd;
	e = sym->maxval;
    }
    else if (ident == Id::min)
    {
	if (!sym->minval)
	    goto Lfwd;
	e = sym->minval;
    }
    else if (ident == Id::init)
    {
	e = defaultInit(loc);
    }
    else if (ident == Id::stringof)
    {	char *s = toChars();
	e = new StringExp(loc, s, strlen(s), 'c');
	Scope sc;
	e = e->semantic(&sc);
    }
    else
    {
	e = toBasetype()->getProperty(loc, ident);
    }
    return e;

Lfwd:
    error(loc, "forward reference of %s.%s", toChars(), ident->toChars());
    return new ErrorExp();
}

int TypeEnum::isintegral()
{
    return 1;
}

int TypeEnum::isfloating()
{
    return 0;
}

int TypeEnum::isunsigned()
{
    return sym->memtype->isunsigned();
}

int TypeEnum::isscalar()
{
    return 1;
    //return sym->memtype->isscalar();
}

MATCH TypeEnum::implicitConvTo(Type *to)
{   MATCH m;

    //printf("TypeEnum::implicitConvTo()\n");
    if (ty == to->ty && sym == ((TypeEnum *)to)->sym)
	m = (mod == to->mod) ? MATCHexact : MATCHconst;
    else if (sym->memtype->implicitConvTo(to))
	m = MATCHconvert;	// match with conversions
    else
	m = MATCHnomatch;	// no match
    return m;
}

MATCH TypeEnum::constConv(Type *to)
{
    if (equals(to))
	return MATCHexact;
    if (ty == to->ty && sym == ((TypeEnum *)to)->sym &&
	to->mod == MODconst)
	return MATCHconst;
    return MATCHnomatch;
}


Expression *TypeEnum::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeEnum::defaultInit() '%s'\n", toChars());
#endif
    // Initialize to first member of enum
    //printf("%s\n", sym->defaultval->type->toChars());
    if (!sym->defaultval)
    {
	error(loc, "forward reference of %s.init", toChars());
	return new ErrorExp();
    }
    return sym->defaultval;
}

int TypeEnum::isZeroInit(Loc loc)
{
    if (!sym->defaultval)
    {
#ifdef DEBUG
	printf("3: ");
#endif
	error(loc, "enum %s is forward referenced", sym->toChars());
	return 0;
    }
    return sym->defaultval->isBool(FALSE);
}

int TypeEnum::hasPointers()
{
    return toBasetype()->hasPointers();
}

/***************************** TypeTypedef *****************************/

TypeTypedef::TypeTypedef(TypedefDeclaration *sym)
	: Type(Ttypedef)
{
    this->sym = sym;
}

Type *TypeTypedef::syntaxCopy()
{
    return this;
}

char *TypeTypedef::toChars()
{
    return Type::toChars();
}

Type *TypeTypedef::semantic(Loc loc, Scope *sc)
{
    //printf("TypeTypedef::semantic(%s), sem = %d\n", toChars(), sym->sem);
    sym->semantic(sc);
    return merge();
}

d_uns64 TypeTypedef::size(Loc loc)
{
    return sym->basetype->size(loc);
}

unsigned TypeTypedef::alignsize()
{
    return sym->basetype->alignsize();
}

Dsymbol *TypeTypedef::toDsymbol(Scope *sc)
{
    return sym;
}

Type *TypeTypedef::toHeadMutable()
{
    if (!mod)
	return this;

    Type *tb = toBasetype();
    Type *t = tb->toHeadMutable();
    if (t->equals(tb))
	return this;
    else
	return mutableOf();
}

void TypeTypedef::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    Type::toDecoBuffer(buf, flag, mangle);
    const char *name = sym->mangle();
    buf->printf("%s", name);
}

void TypeTypedef::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    //printf("TypeTypedef::toCBuffer2() '%s'\n", sym->toChars());
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring(sym->toChars());
}

Expression *TypeTypedef::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeTypedef::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars());
#endif
    if (ident == Id::init)
    {
	return Type::dotExp(sc, e, ident);
    }
    return sym->basetype->dotExp(sc, e, ident);
}

Expression *TypeTypedef::getProperty(Loc loc, Identifier *ident)
{
#if LOGDOTEXP
    printf("TypeTypedef::getProperty(ident = '%s') '%s'\n", ident->toChars(), toChars());
#endif
    if (ident == Id::init)
    {
	return Type::getProperty(loc, ident);
    }
    return sym->basetype->getProperty(loc, ident);
}

int TypeTypedef::isintegral()
{
    //printf("TypeTypedef::isintegral()\n");
    //printf("sym = '%s'\n", sym->toChars());
    //printf("basetype = '%s'\n", sym->basetype->toChars());
    return sym->basetype->isintegral();
}

int TypeTypedef::isfloating()
{
    return sym->basetype->isfloating();
}

int TypeTypedef::isreal()
{
    return sym->basetype->isreal();
}

int TypeTypedef::isimaginary()
{
    return sym->basetype->isimaginary();
}

int TypeTypedef::iscomplex()
{
    return sym->basetype->iscomplex();
}

int TypeTypedef::isunsigned()
{
    return sym->basetype->isunsigned();
}

int TypeTypedef::isscalar()
{
    return sym->basetype->isscalar();
}

int TypeTypedef::isAssignable()
{
    return sym->basetype->isAssignable();
}

int TypeTypedef::checkBoolean()
{
    return sym->basetype->checkBoolean();
}

Type *TypeTypedef::toBasetype()
{
    if (sym->inuse)
    {
	sym->error("circular definition");
	sym->basetype = Type::terror;
	return Type::terror;
    }
    sym->inuse = 1;
    Type *t = sym->basetype->toBasetype();
    sym->inuse = 0;
    t = t->addMod(mod);
    return t;
}

MATCH TypeTypedef::implicitConvTo(Type *to)
{   MATCH m;

    //printf("TypeTypedef::implicitConvTo(to = %s) %s\n", to->toChars(), toChars());
    if (equals(to))
	m = MATCHexact;		// exact match
    else if (sym->basetype->implicitConvTo(to))
	m = MATCHconvert;	// match with conversions
    else if (ty == to->ty && sym == ((TypeTypedef *)to)->sym)
    {
	m = constConv(to);
    }
    else
	m = MATCHnomatch;	// no match
    return m;
}

MATCH TypeTypedef::constConv(Type *to)
{
    if (equals(to))
	return MATCHexact;
    if (ty == to->ty && sym == ((TypeTypedef *)to)->sym)
	return sym->basetype->implicitConvTo(((TypeTypedef *)to)->sym->basetype);
    return MATCHnomatch;
}


Expression *TypeTypedef::defaultInit(Loc loc)
{   Expression *e;
    Type *bt;

#if LOGDEFAULTINIT
    printf("TypeTypedef::defaultInit() '%s'\n", toChars());
#endif
    if (sym->init)
    {
	//sym->init->toExpression()->print();
	return sym->init->toExpression();
    }
    bt = sym->basetype;
    e = bt->defaultInit(loc);
    e->type = this;
    while (bt->ty == Tsarray)
    {	TypeSArray *tsa = (TypeSArray *)bt;
	e->type = tsa->next;
	bt = tsa->next->toBasetype();
    }
    return e;
}

int TypeTypedef::isZeroInit(Loc loc)
{
    if (sym->init)
    {
	if (sym->init->isVoidInitializer())
	    return 1;		// initialize voids to 0
	Expression *e = sym->init->toExpression();
	if (e && e->isBool(FALSE))
	    return 1;
	return 0;		// assume not
    }
    if (sym->inuse)
    {
	sym->error("circular definition");
	sym->basetype = Type::terror;
    }
    sym->inuse = 1;
    int result = sym->basetype->isZeroInit(loc);
    sym->inuse = 0;
    return result;
}

int TypeTypedef::hasPointers()
{
    return toBasetype()->hasPointers();
}

/***************************** TypeStruct *****************************/

TypeStruct::TypeStruct(StructDeclaration *sym)
	: Type(Tstruct)
{
    this->sym = sym;

    // LDC
    this->unaligned = 0;
}

char *TypeStruct::toChars()
{
    //printf("sym.parent: %s, deco = %s\n", sym->parent->toChars(), deco);
    if (mod)
	return Type::toChars();
    TemplateInstance *ti = sym->parent->isTemplateInstance();
    if (ti && ti->toAlias() == sym)
    {
	return ti->toChars();
    }
    return sym->toChars();
}

Type *TypeStruct::syntaxCopy()
{
    return this;
}

Type *TypeStruct::semantic(Loc loc, Scope *sc)
{
    //printf("TypeStruct::semantic('%s')\n", sym->toChars());

    /* Cannot do semantic for sym because scope chain may not
     * be right.
     */
    //sym->semantic(sc);

    return merge();
}

d_uns64 TypeStruct::size(Loc loc)
{
    return sym->size(loc);
}

unsigned TypeStruct::alignsize()
{   unsigned sz;

    sym->size(0);		// give error for forward references
    sz = sym->alignsize;
    if (sz > sym->structalign)
	sz = sym->structalign;
    return sz;
}

Dsymbol *TypeStruct::toDsymbol(Scope *sc)
{
    return sym;
}

void TypeStruct::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    const char *name = sym->mangle();
    //printf("TypeStruct::toDecoBuffer('%s') = '%s'\n", toChars(), name);
    Type::toDecoBuffer(buf, flag, mangle);
    buf->printf("%s", name);
}

void TypeStruct::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    TemplateInstance *ti = sym->parent->isTemplateInstance();
    if (ti && ti->toAlias() == sym)
	buf->writestring(ti->toChars());
    else
	buf->writestring(sym->toChars());
}

Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident)
{   unsigned offset;

    VarDeclaration *v;
    Dsymbol *s;
    DotVarExp *de;
    Declaration *d;

#if LOGDOTEXP
    printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
    if (!sym->members)
    {
	error(e->loc, "struct %s is forward referenced", sym->toChars());
	return new ErrorExp();
    }

    /* If e.tupleof
     */
    if (ident == Id::tupleof)
    {
	/* Create a TupleExp out of the fields of the struct e:
	 * (e.field0, e.field1, e.field2, ...)
	 */
	e = e->semantic(sc);	// do this before turning on noaccesscheck
	Expressions *exps = new Expressions;
	exps->reserve(sym->fields.dim);
	for (size_t i = 0; i < sym->fields.dim; i++)
	{   VarDeclaration *v = (VarDeclaration *)sym->fields.data[i];
	    Expression *fe = new DotVarExp(e->loc, e, v);
	    exps->push(fe);
	}
	e = new TupleExp(e->loc, exps);
	sc = sc->push();
	sc->noaccesscheck = 1;
	e = e->semantic(sc);
	sc->pop();
	return e;
    }

    if (e->op == TOKdotexp)
    {	DotExp *de = (DotExp *)e;

	if (de->e1->op == TOKimport)
	{
	    assert(0);	// cannot find a case where this happens; leave
			// assert in until we do
	    ScopeExp *se = (ScopeExp *)de->e1;

	    s = se->sds->search(e->loc, ident, 0);
	    e = de->e1;
	    goto L1;
	}
    }

    s = sym->search(e->loc, ident, 0);
L1:
    if (!s)
    {
	if (ident != Id::__sizeof &&
	    ident != Id::alignof &&
	    ident != Id::init &&
	    ident != Id::mangleof &&
	    ident != Id::stringof &&
	    ident != Id::offsetof)
	{
	    /* See if we should forward to the alias this.
	     */
	    if (sym->aliasthis)
	    {	/* Rewrite e.ident as:
		 *	e.aliasthis.ident
		 */
		e = new DotIdExp(e->loc, e, sym->aliasthis->ident);
		e = new DotIdExp(e->loc, e, ident);
		return e->semantic(sc);
	    }

	    /* Look for overloaded opDot() to see if we should forward request
	     * to it.
	     */
	    Dsymbol *fd = search_function(sym, Id::opDot);
	    if (fd)
	    {   /* Rewrite e.ident as:
		 *	e.opId().ident
		 */
		e = build_overload(e->loc, sc, e, NULL, fd->ident);
		e = new DotIdExp(e->loc, e, ident);
		return e->semantic(sc);
	    }
	}
	return Type::dotExp(sc, e, ident);
    }
    if (!s->isFuncDeclaration())	// because of overloading
	s->checkDeprecated(e->loc, sc);
    s = s->toAlias();

    v = s->isVarDeclaration();
    if (v && !v->isDataseg())
    {
	Expression *ei = v->getConstInitializer();
	if (ei)
	{   e = ei->copy();	// need to copy it if it's a StringExp
	    e = e->semantic(sc);
	    return e;
	}
    }

    if (s->getType())
    {
	//return new DotTypeExp(e->loc, e, s);
	return new TypeExp(e->loc, s->getType());
    }

    EnumMember *em = s->isEnumMember();
    if (em)
    {
	assert(em->value);
	return em->value->copy();
    }

    TemplateMixin *tm = s->isTemplateMixin();
    if (tm)
    {
	Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, tm));
	de->type = e->type;
	return de;
    }

    TemplateDeclaration *td = s->isTemplateDeclaration();
    if (td)
    {
        e = new DotTemplateExp(e->loc, e, td);
        e->semantic(sc);
	return e;
    }

    TemplateInstance *ti = s->isTemplateInstance();
    if (ti)
    {	if (!ti->semanticRun)
	    ti->semantic(sc);
	s = ti->inst->toAlias();
	if (!s->isTemplateInstance())
	    goto L1;
	Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
	de->type = e->type;
	return de;
    }

    Import *timp = s->isImport();
    if (timp)
    {
	e = new DsymbolExp(e->loc, s, 0);
	e = e->semantic(sc);
	return e;
    }

    OverloadSet *o = s->isOverloadSet();
    if (o)
    {	/* We really should allow this, triggered by:
	 *   template c()
	 *   {
	 *	void a();
	 *	void b () { this.a(); }
	 *   }
	 *   struct S
	 *   {
	 *	mixin c;
	 *	mixin c;
	 *  }
	 *  alias S e;
	 */
	error(e->loc, "overload set for %s.%s not allowed in struct declaration", e->toChars(), ident->toChars());
	return new ErrorExp();
    }

    d = s->isDeclaration();
#ifdef DEBUG
    if (!d)
	printf("d = %s '%s'\n", s->kind(), s->toChars());
#endif
    assert(d);

    if (e->op == TOKtype)
    {	FuncDeclaration *fd = sc->func;

	if (d->isTupleDeclaration())
	{
	    e = new TupleExp(e->loc, d->isTupleDeclaration());
	    e = e->semantic(sc);
	    return e;
	}
	if (d->needThis() && fd && fd->vthis)
	{
	    e = new DotVarExp(e->loc, new ThisExp(e->loc), d);
	    e = e->semantic(sc);
	    return e;
	}
	return new VarExp(e->loc, d, 1);
    }

    if (d->isDataseg())
    {
	// (e, d)
	VarExp *ve;

	accessCheck(e->loc, sc, e, d);
	ve = new VarExp(e->loc, d);
	e = new CommaExp(e->loc, e, ve);
	e->type = d->type;
	return e;
    }

    if (v)
    {
	if (v->toParent() != sym)
	    sym->error(e->loc, "'%s' is not a member", v->toChars());

	// *(&e + offset)
	accessCheck(e->loc, sc, e, d);
#if 0
	Expression *b = new AddrExp(e->loc, e);
	b->type = e->type->pointerTo();
	b = new AddExp(e->loc, b, new IntegerExp(e->loc, v->offset, Type::tint32));
	b->type = v->type->pointerTo();
	b = new PtrExp(e->loc, b);
	b->type = v->type->addMod(e->type->mod);
	return b;
#endif
    }

    de = new DotVarExp(e->loc, e, d);
    return de->semantic(sc);
}

unsigned TypeStruct::memalign(unsigned salign)
{
    sym->size(0);		// give error for forward references
    return sym->structalign;
}

Expression *TypeStruct::defaultInit(Loc loc)
{   Declaration *d;

#if LOGDEFAULTINIT
    printf("TypeStruct::defaultInit() '%s'\n", toChars());
#endif
    d = new StaticStructInitDeclaration(sym->loc, sym);
    assert(d);
    d->type = this;
    return new VarExp(sym->loc, d);
}

int TypeStruct::isZeroInit(Loc loc)
{
    return sym->zeroInit;
}

int TypeStruct::checkBoolean()
{
    return FALSE;
}

int TypeStruct::isAssignable()
{
    /* If any of the fields are const or invariant,
     * then one cannot assign this struct.
     */
    for (size_t i = 0; i < sym->fields.dim; i++)
    {   VarDeclaration *v = (VarDeclaration *)sym->fields.data[i];
	if (v->isConst() || v->isInvariant())
	    return FALSE;
    }
    return TRUE;
}

int TypeStruct::hasPointers()
{
    StructDeclaration *s = sym;

    sym->size(0);		// give error for forward references
    for (size_t i = 0; i < s->fields.dim; i++)
    {
	Dsymbol *sm = (Dsymbol *)s->fields.data[i];
	Declaration *d = sm->isDeclaration();
	if (d->storage_class & STCref || d->hasPointers())
	    return TRUE;
    }
    return FALSE;
}

MATCH TypeStruct::implicitConvTo(Type *to)
{   MATCH m;

    //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to->toChars());
    if (ty == to->ty && sym == ((TypeStruct *)to)->sym)
    {	m = MATCHexact;		// exact match
	if (mod != to->mod)
	{
	    if (to->mod == MODconst)
		m = MATCHconst;
	    else
	    {	/* Check all the fields. If they can all be converted,
		 * allow the conversion.
		 */
		for (int i = 0; i < sym->fields.dim; i++)
		{   Dsymbol *s = (Dsymbol *)sym->fields.data[i];
		    VarDeclaration *v = s->isVarDeclaration();
		    assert(v && v->storage_class & STCfield);

		    // 'from' type
		    Type *tvf = v->type->addMod(mod);

		    // 'to' type
		    Type *tv = v->type->castMod(to->mod);

		    //printf("\t%s => %s, match = %d\n", v->type->toChars(), tv->toChars(), tvf->implicitConvTo(tv));
		    if (tvf->implicitConvTo(tv) < MATCHconst)
			return MATCHnomatch;
		}
		m = MATCHconst;
	    }
	}
    }
    else if (sym->aliasthis)
    {
	m = MATCHnomatch;
	Declaration *d = sym->aliasthis->isDeclaration();
	if (d)
	{   assert(d->type);
	    Type *t = d->type->addMod(mod);
	    m = t->implicitConvTo(to);
	}
    }
    else
	m = MATCHnomatch;	// no match
    return m;
}

Type *TypeStruct::toHeadMutable()
{
    return this;
}

MATCH TypeStruct::constConv(Type *to)
{
    if (equals(to))
	return MATCHexact;
    if (ty == to->ty && sym == ((TypeStruct *)to)->sym &&
	to->mod == MODconst)
	return MATCHconst;
    return MATCHnomatch;
}


/***************************** TypeClass *****************************/

TypeClass::TypeClass(ClassDeclaration *sym)
	: Type(Tclass)
{
    this->sym = sym;
}

char *TypeClass::toChars()
{
    if (mod)
	return Type::toChars();
    return (char *)sym->toPrettyChars();
}

Type *TypeClass::syntaxCopy()
{
    return this;
}

Type *TypeClass::semantic(Loc loc, Scope *sc)
{
    //printf("TypeClass::semantic(%s)\n", sym->toChars());
    if (deco)
	return this;
    //printf("\t%s\n", merge()->deco);
    return merge();
}

d_uns64 TypeClass::size(Loc loc)
{
    return PTRSIZE;
}

Dsymbol *TypeClass::toDsymbol(Scope *sc)
{
    return sym;
}

void TypeClass::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    const char *name = sym->mangle();
    //printf("TypeClass::toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name);
    Type::toDecoBuffer(buf, flag, mangle);
    buf->printf("%s", name);
}

void TypeClass::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    buf->writestring(sym->toChars());
}

Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident)
{   unsigned offset;

    Expression *b;
    VarDeclaration *v;
    Dsymbol *s;

#if LOGDOTEXP
    printf("TypeClass::dotExp(e='%s', ident='%s')\n", e->toChars(), ident->toChars());
#endif

    if (e->op == TOKdotexp)
    {	DotExp *de = (DotExp *)e;

	if (de->e1->op == TOKimport)
	{
	    ScopeExp *se = (ScopeExp *)de->e1;

	    s = se->sds->search(e->loc, ident, 0);
	    e = de->e1;
	    goto L1;
	}
    }

    if (ident == Id::tupleof)
    {
	/* Create a TupleExp
	 */
	e = e->semantic(sc);	// do this before turning on noaccesscheck
	Expressions *exps = new Expressions;
	exps->reserve(sym->fields.dim);
	for (size_t i = 0; i < sym->fields.dim; i++)
	{   VarDeclaration *v = (VarDeclaration *)sym->fields.data[i];
	    Expression *fe = new DotVarExp(e->loc, e, v);
	    exps->push(fe);
	}
	e = new TupleExp(e->loc, exps);
	sc = sc->push();
	sc->noaccesscheck = 1;
	e = e->semantic(sc);
	sc->pop();
	return e;
    }

    s = sym->search(e->loc, ident, 0);
L1:
    if (!s)
    {
	// See if it's a base class
	ClassDeclaration *cbase;
	for (cbase = sym->baseClass; cbase; cbase = cbase->baseClass)
	{
	    if (cbase->ident->equals(ident))
	    {
		e = new DotTypeExp(0, e, cbase);
		return e;
	    }
	}

	if (ident == Id::classinfo)
	{
	    assert(ClassDeclaration::classinfo);
	    Type *t = ClassDeclaration::classinfo->type;
	    if (e->op == TOKtype || e->op == TOKdottype)
	    {
		/* For type.classinfo, we know the classinfo
		 * at compile time.
		 */
		if (!sym->vclassinfo)
		    sym->vclassinfo = new ClassInfoDeclaration(sym);
		e = new VarExp(e->loc, sym->vclassinfo);
		e = e->addressOf(sc);
		e->type = t;	// do this so we don't get redundant dereference
	    }
	    else
	    {
        /* For class objects, the classinfo reference is the first
         * entry in the vtbl[]
         */
#if IN_LLVM

        Type* ct;
        if (sym->isInterfaceDeclaration()) {
            ct = t->pointerTo()->pointerTo()->pointerTo();
        }
        else {
            ct = t->pointerTo()->pointerTo();
        }

        e = e->castTo(sc, ct);
        e = new PtrExp(e->loc, e);
        e->type = ct->nextOf();
        e = new PtrExp(e->loc, e);
        e->type = ct->nextOf()->nextOf();

        if (sym->isInterfaceDeclaration())
        {
            if (sym->isCOMinterface())
            {   /* COM interface vtbl[]s are different in that the
             * first entry is always pointer to QueryInterface().
             * We can't get a .classinfo for it.
             */
            error(e->loc, "no .classinfo for COM interface objects");
            }
            /* For an interface, the first entry in the vtbl[]
             * is actually a pointer to an instance of struct Interface.
             * The first member of Interface is the .classinfo,
             * so add an extra pointer indirection.
             */
            e = new PtrExp(e->loc, e);
            e->type = ct->nextOf()->nextOf()->nextOf();
        }
        }

#else

		e = new PtrExp(e->loc, e);
		e->type = t->pointerTo();
		if (sym->isInterfaceDeclaration())
		{
		    if (sym->isCPPinterface())
		    {	/* C++ interface vtbl[]s are different in that the
			 * first entry is always pointer to the first virtual
			 * function, not classinfo.
			 * We can't get a .classinfo for it.
			 */
			error(e->loc, "no .classinfo for C++ interface objects");
		    }
		    /* For an interface, the first entry in the vtbl[]
		     * is actually a pointer to an instance of struct Interface.
		     * The first member of Interface is the .classinfo,
		     * so add an extra pointer indirection.
		     */
		    e->type = e->type->pointerTo();
		    e = new PtrExp(e->loc, e);
		    e->type = t->pointerTo();
		}
		e = new PtrExp(e->loc, e, t);
        }

#endif // !LDC

	    return e;
	}

	if (ident == Id::__vptr)
	{   /* The pointer to the vtbl[]
	     * *cast(invariant(void*)**)e
	     */
	    e = e->castTo(sc, tvoidptr->invariantOf()->pointerTo()->pointerTo());
	    e = new PtrExp(e->loc, e);
	    e = e->semantic(sc);
	    return e;
	}

	if (ident == Id::__monitor)
	{   /* The handle to the monitor (call it a void*)
	     * *(cast(void**)e + 1)
	     */
	    e = e->castTo(sc, tvoidptr->pointerTo());
	    e = new AddExp(e->loc, e, new IntegerExp(1));
	    e = new PtrExp(e->loc, e);
	    e = e->semantic(sc);
	    return e;
	}

	if (ident == Id::typeinfo)
	{
	    if (!global.params.useDeprecated)
		error(e->loc, ".typeinfo deprecated, use typeid(type)");
	    return getTypeInfo(sc);
	}
	if (ident == Id::outer && sym->vthis)
	{
	    s = sym->vthis;
	}
	else
	{

	    if (ident != Id::__sizeof &&
		ident != Id::alignof &&
		ident != Id::init &&
		ident != Id::mangleof &&
		ident != Id::stringof &&
		ident != Id::offsetof)
	    {
		/* See if we should forward to the alias this.
		 */
		if (sym->aliasthis)
		{   /* Rewrite e.ident as:
		     *	e.aliasthis.ident
		     */
		    e = new DotIdExp(e->loc, e, sym->aliasthis->ident);
		    e = new DotIdExp(e->loc, e, ident);
		    return e->semantic(sc);
		}

		/* Look for overloaded opDot() to see if we should forward request
		 * to it.
		 */
		Dsymbol *fd = search_function(sym, Id::opDot);
		if (fd)
		{   /* Rewrite e.ident as:
		     *	e.opId().ident
		     */
		    e = build_overload(e->loc, sc, e, NULL, fd->ident);
		    e = new DotIdExp(e->loc, e, ident);
		    return e->semantic(sc);
		}
	    }

	    return Type::dotExp(sc, e, ident);
	}
    }
    if (!s->isFuncDeclaration())	// because of overloading
	s->checkDeprecated(e->loc, sc);
    s = s->toAlias();
    v = s->isVarDeclaration();
    if (v && !v->isDataseg())
    {	Expression *ei = v->getConstInitializer();

	if (ei)
	{   e = ei->copy();	// need to copy it if it's a StringExp
	    e = e->semantic(sc);
	    return e;
	}
    }

    if (s->getType())
    {
//	if (e->op == TOKtype)
	    return new TypeExp(e->loc, s->getType());
//	return new DotTypeExp(e->loc, e, s);
    }

    EnumMember *em = s->isEnumMember();
    if (em)
    {
	assert(em->value);
	return em->value->copy();
    }

    TemplateMixin *tm = s->isTemplateMixin();
    if (tm)
    {
	Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, tm));
	de->type = e->type;
	return de;
    }

    TemplateDeclaration *td = s->isTemplateDeclaration();
    if (td)
    {
        e = new DotTemplateExp(e->loc, e, td);
        e->semantic(sc);
	return e;
    }

    TemplateInstance *ti = s->isTemplateInstance();
    if (ti)
    {	if (!ti->semanticRun)
	    ti->semantic(sc);
	s = ti->inst->toAlias();
	if (!s->isTemplateInstance())
	    goto L1;
	Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
	de->type = e->type;
	return de;
    }

    OverloadSet *o = s->isOverloadSet();
    if (o)
    {	/* We really should allow this
	 */
	error(e->loc, "overload set for %s.%s not allowed in struct declaration", e->toChars(), ident->toChars());
	return new ErrorExp();
    }

    Declaration *d = s->isDeclaration();
    if (!d)
    {
	e->error("%s.%s is not a declaration", e->toChars(), ident->toChars());
	return new ErrorExp();
    }

    if (e->op == TOKtype)
    {
	/* It's:
	 *    Class.d
	 */
	if (d->isTupleDeclaration())
	{
	    e = new TupleExp(e->loc, d->isTupleDeclaration());
	    e = e->semantic(sc);
	    return e;
	}
	else if (d->needThis() && (hasThis(sc) || !d->isFuncDeclaration()))
	{
	    if (sc->func)
	    {
		ClassDeclaration *thiscd;
		thiscd = sc->func->toParent()->isClassDeclaration();

		if (thiscd)
		{
		    ClassDeclaration *cd = e->type->isClassHandle();

		    if (cd == thiscd)
		    {
			e = new ThisExp(e->loc);
			e = new DotTypeExp(e->loc, e, cd);
			DotVarExp *de = new DotVarExp(e->loc, e, d);
			e = de->semantic(sc);
			return e;
		    }
		    else if ((!cd || !cd->isBaseOf(thiscd, NULL)) &&
			     !d->isFuncDeclaration())
			e->error("'this' is required, but %s is not a base class of %s", e->type->toChars(), thiscd->toChars());
		}
	    }

	    /* Rewrite as:
	     *	this.d
	     */
	    DotVarExp *de = new DotVarExp(e->loc, new ThisExp(e->loc), d);
	    e = de->semantic(sc);
	    return e;
	}
	else
	{
	    VarExp *ve = new VarExp(e->loc, d, 1);
	    return ve;
	}
    }

    if (d->isDataseg())
    {
	// (e, d)
	VarExp *ve;

	accessCheck(e->loc, sc, e, d);
	ve = new VarExp(e->loc, d);
	e = new CommaExp(e->loc, e, ve);
	e->type = d->type;
	return e;
    }

    if (d->parent && d->toParent()->isModule())
    {
	// (e, d)

	VarExp *ve = new VarExp(e->loc, d, 1);
	e = new CommaExp(e->loc, e, ve);
	e->type = d->type;
	return e;
    }

    DotVarExp *de = new DotVarExp(e->loc, e, d);
    return de->semantic(sc);
}

ClassDeclaration *TypeClass::isClassHandle()
{
    return sym;
}

int TypeClass::isauto()
{
    return sym->isauto;
}

int TypeClass::isBaseOf(Type *t, int *poffset)
{
    if (t->ty == Tclass)
    {   ClassDeclaration *cd;

	cd   = ((TypeClass *)t)->sym;
	if (sym->isBaseOf(cd, poffset))
	    return 1;
    }
    return 0;
}

MATCH TypeClass::implicitConvTo(Type *to)
{
    //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to->toChars(), toChars());
    MATCH m = constConv(to);
    if (m != MATCHnomatch)
	return m;

    ClassDeclaration *cdto = to->isClassHandle();
    if (cdto && cdto->isBaseOf(sym, NULL))
    {	//printf("'to' is base\n");
	return MATCHconvert;
    }

    if (global.params.Dversion == 1)
    {
	// Allow conversion to (void *)
	if (to->ty == Tpointer && ((TypePointer *)to)->next->ty == Tvoid)
	    return MATCHconvert;
    }

    m = MATCHnomatch;
    if (sym->aliasthis)
    {
	Declaration *d = sym->aliasthis->isDeclaration();
	if (d)
	{   assert(d->type);
	    Type *t = d->type->addMod(mod);
	    m = t->implicitConvTo(to);
	}
    }

    return m;
}

MATCH TypeClass::constConv(Type *to)
{
    if (equals(to))
	return MATCHexact;
    if (ty == to->ty && sym == ((TypeClass *)to)->sym &&
	to->mod == MODconst)
	return MATCHconst;
    return MATCHnomatch;
}

Type *TypeClass::toHeadMutable()
{
    return this;
}

Expression *TypeClass::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
    printf("TypeClass::defaultInit() '%s'\n", toChars());
#endif
    Expression *e;
    e = new NullExp(loc);
    e->type = this;
    return e;
}

int TypeClass::isZeroInit(Loc loc)
{
    return 1;
}

int TypeClass::checkBoolean()
{
    return TRUE;
}

int TypeClass::hasPointers()
{
    return TRUE;
}

/***************************** TypeTuple *****************************/

TypeTuple::TypeTuple(Arguments *arguments)
    : Type(Ttuple)
{
    //printf("TypeTuple(this = %p)\n", this);
    this->arguments = arguments;
    //printf("TypeTuple() %s\n", toChars());
#ifdef DEBUG
    if (arguments)
    {
	for (size_t i = 0; i < arguments->dim; i++)
	{
	    Argument *arg = (Argument *)arguments->data[i];
	    assert(arg && arg->type);
	}
    }
#endif
}

/****************
 * Form TypeTuple from the types of the expressions.
 * Assume exps[] is already tuple expanded.
 */

TypeTuple::TypeTuple(Expressions *exps)
    : Type(Ttuple)
{
    Arguments *arguments = new Arguments;
    if (exps)
    {
	arguments->setDim(exps->dim);
	for (size_t i = 0; i < exps->dim; i++)
	{   Expression *e = (Expression *)exps->data[i];
	    if (e->type->ty == Ttuple)
		e->error("cannot form tuple of tuples");
	    Argument *arg = new Argument(STCundefined, e->type, NULL, NULL);
	    arguments->data[i] = (void *)arg;
	}
    }
    this->arguments = arguments;
}

Type *TypeTuple::syntaxCopy()
{
    Arguments *args = Argument::arraySyntaxCopy(arguments);
    Type *t = new TypeTuple(args);
    t->mod = mod;
    return t;
}

Type *TypeTuple::semantic(Loc loc, Scope *sc)
{
    //printf("TypeTuple::semantic(this = %p)\n", this);
    //printf("TypeTuple::semantic() %s\n", toChars());
    if (!deco)
	deco = merge()->deco;

    /* Don't return merge(), because a tuple with one type has the
     * same deco as that type.
     */
    return this;
}

int TypeTuple::equals(Object *o)
{   Type *t;

    t = (Type *)o;
    //printf("TypeTuple::equals(%s, %s)\n", toChars(), t->toChars());
    if (this == t)
    {
	return 1;
    }
    if (t->ty == Ttuple)
    {	TypeTuple *tt = (TypeTuple *)t;

	if (arguments->dim == tt->arguments->dim)
	{
	    for (size_t i = 0; i < tt->arguments->dim; i++)
	    {   Argument *arg1 = (Argument *)arguments->data[i];
		Argument *arg2 = (Argument *)tt->arguments->data[i];

		if (!arg1->type->equals(arg2->type))
		    return 0;
	    }
	    return 1;
	}
    }
    return 0;
}

Type *TypeTuple::reliesOnTident()
{
    if (arguments)
    {
	for (size_t i = 0; i < arguments->dim; i++)
	{
	    Argument *arg = (Argument *)arguments->data[i];
	    Type *t = arg->type->reliesOnTident();
	    if (t)
		return t;
	}
    }
    return NULL;
}

#if 0
Type *TypeTuple::makeConst()
{
    //printf("TypeTuple::makeConst() %s\n", toChars());
    if (cto)
	return cto;
    TypeTuple *t = (TypeTuple *)Type::makeConst();
    t->arguments = new Arguments();
    t->arguments->setDim(arguments->dim);
    for (size_t i = 0; i < arguments->dim; i++)
    {   Argument *arg = (Argument *)arguments->data[i];
	Argument *narg = new Argument(arg->storageClass, arg->type->constOf(), arg->ident, arg->defaultArg);
	t->arguments->data[i] = (Argument *)narg;
    }
    return t;
}
#endif

void TypeTuple::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    Argument::argsToCBuffer(buf, hgs, arguments, 0);
}

void TypeTuple::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
    //printf("TypeTuple::toDecoBuffer() this = %p, %s\n", this, toChars());
    Type::toDecoBuffer(buf, flag, mangle);
    OutBuffer buf2;
    Argument::argsToDecoBuffer(&buf2, arguments, mangle);
    unsigned len = buf2.offset;
    buf->printf("%d%.*s", len, len, (char *)buf2.extractData());
}

Expression *TypeTuple::getProperty(Loc loc, Identifier *ident)
{   Expression *e;

#if LOGDOTEXP
    printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", toChars(), ident->toChars());
#endif
    if (ident == Id::length)
    {
	e = new IntegerExp(loc, arguments->dim, Type::tsize_t);
    }
    else
    {
	error(loc, "no property '%s' for tuple '%s'", ident->toChars(), toChars());
	e = new ErrorExp();
    }
    return e;
}

/***************************** TypeSlice *****************************/

/* This is so we can slice a TypeTuple */

TypeSlice::TypeSlice(Type *next, Expression *lwr, Expression *upr)
    : TypeNext(Tslice, next)
{
    //printf("TypeSlice[%s .. %s]\n", lwr->toChars(), upr->toChars());
    this->lwr = lwr;
    this->upr = upr;
}

Type *TypeSlice::syntaxCopy()
{
    Type *t = new TypeSlice(next->syntaxCopy(), lwr->syntaxCopy(), upr->syntaxCopy());
    t->mod = mod;
    return t;
}

Type *TypeSlice::semantic(Loc loc, Scope *sc)
{
    //printf("TypeSlice::semantic() %s\n", toChars());
    next = next->semantic(loc, sc);
    transitive();
    //printf("next: %s\n", next->toChars());

    Type *tbn = next->toBasetype();
    if (tbn->ty != Ttuple)
    {	error(loc, "can only slice tuple types, not %s", tbn->toChars());
	return Type::terror;
    }
    TypeTuple *tt = (TypeTuple *)tbn;

    lwr = semanticLength(sc, tbn, lwr);
    lwr = lwr->optimize(WANTvalue);
    uinteger_t i1 = lwr->toUInteger();

    upr = semanticLength(sc, tbn, upr);
    upr = upr->optimize(WANTvalue);
    uinteger_t i2 = upr->toUInteger();

    if (!(i1 <= i2 && i2 <= tt->arguments->dim))
    {	error(loc, "slice [%ju..%ju] is out of range of [0..%u]", i1, i2, tt->arguments->dim);
	return Type::terror;
    }

    Arguments *args = new Arguments;
    args->reserve(i2 - i1);
    for (size_t i = i1; i < i2; i++)
    {	Argument *arg = (Argument *)tt->arguments->data[i];
	args->push(arg);
    }

    return new TypeTuple(args);
}

void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
    next->resolve(loc, sc, pe, pt, ps);
    if (*pe)
    {	// It's really a slice expression
	Expression *e;
	e = new SliceExp(loc, *pe, lwr, upr);
	*pe = e;
    }
    else if (*ps)
    {	Dsymbol *s = *ps;
	TupleDeclaration *td = s->isTupleDeclaration();
	if (td)
	{
	    /* It's a slice of a TupleDeclaration
	     */
	    ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td);
	    sym->parent = sc->scopesym;
	    sc = sc->push(sym);

	    lwr = lwr->semantic(sc);
	    lwr = lwr->optimize(WANTvalue);
	    uinteger_t i1 = lwr->toUInteger();

	    upr = upr->semantic(sc);
	    upr = upr->optimize(WANTvalue);
	    uinteger_t i2 = upr->toUInteger();

	    sc = sc->pop();

	    if (!(i1 <= i2 && i2 <= td->objects->dim))
	    {   error(loc, "slice [%ju..%ju] is out of range of [0..%u]", i1, i2, td->objects->dim);
		goto Ldefault;
	    }

	    if (i1 == 0 && i2 == td->objects->dim)
	    {
		*ps = td;
		return;
	    }

	    /* Create a new TupleDeclaration which
	     * is a slice [i1..i2] out of the old one.
	     */
	    Objects *objects = new Objects;
	    objects->setDim(i2 - i1);
	    for (size_t i = 0; i < objects->dim; i++)
	    {
		objects->data[i] = td->objects->data[(size_t)i1 + i];
	    }

	    TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
	    *ps = tds;
	}
	else
	    goto Ldefault;
    }
    else
    {
     Ldefault:
	Type::resolve(loc, sc, pe, pt, ps);
    }
}

void TypeSlice::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
    if (mod != this->mod)
    {	toCBuffer3(buf, hgs, mod);
	return;
    }
    next->toCBuffer2(buf, hgs, this->mod);

    buf->printf("[%s .. ", lwr->toChars());
    buf->printf("%s]", upr->toChars());
}

/***************************** Argument *****************************/

Argument::Argument(unsigned storageClass, Type *type, Identifier *ident, Expression *defaultArg)
{
    this->type = type;
    this->ident = ident;
    this->storageClass = storageClass;
    this->defaultArg = defaultArg;
}

Argument *Argument::syntaxCopy()
{
    Argument *a = new Argument(storageClass,
		type ? type->syntaxCopy() : NULL,
		ident,
		defaultArg ? defaultArg->syntaxCopy() : NULL);
    return a;
}

Arguments *Argument::arraySyntaxCopy(Arguments *args)
{   Arguments *a = NULL;

    if (args)
    {
	a = new Arguments();
	a->setDim(args->dim);
	for (size_t i = 0; i < a->dim; i++)
	{   Argument *arg = (Argument *)args->data[i];

	    arg = arg->syntaxCopy();
	    a->data[i] = (void *)arg;
	}
    }
    return a;
}

char *Argument::argsTypesToChars(Arguments *args, int varargs)
{
    OutBuffer *buf = new OutBuffer();

#if 1
    HdrGenState hgs;
    argsToCBuffer(buf, &hgs, args, varargs);
#else
    buf->writeByte('(');
    if (args)
    {	OutBuffer argbuf;
	HdrGenState hgs;

	for (int i = 0; i < args->dim; i++)
	{   if (i)
		buf->writeByte(',');
	    Argument *arg = (Argument *)args->data[i];
	    argbuf.reset();
	    arg->type->toCBuffer2(&argbuf, &hgs, 0);
	    buf->write(&argbuf);
	}
	if (varargs)
	{
	    if (i && varargs == 1)
		buf->writeByte(',');
	    buf->writestring("...");
	}
    }
    buf->writeByte(')');
#endif
    return buf->toChars();
}

void Argument::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Arguments *arguments, int varargs)
{
    buf->writeByte('(');
    if (arguments)
    {	int i;
	OutBuffer argbuf;

	for (i = 0; i < arguments->dim; i++)
	{
	    if (i)
		buf->writestring(", ");
	    Argument *arg = (Argument *)arguments->data[i];

	    if (arg->storageClass & STCout)
		buf->writestring("out ");
	    else if (arg->storageClass & STCref)
		buf->writestring((global.params.Dversion == 1)
			? (char *)"inout " : (char *)"ref ");
	    else if (arg->storageClass & STCin)
		buf->writestring("in ");
	    else if (arg->storageClass & STClazy)
		buf->writestring("lazy ");
	    else if (arg->storageClass & STCalias)
		buf->writestring("alias ");
	    else if (arg->storageClass & STCauto)
		buf->writestring("auto ");

	    unsigned stc = arg->storageClass;
	    if (arg->type && arg->type->mod & MODshared)
		stc &= ~STCshared;

	    StorageClassDeclaration::stcToCBuffer(buf,
		stc & (STCconst | STCimmutable | STCshared | STCscope));

	    argbuf.reset();
	    if (arg->storageClass & STCalias)
	    {	if (arg->ident)
		    argbuf.writestring(arg->ident->toChars());
	    }
	    else
		arg->type->toCBuffer(&argbuf, arg->ident, hgs);
	    if (arg->defaultArg)
	    {
		argbuf.writestring(" = ");
		arg->defaultArg->toCBuffer(&argbuf, hgs);
	    }
	    buf->write(&argbuf);
	}
	if (varargs)
	{
	    if (i && varargs == 1)
		buf->writeByte(',');
	    buf->writestring("...");
	}
    }
    buf->writeByte(')');
}


void Argument::argsToDecoBuffer(OutBuffer *buf, Arguments *arguments, bool mangle)
{
    //printf("Argument::argsToDecoBuffer()\n");

    // Write argument types
    if (arguments)
    {
	size_t dim = Argument::dim(arguments);
	for (size_t i = 0; i < dim; i++)
	{
	    Argument *arg = Argument::getNth(arguments, i);
	    arg->toDecoBuffer(buf, mangle);
	}
    }
}


/****************************************
 * Determine if parameter list is really a template parameter list
 * (i.e. it has auto or alias parameters)
 */

int Argument::isTPL(Arguments *arguments)
{
    //printf("Argument::isTPL()\n");

    if (arguments)
    {
	size_t dim = Argument::dim(arguments);
	for (size_t i = 0; i < dim; i++)
	{
	    Argument *arg = Argument::getNth(arguments, i);
	    if (arg->storageClass & (STCalias | STCauto | STCstatic))
		return 1;
	}
    }
    return 0;
}

/****************************************************
 * Determine if parameter is a lazy array of delegates.
 * If so, return the return type of those delegates.
 * If not, return NULL.
 */

Type *Argument::isLazyArray()
{
//    if (inout == Lazy)
    {
	Type *tb = type->toBasetype();
	if (tb->ty == Tsarray || tb->ty == Tarray)
	{
	    Type *tel = ((TypeArray *)tb)->next->toBasetype();
	    if (tel->ty == Tdelegate)
	    {
		TypeDelegate *td = (TypeDelegate *)tel;
		TypeFunction *tf = (TypeFunction *)td->next;

		if (!tf->varargs && Argument::dim(tf->parameters) == 0)
		{
		    return tf->next;	// return type of delegate
		}
	    }
	}
    }
    return NULL;
}

void Argument::toDecoBuffer(OutBuffer *buf, bool mangle)
{
    if (storageClass & STCscope)
	buf->writeByte('M');
    switch (storageClass & (STCin | STCout | STCref | STClazy))
    {   case 0:
	case STCin:
	    break;
	case STCout:
	    buf->writeByte('J');
	    break;
	case STCref:
	    buf->writeByte('K');
	    break;
	case STClazy:
	    buf->writeByte('L');
	    break;
	default:
#ifdef DEBUG
	    halt();
#endif
	    assert(0);
    }
#if 0
    int mod = 0x100;
    if (type->toBasetype()->ty == Tclass)
	mod = 0;
    type->toDecoBuffer(buf, mod);
#else
    //type->toHeadMutable()->toDecoBuffer(buf, 0);
    type->toDecoBuffer(buf, 0, mangle);
#endif
}

/***************************************
 * Determine number of arguments, folding in tuples.
 */

size_t Argument::dim(Arguments *args)
{
    size_t n = 0;
    if (args)
    {
	for (size_t i = 0; i < args->dim; i++)
	{   Argument *arg = (Argument *)args->data[i];
	    Type *t = arg->type->toBasetype();

	    if (t->ty == Ttuple)
	    {   TypeTuple *tu = (TypeTuple *)t;
		n += dim(tu->arguments);
	    }
	    else
		n++;
	}
    }
    return n;
}

/***************************************
 * Get nth Argument, folding in tuples.
 * Returns:
 *	Argument*	nth Argument
 *	NULL		not found, *pn gets incremented by the number
 *			of Arguments
 */

Argument *Argument::getNth(Arguments *args, size_t nth, size_t *pn)
{
    if (!args)
	return NULL;

    size_t n = 0;
    for (size_t i = 0; i < args->dim; i++)
    {   Argument *arg = (Argument *)args->data[i];
	Type *t = arg->type->toBasetype();

	if (t->ty == Ttuple)
	{   TypeTuple *tu = (TypeTuple *)t;
	    arg = getNth(tu->arguments, nth - n, &n);
	    if (arg)
		return arg;
	}
	else if (n == nth)
	    return arg;
	else
	    n++;
    }

    if (pn)
	*pn += n;
    return NULL;
}